diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
index 3e2012d..278ede5 100644
--- a/.github/workflows/quality.yml
+++ b/.github/workflows/quality.yml
@@ -56,7 +56,7 @@ jobs:
run: uv python install 3.12
- name: Install locked dependencies
- run: uv sync --locked --extra legacy-ui
+ run: uv sync --locked
- name: Lint supported architecture boundary
run: >-
diff --git a/pages/Blueprinting/overview.py b/pages/Blueprinting/overview.py
deleted file mode 100644
index acfee0b..0000000
--- a/pages/Blueprinting/overview.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""Blueprinting single-point analysis workbench."""
-
-import streamlit as st
-
-from blueprinting.workbench.streamlit_ui import (
- analysis_form,
- cached_analysis,
- recalled,
- remember,
- render_analysis_summary,
- setup_workbench_page,
-)
-
-setup_workbench_page("分析总览", "🧭")
-st.caption(
- "从模型语义和并行策略推导 PortablePlanIR,再以版本化硬件证据估算延迟与内存。"
- "Calculon 不参与这条产品分析路径。"
-)
-
-STATE_KEY = "blueprinting.analysis.last_result"
-draft = analysis_form("blueprinting.overview")
-if draft is not None:
- with st.spinner("正在进行形式化推导与证据估算…"):
- remember(STATE_KEY, cached_analysis(draft))
-
-outcome = recalled(STATE_KEY)
-if outcome is None:
- st.info("从左侧选择模型、硬件证据和执行策略,然后运行 Blueprinting 分析。")
- st.markdown(
- """
- 这条工作台路径提供:
-
- - 类型化配置校验与可复现 request digest;
- - ModelIR → DistributedTaskIR → PortablePlanIR 推导;
- - 计算、访存、通信、Pipeline bubble 和单设备内存估算;
- - 明确的证据版本、实现边界与失败诊断。
- """
- )
-else:
- render_analysis_summary(outcome)
diff --git a/pages/LLM_Calc/blockwise.py b/pages/LLM_Calc/blockwise.py
deleted file mode 100755
index 8297f96..0000000
--- a/pages/LLM_Calc/blockwise.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""LLM 训练计算器 - 块粒度视图页面"""
-
-import streamlit as st
-import hyperparameter as hp
-
-from blueprinting.ui import (
- setup_page,
- setup_sidebar,
- page_header_with_config,
- page_title,
- section_header,
-)
-from blueprinting.st import attention_block, block, ffn_block
-
-
-# ============================================================================
-# 页面初始化
-# ============================================================================
-setup_page(title="LLM训练计算器 - 块粒度")
-app_json, sys_json, exe_json = setup_sidebar()
-
-page_title(
- "块粒度分析",
- subtitle="Transformer 块级结构分析和 IR 编译模拟",
- icon="🧱"
-)
-
-
-# ============================================================================
-# 主要超参配置
-# ============================================================================
-with hp.scope(app=app_json, model=app_json, sys=sys_json, exe=exe_json) as ps:
- config = page_header_with_config("主要超参", ps, mbs_param="exe.micro_batch_size")
- use_humanreadable = config["use_humanreadable"]
- use_raw_output = config["use_raw_output"]
-
- # ========================================================================
- # 块粒度视图
- # ========================================================================
- section_header("Transformer 块结构", "可视化 Transformer 块的内部组件")
-
- with st.expander("⚙️ 当前配置", expanded=False):
- st.json(ps.storage().storage())
-
- with block("transformer_block", 6):
- st.markdown("### 🔷 Transformer Block")
- ffn_block()
- attention_block()
diff --git a/pages/LLM_Calc/distexp.py b/pages/LLM_Calc/distexp.py
deleted file mode 100755
index 2a8c164..0000000
--- a/pages/LLM_Calc/distexp.py
+++ /dev/null
@@ -1,238 +0,0 @@
-"""LLM 训练计算器 - 分布式实验页面"""
-
-import logging
-
-import pandas as pd
-import plotly.express as px
-import streamlit as st
-import hyperparameter as hp
-
-from calculon.llm import Llm
-from calculon.system import System
-from blueprinting.types import Execution, Model
-from blueprinting.ui import (
- setup_page,
- setup_sidebar,
- page_header_with_config,
- page_title,
- section_header,
- info_card,
-)
-
-
-# ============================================================================
-# 页面初始化
-# ============================================================================
-setup_page(title="LLM训练计算器 - 分布式实验")
-app_json, sys_json, exe_json = setup_sidebar()
-logger = logging.getLogger()
-
-page_title(
- "分布式实验",
- subtitle="探索不同 TP/PP/DP 并行配置对训练性能的影响",
- icon="🔄"
-)
-
-
-# ============================================================================
-# 辅助函数
-# ============================================================================
-def gen_nums(rng):
- """生成范围内的 2 的幂次数列"""
- left, right = rng
- for i in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]:
- if left <= i <= right:
- yield i
-
-
-def run_experiment(ps, app_json, sys_json, tp, pp, dp, logger):
- """运行单次实验,返回 TGS 或 None"""
- with hp.scope() as ps_exp:
- ps_exp.exe.tensor_par = tp
- ps_exp.exe.pipeline_par = pp
- ps_exp.exe.data_par = dp
- ps_exp.exe.num_procs = tp * pp * dp
-
- app = Model.from_cfg(ps.app)
- exe = Execution(ps_exp.exe)
- syst = System(sys_json)
-
- try:
- model = Llm(app, logger)
- model.compile(syst, exe)
- model.run(syst)
-
- stats = model.get_stats_json(False)
- tgs = (
- app.seq_size
- * exe.global_batch_size
- / stats["total_time"]
- / (exe.tensor_par * exe.pipeline_par * exe.data_par)
- )
- return tgs
- except Exception:
- return None
-
-
-def plot_analysis(results, x_col, y_col, color_col, filter_col, filter_values, title_prefix):
- """绘制分析图表"""
- c1, c2 = st.columns(2)
- for i, val in enumerate(filter_values):
- filtered = results[results[filter_col] == val]
- col = c1 if i % 2 == 0 else c2
- with col:
- st.plotly_chart(
- px.line(
- filtered,
- x=x_col,
- y=y_col,
- color=color_col,
- title=f"{title_prefix}[{filter_col}={val}]",
- ),
- use_container_width=True,
- )
-
-
-# ============================================================================
-# 主要超参配置
-# ============================================================================
-with hp.scope(app=app_json, model=app_json, sys=sys_json, exe=exe_json) as ps:
- config = page_header_with_config("主要超参", ps, use_slider=True)
- use_humanreadable = config["use_humanreadable"]
- use_raw_output = config["use_raw_output"]
-
- # ========================================================================
- # 运行实验
- # ========================================================================
- def gen_setups():
- """生成所有实验配置组合"""
- for tp in gen_nums(ps.exp.tp | [1, 1]):
- for pp in gen_nums(ps.exp.pp | [1, 1]):
- for dp in gen_nums(ps.exp.dp | [1, 1]):
- yield tp, pp, dp
-
- results = []
- for tp, pp, dp in gen_setups():
- tgs = run_experiment(ps, app_json, sys_json, tp, pp, dp, logger)
- results.append({
- "tp": tp,
- "pp": pp,
- "dp": dp,
- "tgs": tgs,
- })
-
- results = pd.DataFrame.from_records(results)
-
- # ========================================================================
- # 实验数据展示
- # ========================================================================
- with st.expander("实验数据", expanded=True):
- st.dataframe(results, use_container_width=True)
-
- # ========================================================================
- # TP 分析
- # ========================================================================
- tp_range = ps.exp.tp | [1, 1]
- if tp_range[1] > tp_range[0]:
- with st.expander("TP分析", expanded=True):
- c1, c2 = st.columns(2)
-
- # 按 PP 分组
- with c1:
- for pp in gen_nums(ps.exp.pp | [1, 1]):
- st.plotly_chart(
- px.line(
- results[results.pp == pp],
- x="tp",
- y="tgs",
- color="dp",
- title=f"TP分析[pp={pp}]",
- ),
- use_container_width=True,
- )
-
- # 按 DP 分组
- with c2:
- for dp in gen_nums(ps.exp.dp | [1, 1]):
- st.plotly_chart(
- px.line(
- results[results.dp == dp],
- x="tp",
- y="tgs",
- color="pp",
- title=f"TP分析[dp={dp}]",
- ),
- use_container_width=True,
- )
-
- # ========================================================================
- # PP 分析
- # ========================================================================
- pp_range = ps.exp.pp | [1, 1]
- if pp_range[1] > pp_range[0]:
- with st.expander("PP分析", expanded=True):
- c1, c2 = st.columns(2)
-
- # 按 TP 分组
- with c1:
- for tp in gen_nums(ps.exp.tp | [1, 1]):
- st.plotly_chart(
- px.line(
- results[results.tp == tp],
- x="pp",
- y="tgs",
- color="dp",
- title=f"PP分析[tp={tp}]",
- ),
- use_container_width=True,
- )
-
- # 按 DP 分组
- with c2:
- for dp in gen_nums(ps.exp.dp | [1, 1]):
- st.plotly_chart(
- px.line(
- results[results.dp == dp],
- x="pp",
- y="tgs",
- color="tp",
- title=f"PP分析[dp={dp}]",
- ),
- use_container_width=True,
- )
-
- # ========================================================================
- # DP 分析
- # ========================================================================
- dp_range = ps.exp.dp | [1, 1]
- if dp_range[1] > dp_range[0]:
- with st.expander("DP分析", expanded=True):
- c1, c2 = st.columns(2)
-
- # 按 TP 分组
- with c1:
- for tp in gen_nums(ps.exp.tp | [1, 1]):
- st.plotly_chart(
- px.line(
- results[results.tp == tp],
- x="dp",
- y="tgs",
- color="pp",
- title=f"DP分析[tp={tp}]",
- ),
- use_container_width=True,
- )
-
- # 按 PP 分组
- with c2:
- for pp in gen_nums(ps.exp.pp | [1, 1]):
- st.plotly_chart(
- px.line(
- results[results.pp == pp],
- x="dp",
- y="tgs",
- color="tp",
- title=f"DP分析[pp={pp}]",
- ),
- use_container_width=True,
- )
diff --git a/pages/LLM_Calc/overview.py b/pages/LLM_Calc/overview.py
deleted file mode 100755
index f622187..0000000
--- a/pages/LLM_Calc/overview.py
+++ /dev/null
@@ -1,263 +0,0 @@
-"""LLM 训练计算器 - 总览页面"""
-
-import json
-import logging
-from contextlib import nullcontext
-
-import pandas as pd
-import plotly.express as px
-import streamlit as st
-import hyperparameter as hp
-from streamlit_extras.add_vertical_space import add_vertical_space
-from streamlit_extras.row import row
-
-from calculon.llm import Llm
-from calculon.system import System
-from blueprinting.types import Execution, Model
-from blueprinting.ui import (
- human_readable_flops,
- human_readable_num,
- make_summary,
- setup_page,
- setup_sidebar,
- page_header_with_config,
- transformer_config_expander,
- raw_output_section,
- page_title,
- metrics_row,
- section_header,
- info_card,
-)
-
-
-# ============================================================================
-# 页面初始化
-# ============================================================================
-setup_page(title="LLM训练计算器")
-app_json, sys_json, exe_json = setup_sidebar()
-logger = logging.getLogger()
-
-# 页面标题
-page_title(
- "LLM 训练计算器",
- subtitle="分析大语言模型训练的计算、通信和内存开销",
- icon="🧮"
-)
-
-
-# ============================================================================
-# 主要超参配置
-# ============================================================================
-with hp.scope(app=app_json, model=app_json, sys=sys_json, exe=exe_json) as ps:
- config = page_header_with_config("主要超参", ps)
- use_humanreadable = config["use_humanreadable"]
- use_raw_output = config["use_raw_output"]
-
- # 编译模型
- app = Model.from_cfg(ps.app)
- exe = Execution(ps.exe)
- syst = System(sys_json)
-
- model = Llm(app, logger)
- model.compile(syst, exe)
- model.run(syst)
-
-
-# ============================================================================
-# 页面内容
-# ============================================================================
-tab_model, tab_detail = st.tabs(["📊 模型 Overview", "⚙️ 执行细节"])
-
-
-# ============================================================================
-# 模型 Overview Tab
-# ============================================================================
-with tab_model, ps:
- # Transformer 参数配置
- model_config = transformer_config_expander(ps)
-
- with hp.scope(**model_config) as ps:
- stats = model.get_stats_json(False)
-
- # 原始输出(可选)
- if use_raw_output:
- raw_output_section(stats, make_summary)
-
- # 整体性能指标 - 使用醒目的布局
- section_header("整体性能", "训练迭代时间和吞吐量")
-
- tgs = (
- app.seq_size
- * exe.global_batch_size
- / stats["total_time"]
- / (exe.tensor_par * exe.pipeline_par * exe.data_par)
- )
-
- metrics_row(
- ("⏱️ 迭代时间", "%.2f s" % stats["total_time"], None, "单次训练迭代所需时间"),
- ("🚀 TGS", "%.2f" % tgs, None, "每秒处理的 Token 数 (per GPU)"),
- ("🔢 总参数量", human_readable_num(app.nparam_total), None, "模型总参数量"),
- ("💻 计算量", human_readable_flops(app.flops_total * 3), None, "单次迭代总 FLOPs"),
- )
-
- st.markdown("") # 间距
-
- # 参数量和计算量分析
- col1, col2 = st.columns(2)
-
- with col1:
- with st.expander("📦 参数量分析", expanded=True):
- st.dataframe(
- pd.DataFrame.from_records([
- {
- "层级": "🔹 单块",
- "Embedding": "-",
- "Attention": app.nparam_attn,
- "归一化": app.nparam_norm,
- "FFN": app.nparam_mlp,
- "合计": app.nparam_attn + app.nparam_norm + app.nparam_mlp,
- },
- {
- "层级": "🔸 整体",
- "Embedding": app.nparam_embedding,
- "Attention": app.nparam_attn * app.num_blocks,
- "归一化": app.nparam_norm * app.num_blocks,
- "FFN": app.nparam_mlp * app.num_blocks,
- "合计": app.nparam_total,
- },
- ]).style.format(
- {col: human_readable_num for col in ["Embedding", "Attention", "归一化", "FFN", "合计"]}
- if use_humanreadable else None
- ),
- use_container_width=True,
- hide_index=True,
- )
-
- with col2:
- with st.expander("⚡ 计算量分析 (FLOPs)", expanded=True):
- st.dataframe(
- pd.DataFrame.from_records([
- {
- "层级": "🔹 单块",
- "Embedding": "-",
- "Attention": app.flops_attn,
- "归一化": app.flops_norm,
- "FFN": app.flops_mlp,
- "合计": app.flops_attn + app.flops_norm + app.flops_mlp,
- },
- {
- "层级": "🔸 整体×3",
- "Embedding": 3 * app.flops_embedding,
- "Attention": 3 * app.flops_attn * app.num_blocks,
- "归一化": 3 * app.flops_norm * app.num_blocks,
- "FFN": 3 * app.flops_mlp * app.num_blocks,
- "合计": 3 * app.flops_total,
- },
- ]).style.format(
- {col: human_readable_flops for col in ["Embedding", "Attention", "归一化", "FFN", "合计"]}
- if use_humanreadable else None
- ),
- use_container_width=True,
- hide_index=True,
- )
-
- # 详细计算量表格(折叠)
- with st.expander("📋 详细计算量分解", expanded=False):
- st.dataframe(
- pd.DataFrame.from_records([
- {"阶段": "块前向", "Embedding": 0.0, "Attention": app.flops_attn, "归一化": app.flops_norm, "FFN": app.flops_mlp},
- {"阶段": "块反向", "Embedding": 0.0, "Attention": 2 * app.flops_attn, "归一化": 2 * app.flops_norm, "FFN": 2 * app.flops_mlp},
- {"阶段": "整体前向", "Embedding": app.flops_embedding, "Attention": app.flops_attn * app.num_blocks, "归一化": app.flops_norm * app.num_blocks, "FFN": app.flops_norm * app.num_blocks, "总计": app.flops_total},
- {"阶段": "整体反向", "Embedding": 2 * app.flops_embedding, "Attention": 2 * app.flops_attn * app.num_blocks, "归一化": 2 * app.flops_norm * app.num_blocks, "FFN": 2 * app.flops_norm * app.num_blocks, "总计": 2 * app.flops_total},
- ]).style.format(human_readable_flops if use_humanreadable else None),
- use_container_width=True,
- hide_index=True,
- )
-
- # 通信统计
- with st.expander("📡 通信量分析", expanded=False):
- st.caption("通信操作: all reduce / all gather / reduce scatter / send recv")
- comm_data = pd.DataFrame.from_records([
- {"阶段": "块前向", "Embedding": app.comm_embedding_fw, "Attention": app.comm_attn_fw, "FFN": app.comm_mlp_fw},
- {"阶段": "块反向", "Embedding": app.comm_embedding_bw, "Attention": app.comm_attn_bw, "FFN": app.comm_mlp_bw},
- {"阶段": "整体前向", "Embedding": app.comm_embedding_fw, "Attention": app.comm_attn_fw * app.num_blocks, "FFN": app.comm_mlp_fw * app.num_blocks, "总计": app.comm_total_fw * app.num_blocks + app.comm_pipeline_parallel_fw},
- {"阶段": "整体反向", "Embedding": app.comm_embedding_bw, "Attention": app.comm_attn_bw * app.num_blocks, "FFN": app.comm_mlp_bw * app.num_blocks, "总计": app.comm_total_bw * app.num_blocks + app.comm_pipeline_parallel_bw},
- ])
- st.dataframe(
- comm_data.map(lambda x: str(x).replace("\n", " | ") if isinstance(x, str) else x),
- use_container_width=True,
- hide_index=True,
- )
-
-
-# ============================================================================
-# 执行细节 Tab
-# ============================================================================
-with tab_detail:
- if use_raw_output:
- raw_output_section(stats, make_summary)
-
- stats_scope = hp.scope(**stats)
-
- section_header("阶段分解", "各训练阶段的 FLOPs 和显存访问量")
-
- def make_detail(da, title, expanded=True):
- """渲染详细信息表格和图表"""
- with st.expander(title, expanded=expanded):
- c1, c2 = st.columns([0.35, 0.65])
- with c1:
- st.dataframe(da, use_container_width=True, hide_index=True)
- with c2:
- fig = px.bar(
- da,
- x="stage",
- y="value",
- color="stage",
- color_discrete_sequence=px.colors.qualitative.Set2,
- )
- fig.update_layout(
- showlegend=False,
- margin=dict(l=20, r=20, t=30, b=20),
- xaxis_title="",
- yaxis_title="",
- )
- st.plotly_chart(fig, use_container_width=True)
-
- col1, col2 = st.columns(2)
-
- # FLOPs 详情
- with col1:
- make_detail(
- pd.DataFrame({
- "stage": ["前向", "激活梯度", "权重梯度", "优化器"],
- "value": [
- stats_scope.block_fw_flops | 0,
- stats_scope.block_agrad_flops | 0,
- stats_scope.block_wgrad_flops | 0,
- stats_scope.block_optim_flops | 0,
- ],
- }),
- "⚡ FLOPs 分布",
- )
-
- # 显存详情
- with col2:
- make_detail(
- pd.DataFrame({
- "stage": ["前向", "激活梯度", "权重梯度", "优化器"],
- "value": [
- stats_scope.block_fw_mem_accessed | 0,
- stats_scope.block_agrad_mem_accessed | 0,
- stats_scope.block_wgrad_mem_accessed | 0,
- stats_scope.block_optim_mem_accessed | 0,
- ],
- }),
- "💾 显存访问量",
- )
-
- # 提示信息
- info_card(
- "分析说明",
- "FLOPs 分布展示了各训练阶段的计算量,显存访问量反映了数据移动开销。优化器阶段通常占用较少的 FLOPs 但可能有较多的显存访问。",
- icon="💡"
- )
diff --git a/pyproject.toml b/pyproject.toml
index 48a38e3..c598641 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -41,11 +41,9 @@ classifiers = [
]
dependencies = [
- "hyperparameter>=0.4.0",
"numpy>=1.20.0",
"pandas>=1.3.0",
"rich>=12.0.0",
- "sympy>=1.12.1",
"nicegui>=3.15,<4",
"psutil>=5.9.0",
]
@@ -54,12 +52,7 @@ dependencies = [
performance-data = [
"pyarrow>=12.0.0",
]
-legacy-ui = [
- "streamlit>=1.53,<2",
- "streamlit-extras>=0.6,<1",
-]
full = [
- "blueprinting[legacy-ui]",
"plotly>=5.0.0",
]
dev = [
@@ -111,10 +104,9 @@ include = [
]
exclude = [
"/.github",
- "/docs",
- "/examples",
- "/pages",
- "/data/evidence",
+ "/docs",
+ "/examples",
+ "/data/evidence",
]
[tool.hatch.build.targets.wheel]
@@ -262,6 +254,4 @@ dev = [
"pytest-asyncio>=0.24.0",
"pytest-cov>=4.0.0",
"ruff>=0.1.0",
- "streamlit>=1.53,<2",
- "streamlit-extras>=0.6,<1",
]
diff --git a/src/blueprinting/console.py b/src/blueprinting/console.py
deleted file mode 100755
index 58bbb29..0000000
--- a/src/blueprinting/console.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from typing import Optional
-
-import pandas as pd
-from rich import box
-from rich.console import Console
-from rich.table import Table
-
-console = Console()
-
-
-def df_to_table(
- pandas_dataframe: pd.DataFrame,
- rich_table: Table,
- show_index: bool = False,
- index_name: Optional[str] = None,
-) -> Table:
- """Convert a pandas.DataFrame obj into a rich.Table obj.
- Args:
- pandas_dataframe (DataFrame): A Pandas DataFrame to be converted to a rich Table.
- rich_table (Table): A rich Table that should be populated by the DataFrame values.
- show_index (bool): Add a column with a row count to the table. Defaults to True.
- index_name (str, optional): The column name to give to the index column. Defaults to None, showing no value.
- Returns:
- Table: The rich Table instance passed, populated with the DataFrame values."""
-
- if show_index:
- index_name = str(index_name) if index_name else ""
- rich_table.add_column(index_name)
-
- for column in pandas_dataframe.columns:
- rich_table.add_column(str(column))
-
- for index, value_list in enumerate(pandas_dataframe.values.tolist()):
- row = [str(index)] if show_index else []
- row += [str(x) for x in value_list]
- rich_table.add_row(*row)
-
- return rich_table
-
-
-def print_rich_table(df, caption=None):
- console = Console()
- table = Table(show_header=True, header_style="bold magenta", caption=caption)
- table = df_to_table(df, table)
- table.row_styles = ["none", "dim"]
- table.box = box.SIMPLE_HEAD
- table.expand = True
- console.print(table)
diff --git a/src/blueprinting/core/__init__.py b/src/blueprinting/core/__init__.py
deleted file mode 100644
index d9a89f3..0000000
--- a/src/blueprinting/core/__init__.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""Blueprinting 核心计算模块
-
-符号类型 (均基于 sympy.Function,惰性求值):
-- SymPick: 条件表达式 (if-then-else)
-- SymMax / SymMin: 惰性最大 / 最小值
-
-预定义符号 (按类组织: Model, Parallel, Exec, Hardware):
-- 也可扁平导入: from blueprinting.core import BATCH, TP, ...
-"""
-
-from .symbolic import SymMax, SymMin, SymPick, eval_lazy, get_symbol, sym_max, sym_min
-from .symbols import (
- ALL_SYMBOLS,
- ATTN_HEADS,
- BATCH,
- CP,
- DP,
- EP,
- EXEC_SYMBOLS,
- FEEDFORWARD,
- HARDWARE_SYMBOLS,
- HEAD_DIM,
- HIDDEN,
- MEM_BANDWIDTH,
- MICRO_BATCH,
- MODEL_SYMBOLS,
- NET_BANDWIDTH,
- NUM_LAYERS,
- NUM_MICRO_BATCHES,
- PARALLEL_SYMBOLS,
- PEAK_FLOPS,
- PP,
- SEQ,
- TP,
- VOCAB_SIZE,
- Exec,
- Hardware,
- Model,
- Parallel,
-)
diff --git a/src/blueprinting/core/symbolic.py b/src/blueprinting/core/symbolic.py
deleted file mode 100644
index 1e872c0..0000000
--- a/src/blueprinting/core/symbolic.py
+++ /dev/null
@@ -1,146 +0,0 @@
-"""符号表达式扩展
-
-基于 sympy.Function 的惰性求值符号表达式:
-- SymPick: 条件表达式 (if-then-else)
-- SymMax / SymMin: 惰性 max / min
-- eval_lazy: 统一替换求值
-"""
-
-from typing import Any, Dict
-
-from sympy import Expr, Function, S, Symbol, sympify
-
-from .symbols import ALL_SYMBOLS
-
-
-def get_symbol(name: str) -> Symbol:
- """根据名称获取预定义符号,不存在则创建新符号."""
- symbol_map = {s.name: s for s in ALL_SYMBOLS}
- return symbol_map.get(name, Symbol(name))
-
-
-class SymPick(Function):
- """符号条件表达式 (if-then-else).
-
- 条件可判定时自动折叠,否则保持惰性。subs() 后自动重新触发 eval。
-
- >>> tp = Symbol('tp')
- >>> SymPick(tp > 1, tp * 100, 0).subs(tp, 4)
- 400
- """
-
- nargs = (3,)
-
- @classmethod
- def eval(cls, cond, true_expr, false_expr):
- if cond is S.true or cond == S.true:
- return true_expr
- if cond is S.false or cond == S.false:
- return false_expr
- try:
- return true_expr if bool(cond) else false_expr
- except TypeError:
- return None
-
- def _latex(self, printer):
- c = printer.doprint(self.args[0])
- t = printer.doprint(self.args[1])
- f = printer.doprint(self.args[2])
- return rf"\begin{{cases}} {t} & \text{{if }} {c} \\ {f} & \text{{otherwise}} \end{{cases}}"
-
-
-class SymMax(Function):
- """惰性最大值,全部具体化时折叠,否则保持未求值。
-
- 与 sympy.Max 的区别:不触发代数简化。支持嵌套自动展平。
-
- >>> a = Symbol('a')
- >>> SymMax(a, 10).subs(a, 15)
- 15
- """
-
- @classmethod
- def eval(cls, *args):
- flat, changed = [], False
- for a in args:
- if isinstance(a, SymMax):
- flat.extend(a.args)
- changed = True
- else:
- flat.append(a)
- if changed:
- return cls(*flat)
- if all(a.is_number for a in args):
- return sympify(max(float(a) for a in args))
- return None
-
- def _latex(self, printer):
- return rf"\max\left({', '.join(printer.doprint(a) for a in self.args)}\right)"
-
-
-class SymMin(Function):
- """惰性最小值,与 SymMax 对称。
-
- >>> a = Symbol('a')
- >>> SymMin(a, 10).subs(a, 5)
- 5
- """
-
- @classmethod
- def eval(cls, *args):
- flat, changed = [], False
- for a in args:
- if isinstance(a, SymMin):
- flat.extend(a.args)
- changed = True
- else:
- flat.append(a)
- if changed:
- return cls(*flat)
- if all(a.is_number for a in args):
- return sympify(min(float(a) for a in args))
- return None
-
- def _latex(self, printer):
- return rf"\min\left({', '.join(printer.doprint(a) for a in self.args)}\right)"
-
-
-def sym_max(a, b):
- """创建惰性 max,纯数值时直接返回 float。"""
- if isinstance(a, (int, float)) and isinstance(b, (int, float)):
- return max(float(a), float(b))
- return SymMax(sympify(a), sympify(b))
-
-
-def sym_min(a, b):
- """创建惰性 min,纯数值时直接返回 float。"""
- if isinstance(a, (int, float)) and isinstance(b, (int, float)):
- return min(float(a), float(b))
- return SymMin(sympify(a), sympify(b))
-
-
-def eval_lazy(expr, subs: dict = None):
- """对表达式执行符号替换并尝试数值化.
-
- Args:
- expr: 任意表达式
- subs: 替换字典,key 可以是 Symbol 或 str
- """
- if isinstance(expr, (int, float)):
- return expr
-
- subs = subs or {}
- normalized: Dict[Symbol, Any] = {
- (get_symbol(k) if isinstance(k, str) else k): v for k, v in subs.items()
- }
-
- if isinstance(expr, Expr):
- result = expr.subs(normalized)
- if result.is_number:
- try:
- return float(result)
- except (TypeError, ValueError):
- return result
- return result
-
- return expr
diff --git a/src/blueprinting/core/symbols.py b/src/blueprinting/core/symbols.py
deleted file mode 100644
index 8894c97..0000000
--- a/src/blueprinting/core/symbols.py
+++ /dev/null
@@ -1,83 +0,0 @@
-"""预定义符号常量
-
-按类别组织仿真建模中的符号,便于引用和批量操作。
-
- from blueprinting.core import Model, Parallel, BATCH, TP, ALL_SYMBOLS
-"""
-
-from sympy import Symbol
-
-
-class Model:
- """模型结构相关符号."""
-
- BATCH = Symbol("batch", positive=True, integer=True)
- SEQ = Symbol("seq", positive=True, integer=True)
- HIDDEN = Symbol("hidden", positive=True, integer=True)
- FEEDFORWARD = Symbol("feedforward", positive=True, integer=True)
- NUM_LAYERS = Symbol("num_layers", positive=True, integer=True)
- ATTN_HEADS = Symbol("attn_heads", positive=True, integer=True)
- HEAD_DIM = Symbol("head_dim", positive=True, integer=True)
- VOCAB_SIZE = Symbol("vocab_size", positive=True, integer=True)
-
-
-class Parallel:
- """并行策略相关符号."""
-
- TP = Symbol("tp", positive=True, integer=True)
- PP = Symbol("pp", positive=True, integer=True)
- DP = Symbol("dp", positive=True, integer=True)
- CP = Symbol("cp", positive=True, integer=True)
- EP = Symbol("ep", positive=True, integer=True)
-
-
-class Exec:
- """执行 / 调度相关符号."""
-
- MICRO_BATCH = Symbol("micro_batch", positive=True, integer=True)
- NUM_MICRO_BATCHES = Symbol("num_micro_batches", positive=True, integer=True)
-
-
-class Hardware:
- """硬件能力相关符号."""
-
- PEAK_FLOPS = Symbol("peak_flops", positive=True)
- MEM_BANDWIDTH = Symbol("mem_bandwidth", positive=True)
- NET_BANDWIDTH = Symbol("net_bandwidth", positive=True)
-
-
-# 符号集合
-MODEL_SYMBOLS = {
- Model.BATCH,
- Model.SEQ,
- Model.HIDDEN,
- Model.FEEDFORWARD,
- Model.NUM_LAYERS,
- Model.ATTN_HEADS,
- Model.HEAD_DIM,
- Model.VOCAB_SIZE,
-}
-PARALLEL_SYMBOLS = {Parallel.TP, Parallel.PP, Parallel.DP, Parallel.CP, Parallel.EP}
-EXEC_SYMBOLS = {Exec.MICRO_BATCH, Exec.NUM_MICRO_BATCHES}
-HARDWARE_SYMBOLS = {Hardware.PEAK_FLOPS, Hardware.MEM_BANDWIDTH, Hardware.NET_BANDWIDTH}
-ALL_SYMBOLS = MODEL_SYMBOLS | PARALLEL_SYMBOLS | EXEC_SYMBOLS | HARDWARE_SYMBOLS
-
-# 扁平别名
-BATCH = Model.BATCH
-SEQ = Model.SEQ
-HIDDEN = Model.HIDDEN
-FEEDFORWARD = Model.FEEDFORWARD
-NUM_LAYERS = Model.NUM_LAYERS
-ATTN_HEADS = Model.ATTN_HEADS
-HEAD_DIM = Model.HEAD_DIM
-VOCAB_SIZE = Model.VOCAB_SIZE
-TP = Parallel.TP
-PP = Parallel.PP
-DP = Parallel.DP
-CP = Parallel.CP
-EP = Parallel.EP
-MICRO_BATCH = Exec.MICRO_BATCH
-NUM_MICRO_BATCHES = Exec.NUM_MICRO_BATCHES
-PEAK_FLOPS = Hardware.PEAK_FLOPS
-MEM_BANDWIDTH = Hardware.MEM_BANDWIDTH
-NET_BANDWIDTH = Hardware.NET_BANDWIDTH
diff --git a/src/blueprinting/io.py b/src/blueprinting/io.py
deleted file mode 100644
index 532a6a3..0000000
--- a/src/blueprinting/io.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""IO utilities for blueprinting."""
-
-import json
-import os
-from typing import Any, Dict
-
-__all__ = ["read_json_file", "write_json_file", "is_json_extension"]
-
-
-def read_json_file(filepath: str) -> Dict[str, Any]:
- """Read a JSON file and return its contents.
-
- Args:
- filepath: Path to the JSON file
-
- Returns:
- Dictionary with file contents
- """
- with open(filepath) as f:
- return json.load(f)
-
-
-def write_json_file(data: Dict[str, Any], filepath: str) -> None:
- """Write data to a JSON file.
-
- Args:
- data: Dictionary to write
- filepath: Path to the output file
- """
- with open(filepath, "w") as f:
- json.dump(data, f, indent=2)
-
-
-def is_json_extension(filepath: str) -> bool:
- """Check if a filepath has a JSON extension.
-
- Args:
- filepath: Path to check
-
- Returns:
- True if the file has a .json extension
- """
- _, ext = os.path.splitext(filepath)
- return ext.lower() == ".json"
diff --git a/src/blueprinting/nn/__init__.py b/src/blueprinting/nn/__init__.py
deleted file mode 100755
index 6fcf1c2..0000000
--- a/src/blueprinting/nn/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from .base import TensorDef
-from .modules import (
- AddDef,
- BatchMatmulDef,
- ColumnParallelLinear,
- LayerNormDef,
- LinearDef,
- MulDef,
- RMSNormDef,
- RowParallelLinear,
- SequenceParallelAdd,
- SequenceParallelRMSNorm,
- SiLUDef,
- SoftmaxDef,
-)
diff --git a/src/blueprinting/nn/base.py b/src/blueprinting/nn/base.py
deleted file mode 100755
index 2944701..0000000
--- a/src/blueprinting/nn/base.py
+++ /dev/null
@@ -1,296 +0,0 @@
-from dataclasses import dataclass
-from typing import List, Tuple
-
-import hyperparameter as hp
-from sympy import Expr
-
-from blueprinting.types.base import Calculation, DType, TensorDef
-
-
-@hp.param("sys")
-def flops_throughput(dtype, flops, use_matrix_core=True):
- if isinstance(flops, Expr):
- subs = hp.scope.blueprinting.symbolic.subs | []
- flops = int(flops.subs(dict(subs)))
- if use_matrix_core:
- throughput = 1e12 * (hp.scope.sys.matrix.float16.tflops | 0)
- efficiency = hp.scope.sys.matrix.float16.gflops_efficiency | []
- else:
- throughput = 1e12 * (hp.scope.sys.vector.float16.tflops | 0)
- efficiency = hp.scope.sys.vector.float16.gflops_efficiency | []
- efficiency_ratio = 1.0
- for gflops, eff in efficiency:
- if flops > gflops * 1e9:
- efficiency_ratio = eff
- break
- return throughput * efficiency_ratio
-
-
-@hp.param("sys")
-def memrw_throughput(memrw):
- if isinstance(memrw, Expr):
- subs = hp.scope.blueprinting.symbolic.subs | {}
- memrw = int(memrw.subs(dict(subs)))
- throughput = 1e9 * (hp.scope.sys.mem1.GBps | 0)
- efficiency = hp.scope.sys.mem1.MB_efficiency | []
- efficiency_ratio = 1.0
- for mbytes, eff in efficiency:
- if memrw > mbytes * 1e6:
- efficiency_ratio = eff
- break
- return throughput * efficiency_ratio
-
-
-@hp.param("sys")
-def c2c_throughput():
- networks = hp.scope.sys.networks | []
- throughput = 1e9 * (networks[0]["bandwidth"])
- efficiency = networks[0]["efficiency"]
- return throughput * efficiency
-
-
-@hp.param("sys")
-def c2c_nbytes(c2c, comm_type, num_peers):
- if isinstance(c2c, Expr):
- subs = hp.scope.blueprinting.symbolic.subs | {}
- c2c = int(c2c.subs(dict(subs)))
- networks = hp.scope.sys.networks | []
- ops = networks[0]["ops"]
- op_size = 0
- for op, [scalar, offset] in ops.items():
- if comm_type == op:
- op_size = scalar * c2c + offset * (1 / num_peers * c2c)
- break
- return op_size
-
-
-def c2c_times(comm_type, comm_size, throughput):
- networks = hp.scope.sys.networks | []
- ops = networks[0]["ops"]
- latency = networks[0]["latency"]
- times = comm_size / throughput
- return times + latency if comm_type in ops else 0.0
-
-
-@dataclass
-class LayerDef:
- dtype: DType
-
- def forward(self, *inputs) -> TensorDef:
- return TensorDef()
-
- def __call__(self, *inputs: TensorDef):
- return Calculation(inputs, self.forward(*inputs), self)
-
- @property
- def nelems(self) -> int:
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_input(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return inputs[0].nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_output(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return self(*inputs).nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_weight(self) -> int:
- if not hasattr(self, "weight"):
- return 0
- return self.weight.nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_weight_grads(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if not hasattr(self, "weight"):
- return 0
- weight_grads = self.weight.nbytes
- return weight_grads
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return inputs[0].nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity_grads(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return self(*inputs).nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def c2c_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def c2c_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_flops_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- flops = self.flops_fw
- throughput = flops_throughput(self.dtype, flops)
- return flops / throughput
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_flops_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- flops = self.flops_bw
- throughput = flops_throughput(self.dtype, flops)
- return flops / throughput
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_memrw_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- memrw = self.memory_fw
- throughput = memrw_throughput(memrw)
- return memrw / throughput
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_memrw_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- memrw = self.memory_bw
- throughput = memrw_throughput(memrw)
- return memrw / throughput
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- time_c2c_fw = self.time_c2c_fw
- time_flops = self.time_flops_fw
- time_memrw = self.time_memrw_fw
- if hp.scope.sys.processing_mode | "roofline" == "roofline":
- return max(time_flops, time_memrw)
- # hp.scope.sys.processing_mode == "no_overlap"
- return time_flops + time_memrw + time_c2c_fw
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- time_flops = self.time_flops_bw
- time_memrw = self.time_memrw_bw
- if hp.scope.sys.processing_mode | "roofline" == "roofline":
- return max(time_flops, time_memrw)
- # hp.scope.sys.processing_mode == "no_overlap"
- return time_flops + time_memrw
-
- @property
- @hp.param("blueprinting.layerdef")
- def memory_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- input_memory = self.nbytes_input
- output_memory = self.nbytes_output
-
- if hasattr(self, "weight"):
- weight_memory = self.nbytes_weight
- return input_memory + weight_memory + output_memory
- else:
- return input_memory + output_memory
-
- @property
- @hp.param("blueprinting.layerdef")
- def wgrad_memory(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if not hasattr(self, "weight"):
- return 0
- wgard_mem = (
- self.nbytes_weight_grads + self.nbytes_activity + self.nbytes_activity_grads
- )
- return wgard_mem
-
- @property
- @hp.param("blueprinting.layerdef")
- def agrad_memory(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if hasattr(self, "weight"):
- agard_mem = (
- self.nbytes_weight + self.nbytes_activity + self.nbytes_activity_grads
- )
- else:
- agard_mem = self.memory_fw
-
- return agard_mem
-
- @property
- @hp.param("blueprinting.layerdef")
- def memory_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- agrad_memory = self.agrad_memory
- wgrad_memory = self.wgrad_memory
-
- return agrad_memory + wgrad_memory
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_c2c_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_c2c_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def placement_weight(self, inputs: List[TensorDef] = None) -> Tuple[TensorDef, ...]:
- if inputs is None:
- inputs = []
- return [TensorDef([0, 0], dtype=self.dtype)]
-
- @property
- def dsize(self) -> int:
- return self.dtype.size
diff --git a/src/blueprinting/nn/modules.py b/src/blueprinting/nn/modules.py
deleted file mode 100755
index 77086e2..0000000
--- a/src/blueprinting/nn/modules.py
+++ /dev/null
@@ -1,937 +0,0 @@
-from dataclasses import dataclass
-from functools import reduce
-from operator import mul
-from typing import List, Tuple, Union
-
-import hyperparameter as hp
-from sympy import Expr
-
-from blueprinting.core import SymPick
-from blueprinting.nn.base import LayerDef, c2c_nbytes, c2c_throughput, c2c_times
-from blueprinting.types.base import DType, TensorDef
-from blueprinting.util import pick
-
-
-@hp.param("blueprinting.flops")
-def gemm_flops(
- A: TensorDef, B: TensorDef, with_bias=False, count_add=True
-) -> Union[int, Expr]:
- """Calculate flops for GEMM
-
- Examples
- --------
- >>> from sympy import symbols
- >>> a, b, c, d, e, f, g = symbols("a b c d e f g")
- >>> gemm_flops(TensorDef([a, b]), TensorDef([b, c]))
- a*b*c
- >>> gemm_flops(TensorDef([a, b]), TensorDef([b, c]), count_add=True)
- a*b*(2*c - 1)
- >>> gemm_flops(TensorDef([a, b]), TensorDef([b, c]), with_bias=True, count_add=True)
- 2*a*b*c
- >>> gemm_flops(TensorDef([a, b, c]), TensorDef([c, d]))
- a*b*c*d
- >>> gemm_flops(TensorDef([a, b, c]), TensorDef([c, d]), count_add=True)
- a*b*c*(2*d - 1)
- >>> gemm_flops(TensorDef([a, b, c]), TensorDef([c, d]), with_bias=True, count_add=True)
- 2*a*b*c*d
- >>> gemm_flops(TensorDef([a, b, c]), TensorDef([c, b, d]))
- a*b*c*d
- >>> gemm_flops(TensorDef([a, b, c]), TensorDef([c, b, d]), count_add=True)
- a*b*c*(2*d - 1)
- >>> gemm_flops(TensorDef([a, b, c]), TensorDef([c, b, d]), with_bias=True, count_add=True)
- 2*a*b*c*d
- """
- if A.shape[-1] != B.shape[0]:
- raise Exception(f"bad gemm: {A} x {B}: {A.shape[-1]} != {B.shape[0]}")
- offset = len(B.shape) - 1
- for idx, (a, b) in enumerate(zip(reversed(A.shape), B.shape[:-1])):
- if a != b:
- offset = idx
- break
-
- if count_add:
- if with_bias:
- return reduce(mul, A.shape) * 2 * reduce(mul, B.shape[offset:])
- return reduce(mul, A.shape) * (2 * reduce(mul, B.shape[offset:]) - 1)
- return reduce(mul, A.shape) * reduce(mul, B.shape[offset:])
-
-
-@hp.param("blueprinting.flops")
-def batch_gemm_flops(
- A: TensorDef, B: TensorDef, with_bias=False, count_add=True
-) -> Union[int, Expr]:
- if A.shape[-1] != B.shape[-2]:
- raise Exception(f"bad batch_gemm: {A} x {B}: {A.shape[-1]} != {B.shape[-2]}")
-
- if count_add:
- if with_bias:
- return reduce(mul, A.shape) * 2 * B.shape[-1]
- return reduce(mul, A.shape) * (2 * (B.shape[-1]) - 1)
- return reduce(mul, A.shape) * B.shape[-1]
-
-
-@dataclass
-class LinearDef(LayerDef):
- dtype: DType
- in_features: Union[int, Expr]
- out_features: Union[int, Expr]
- bias: bool = True
-
- def __post_init__(self):
- self.weight = TensorDef((self.in_features, self.out_features), dtype=self.dtype)
-
- def forward(self, *inputs) -> TensorDef:
- return TensorDef(inputs[0].shape[:-1] + [self.out_features], inputs[0].dtype)
-
- @property
- def nelems(self):
- w = self.weight.nelems
- return w + self.out_features if self.bias else w
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return gemm_flops(inputs[0], self.weight, with_bias=self.bias)
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- grad_output = self(*inputs).belike()
- act_flops = gemm_flops(grad_output, self.weight.T, with_bias=self.bias)
- w_flops = gemm_flops(inputs[0].T, grad_output, with_bias=self.bias)
- return act_flops + w_flops
-
-
-@dataclass
-class ColumnParallelLinear(LinearDef):
- tensor_model_parallel_size: int = 1
- tensor_par_comm_type: str = "ar"
-
- def __post_init__(self):
- self.weight = TensorDef(
- (self.in_features, self.out_features / self.tensor_model_parallel_size),
- dtype=self.dtype,
- )
-
- def forward(self, *inputs) -> TensorDef:
- return TensorDef(
- inputs[0].shape[:-1]
- + [self.out_features / self.tensor_model_parallel_size],
- inputs[0].dtype,
- )
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None):
- """ColumnParallelLinear backward FLOPs (不包含通信开销)"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- grad_output = self(*inputs).belike()
- act_flops = gemm_flops(grad_output, self.weight.T, with_bias=self.bias)
- w_flops = gemm_flops(inputs[0].T, grad_output, with_bias=self.bias)
- # 注意: all_reduce 是通信开销,不应计入 FLOPs
- return act_flops + w_flops
-
- @property
- @hp.param("blueprinting.layerdef")
- def c2c_fw(self, inputs: List[TensorDef] = None) -> int:
- """ColumnParallelLinear forward c2c: rs_ag 模式下才有通信"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- nbytes = self.forward(*inputs).nbytes
- # rs_ag 模式且 TP>1 时有 all_gather 通信
- if self.tensor_par_comm_type == "rs_ag":
- return SymPick(self.tensor_model_parallel_size > 1, nbytes, 0)
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def c2c_bw(self, inputs: List[TensorDef] = None) -> int:
- """ColumnParallelLinear backward c2c: TP>1 时有通信"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- nbytes = self.forward(*inputs).nbytes
- return SymPick(self.tensor_model_parallel_size > 1, nbytes, 0)
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_c2c_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- c2c = self.c2c_fw
- comm_type = pick(self.tensor_par_comm_type == "rs_ag", "all_gather", "identity")
- throughput = c2c_throughput()
- comm_size = c2c_nbytes(c2c, comm_type, self.tensor_model_parallel_size)
- return c2c_times(comm_type, comm_size, throughput)
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_c2c_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- c2c = self.c2c_bw
- comm_type = pick(
- self.tensor_par_comm_type == "rs_ag", "reduce_scatter", "all_reduce"
- )
- throughput = c2c_throughput()
- comm_size = c2c_nbytes(c2c, comm_type, self.tensor_model_parallel_size)
- return c2c_times(comm_type, comm_size, throughput)
-
- @property
- @hp.param("blueprinting.layerdef")
- def placement_weight(self, inputs: List[TensorDef] = None) -> Tuple[TensorDef, ...]:
- if inputs is None:
- inputs = []
- row = self.in_features
- column = self.out_features / self.tensor_model_parallel_size
- return [TensorDef([row, column], dtype=self.dtype)]
-
-
-@dataclass
-class RowParallelLinear(LinearDef):
- tensor_model_parallel_size: int = 1
- tensor_par_comm_type: str = "ar"
-
- def __post_init__(self):
- self.weight = TensorDef(
- (self.in_features / self.tensor_model_parallel_size, self.out_features),
- dtype=self.dtype,
- )
-
- def forward(self, *inputs) -> TensorDef:
- return TensorDef(inputs[0].shape[:-1] + [self.out_features], inputs[0].dtype)
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_input(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- input = TensorDef(
- inputs[0].shape[:-1] + [self.in_features / self.tensor_model_parallel_size],
- inputs[0].dtype,
- )
- return input.nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- input = TensorDef(
- inputs[0].shape[:-1] + [self.in_features / self.tensor_model_parallel_size],
- inputs[0].dtype,
- )
- return input.nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None) -> int:
- """RowParallelLinear forward FLOPs (不包含通信开销)"""
- if inputs is None:
- inputs = []
- input = TensorDef(
- inputs[0].shape[:-1] + [self.in_features / self.tensor_model_parallel_size],
- inputs[0].dtype,
- )
- output_flops = gemm_flops(input, self.weight, with_bias=self.bias)
- # 注意: all_reduce 是通信开销,不应计入 FLOPs
- return output_flops
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- input = TensorDef(
- inputs[0].shape[:-1] + [self.in_features / self.tensor_model_parallel_size],
- inputs[0].dtype,
- )
- grad_output = self(*inputs).belike()
- act_flops = gemm_flops(grad_output, self.weight.T, with_bias=self.bias)
- w_flops = gemm_flops(input.T, grad_output, with_bias=self.bias)
- return act_flops + w_flops
-
- @property
- @hp.param("blueprinting.layerdef")
- def c2c_fw(self, inputs: List[TensorDef] = None) -> int:
- """RowParallelLinear forward c2c: TP>1 时有 all_reduce/reduce_scatter 通信"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- nbytes = self.forward(*inputs).nbytes
- return SymPick(self.tensor_model_parallel_size > 1, nbytes, 0)
-
- @property
- @hp.param("blueprinting.layerdef")
- def c2c_bw(self, inputs: List[TensorDef] = None) -> int:
- """RowParallelLinear backward c2c: rs_ag 模式下才有通信"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- nbytes = self.forward(*inputs).nbytes
- # rs_ag 模式且 TP>1 时有 all_gather 通信
- if self.tensor_par_comm_type == "rs_ag":
- return SymPick(self.tensor_model_parallel_size > 1, nbytes, 0)
- return 0
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_c2c_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- c2c = self.c2c_fw
- comm_type = pick(
- self.tensor_par_comm_type == "rs_ag", "reduce_scatter", "all_reduce"
- )
- throughput = c2c_throughput()
- comm_size = c2c_nbytes(c2c, comm_type, self.tensor_model_parallel_size)
- return c2c_times(comm_type, comm_size, throughput)
-
- @property
- @hp.param("blueprinting.layerdef")
- def time_c2c_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- c2c = self.c2c_bw
- comm_type = pick(self.tensor_par_comm_type == "rs_ag", "all_gather", "identity")
- throughput = c2c_throughput()
- comm_size = c2c_nbytes(c2c, comm_type, self.tensor_model_parallel_size)
- return c2c_times(comm_type, comm_size, throughput)
-
- @property
- @hp.param("blueprinting.layerdef")
- def placement_weight(self, inputs: List[TensorDef] = None) -> Tuple[TensorDef, ...]:
- if inputs is None:
- inputs = []
- row = self.in_features / self.tensor_model_parallel_size
- column = self.out_features
- return [TensorDef([row, column], dtype=self.dtype)]
-
-
-@dataclass
-class LayerNormDef(LayerDef):
- dtype: DType
- normalized_shape: List[Union[int, Expr]]
- bias: bool = True
-
- def __post_init__(self):
- self.weight = TensorDef(self.normalized_shape, dtype=self.dtype)
-
- def forward(self, *inputs) -> TensorDef:
- return TensorDef(inputs[0].shape, inputs[0].dtype)
-
- @property
- def nelems(self) -> int:
- return self.normalized_shape * 2 if self.bias else self.normalized_shape
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_input(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return inputs[0].nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_output(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return self(*inputs).nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity_grads(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return inputs[0].nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_weight(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return self.normalized_shape * 2 * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_weight_grads(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return self.normalized_shape * 2 * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- num_features = reduce(mul, inputs[0].shape)
- mean_flops = num_features
- var_flops = 2 * num_features
- offset_flops = num_features
- scale_flops = num_features
- bias_flops = num_features if self.bias else 0
- return mean_flops + var_flops + offset_flops + scale_flops + bias_flops
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None):
- """LayerNorm backward: 涉及 mean/var 梯度计算,约 10-12 ops per element
-
- 参考: https://kratzert.github.io/2016/02/12/understanding-the-gradient-flow-through-the-batch-normalization-layer.html
- """
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- num_features = reduce(mul, inputs[0].shape)
- # 反向传播涉及: dx_hat, dvar, dmean, dx, dgamma, dbeta
- # 总计约 10-12 ops per element
- return 10 * num_features
-
- @property
- @hp.param("blueprinting.layerdef")
- def placement_weight(self, inputs: List[TensorDef] = None) -> Tuple[TensorDef, ...]:
- if inputs is None:
- inputs = []
- return [
- TensorDef([self.normalized_shape], dtype=self.dtype),
- TensorDef([self.normalized_shape], dtype=self.dtype),
- ]
-
-
-@dataclass
-class Conv2dDef(LayerDef):
- dtype: str
- in_channels: int
- out_channels: int
-
-
-"""
- https://kratzert.github.io/2016/02/12/understanding-the-gradient-flow-through-the-batch-normalization-layer.html
- https://cthorey.github.io./blog/2016/backpropagation/
-"""
-
-
-@dataclass
-class RMSNormDef(LayerDef):
- """RMSNorm: Root Mean Square Layer Normalization
-
- Forward: y = x / sqrt(mean(x^2) + eps) * gamma
- Backward: 需要计算 dx 和 dgamma
- """
-
- dtype: DType
- normalized_shape: List[Union[int, Expr]]
- eps: float = 1e-5
- elementwise_affine: bool = True
-
- def forward(self, *inputs):
- return TensorDef(inputs[0].shape, inputs[0].dtype)
-
- @property
- def nelems(self):
- return self.normalized_shape
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- num_features = reduce(mul, inputs[0].shape)
- square_flops = num_features # 平方运算
- mean_flops = num_features
- div_flops = num_features
- scale_flops = num_features
- return square_flops + div_flops + mean_flops + scale_flops
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None):
- """RMSNorm backward: ~8 ops per element for gradient computation"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- num_features = reduce(mul, inputs[0].shape)
- # Backward pass involves: computing input gradients and weight gradients
- # Similar complexity to forward, approximately 2x forward
- return 8 * num_features
-
- @property
- @hp.param("blueprinting.layerdef")
- def placement_weight(self, inputs: List[TensorDef] = None) -> Tuple[TensorDef, ...]:
- if inputs is None:
- inputs = []
- return [TensorDef([self.normalized_shape], dtype=self.dtype)]
-
-
-@dataclass
-class SequenceParallelRMSNorm(RMSNormDef):
- tensor_model_parallel_size: int = 1
-
- def forward(self, *inputs):
- seq_len = inputs[0].shape[-2] / self.tensor_model_parallel_size
- return TensorDef(
- inputs[0].shape[:-2] + [seq_len, inputs[0].shape[-1]], inputs[0].dtype
- )
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_input(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- seq_len = inputs[0].shape[-2] / self.tensor_model_parallel_size
- input = TensorDef(
- inputs[0].shape[:-2] + [seq_len, inputs[0].shape[-1]], inputs[0].dtype
- )
- return input.nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- seq_len = inputs[0].shape[-2] / self.tensor_model_parallel_size
- input = TensorDef(
- inputs[0].shape[:-2] + [seq_len, inputs[0].shape[-1]], inputs[0].dtype
- )
- return input.nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- seq_len = inputs[0].shape[-2] / self.tensor_model_parallel_size
- input = TensorDef(
- inputs[0].shape[:-2] + [seq_len, inputs[0].shape[-1]], inputs[0].dtype
- )
- num_features = reduce(mul, input.shape)
- square_flops = num_features # 平方运算
- mean_flops = num_features
- div_flops = num_features
- scale_flops = num_features
- return square_flops + div_flops + mean_flops + scale_flops
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None):
- """SequenceParallelRMSNorm backward"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- seq_len = inputs[0].shape[-2] / self.tensor_model_parallel_size
- input = TensorDef(
- inputs[0].shape[:-2] + [seq_len, inputs[0].shape[-1]], inputs[0].dtype
- )
- num_features = reduce(mul, input.shape)
- return 8 * num_features
-
- @property
- @hp.param("blueprinting.layerdef")
- def placement_weight(self, inputs: List[TensorDef] = None) -> Tuple[TensorDef, ...]:
- if inputs is None:
- inputs = []
- return [TensorDef([self.normalized_shape], dtype=self.dtype)]
-
-
-"""
- https://pytorch.org/docs/stable/generated/torch.bmm.html
-"""
-
-
-@dataclass
-class BatchMatmulDef(LayerDef):
- """Batch Matrix Multiplication: torch.bmm
-
- 无权重层,需要两个输入张量进行批量矩阵乘法。
- """
-
- dtype: DType
-
- def forward(self, *inputs):
- return TensorDef(inputs[0].shape[:-1] + [inputs[1].shape[-1]], inputs[0].dtype)
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- return (
- reduce(mul, inputs[0].shape) + reduce(mul, inputs[1].shape)
- ) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity_grads(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- activity_grads = TensorDef(
- inputs[0].shape[:-1] + [inputs[1].shape[-1]], inputs[0].dtype
- )
- return reduce(mul, activity_grads.shape) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- return batch_gemm_flops(inputs[0], inputs[1])
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- return 2 * batch_gemm_flops(inputs[0], inputs[1])
-
-
-"""
- https://automata88.medium.com/how-to-implement-the-softmax-derivative-independently-from-any-loss-function-ae6d44363a9d
- https://pytorch.org/docs/stable/generated/torch.nn.Softmax.html#torch.nn.Softmax
-"""
-
-
-@dataclass
-class SoftmaxDef(LayerDef):
- dtype: DType
- dims: int = (
- 0 # A dimension along which Softmax will be computed (so every slice along dim will sum to 1).
- )
-
- def forward(self, *inputs):
- return TensorDef(inputs[0].shape, inputs[0].dtype)
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return reduce(mul, inputs[0].shape) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity_grads(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return reduce(mul, inputs[0].shape) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return 5 * reduce(mul, inputs[0].shape)
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return 8 * reduce(mul, inputs[0].shape)
-
-
-"""
- https://pytorch.org/docs/stable/generated/torch.nn.SiLU.html#torch.nn.SiLU
-"""
-
-
-@dataclass
-class SiLUDef(LayerDef):
- """SiLU (Swish) activation: x * sigmoid(x)
-
- FLOPs计算:
- - Forward: sigmoid(x) 需要 exp + div + 1 = 3 ops,乘法 1 op,共 4 ops per element
- - Backward: d/dx[x * sigmoid(x)] = sigmoid(x) + x * sigmoid(x) * (1 - sigmoid(x))
- 需要约 6 ops per element
- """
-
- dtype: DType
- inplace: bool = False
-
- def forward(self, *inputs):
- return TensorDef(inputs[0].shape, inputs[0].dtype)
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- return reduce(mul, inputs[0].shape) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_input(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return inputs[0].nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_output(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- return self(*inputs).nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity_grads(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return reduce(mul, inputs[0].shape) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None) -> int:
- """SiLU forward: x * sigmoid(x), ~4 ops per element"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return 4 * reduce(mul, inputs[0].shape)
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None) -> int:
- """SiLU backward: ~6 ops per element for gradient computation"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return 6 * reduce(mul, inputs[0].shape)
-
-
-"""
- https://pytorch.org/docs/stable/generated/torch.add.html#torch.add
- https://explained.ai/matrix-calculus/#sec:1.4.2
-"""
-
-
-@dataclass
-class AddDef(LayerDef):
- """Element-wise addition: torch.add
-
- 无权重层,执行两个张量的逐元素加法。
- """
-
- alpha: int = 1
-
- def forward(self, *inputs):
- return TensorDef(inputs[0].shape, inputs[0].dtype)
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- return (
- reduce(mul, inputs[0].shape) + reduce(mul, inputs[1].shape)
- ) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_input(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- return inputs[0].nbytes + inputs[1].nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_output(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return self.forward(*inputs).nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return reduce(mul, inputs[0].shape)
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None) -> int:
- """加法反向传播: dy/dx = 1, 梯度直接传递,无计算"""
- if inputs is None:
- inputs = []
- return 0
-
-
-@dataclass
-class MulDef(LayerDef):
- """Element-wise multiplication: torch.mul
-
- 无权重层,执行两个张量的逐元素乘法。
- 用于 SwiGLU 中的 silu(gate) * up 操作。
- """
-
- def forward(self, *inputs):
- return TensorDef(inputs[0].shape, inputs[0].dtype)
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- return (
- reduce(mul, inputs[0].shape) + reduce(mul, inputs[1].shape)
- ) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_input(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- return inputs[0].nbytes + inputs[1].nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_output(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return self.forward(*inputs).nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None) -> int:
- """逐元素乘法: N 次乘法操作"""
- if inputs is None:
- inputs = []
- if not inputs:
- return 0
- return reduce(mul, inputs[0].shape)
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None) -> int:
- """乘法反向传播: d(x*y)/dx = y, d(x*y)/dy = x
-
- 需要计算两个梯度,各需要 N 次乘法
- """
- if inputs is None:
- inputs = []
- if len(inputs) < 2:
- return 0
- return 2 * reduce(mul, inputs[0].shape)
-
-
-@dataclass
-class SequenceParallelAdd(AddDef):
- """Sequence Parallel Add: 序列并行下的加法操作
-
- 在序列并行模式下,序列维度被分割到多个设备上。
- """
-
- tensor_model_parallel_size: int = 1
-
- def forward(self, *inputs):
- seq_len = inputs[0].shape[-2] / self.tensor_model_parallel_size
- output = TensorDef(
- inputs[0].shape[:-2] + [seq_len, inputs[0].shape[-1]], inputs[0].dtype
- )
- return output
-
- def _get_partitioned_inputs(self, inputs):
- """获取序列并行分区后的输入"""
- if len(inputs) < 2:
- return None, None
- seq_len = inputs[0].shape[-2] / self.tensor_model_parallel_size
- input0 = TensorDef(
- inputs[0].shape[:-2] + [seq_len, inputs[0].shape[-1]], inputs[0].dtype
- )
- input1 = TensorDef(
- inputs[1].shape[:-2] + [seq_len, inputs[1].shape[-1]], inputs[1].dtype
- )
- return input0, input1
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_activity(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- input0, input1 = self._get_partitioned_inputs(inputs)
- if input0 is None:
- return 0
- return (reduce(mul, input0.shape) + reduce(mul, input1.shape)) * self.dsize
-
- @property
- @hp.param("blueprinting.layerdef")
- def nbytes_input(self, inputs: List[TensorDef] = None):
- if inputs is None:
- inputs = []
- input0, input1 = self._get_partitioned_inputs(inputs)
- if input0 is None:
- return 0
- return input0.nbytes + input1.nbytes
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_fw(self, inputs: List[TensorDef] = None) -> int:
- if inputs is None:
- inputs = []
- input0, _ = self._get_partitioned_inputs(inputs)
- if input0 is None:
- return 0
- return reduce(mul, input0.shape)
-
- @property
- @hp.param("blueprinting.layerdef")
- def flops_bw(self, inputs: List[TensorDef] = None) -> int:
- """序列并行加法反向传播: dy/dx = 1, 梯度直接传递,无计算"""
- if inputs is None:
- inputs = []
- return 0
diff --git a/src/blueprinting/st/__init__.py b/src/blueprinting/st/__init__.py
deleted file mode 100755
index 2f6b65e..0000000
--- a/src/blueprinting/st/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .blocks.attn import attention_block
-from .blocks.container import block
-from .blocks.ffn import ffn_block
diff --git a/src/blueprinting/st/blocks/__init__.py b/src/blueprinting/st/blocks/__init__.py
deleted file mode 100755
index e69de29..0000000
diff --git a/src/blueprinting/st/blocks/attn.py b/src/blueprinting/st/blocks/attn.py
deleted file mode 100755
index 8e3e8ba..0000000
--- a/src/blueprinting/st/blocks/attn.py
+++ /dev/null
@@ -1,149 +0,0 @@
-import hyperparameter as hp
-import streamlit as st
-from streamlit_extras.row import row
-from sympy import Symbol
-
-from blueprinting.nn import (
- AddDef,
- BatchMatmulDef,
- ColumnParallelLinear,
- RMSNormDef,
- RowParallelLinear,
- SequenceParallelAdd,
- SequenceParallelRMSNorm,
- SoftmaxDef,
- TensorDef,
-)
-from blueprinting.st.blocks.container import block
-from blueprinting.st.blocks.formatter import DefaultFormatter, auto_symbol
-from blueprinting.types.base import DType
-from blueprinting.util import pick
-
-
-@hp.param("model")
-def attention_block():
- """Attention 模块计算
-
- 参考llama2中的Attention模块
- """
- # 读取hyperparameter的参数配置, 创建 数值:符号 对
- dtype = DType(hp.scope.exe.datatype | "float16")
- _seqlen, SEQLEN = hp.scope.model.seq_size | 4096, Symbol("seqlen")
- _bsize, BSIZE = hp.scope.exe.microbatch_size | 0, Symbol("bsize")
- _hidden, HIDDEN = hp.scope.model.hidden | 4096, Symbol("hidden")
- _attnheads, ATTNHEADS = hp.scope.model.attn_heads | 32, Symbol("attnheads")
- _attnsize, ATTNSIZE = (
- hp.scope.model.attn_size | 128,
- Symbol("attnsize"),
- ) # hidden // attn_heads
- _tpsize, TPSIZE = hp.scope.exe.tensor_par | 1, Symbol("tpsize")
-
- tensor_par_comm_type = (
- hp.scope.exe.tensor_par_comm_type | "ar"
- ) # "ar" 表示 all reduce, "rs_ag" 表示 reduce scatter and all gather
- sequence_par = hp.scope.exe.sequence_par | "true"
-
- # 创建层级容器
- with block("attn_block", 4):
- st.markdown("#### Attention Block")
-
- with block("attn_res_block", 2):
- st.markdown("#### Residual Connection")
- res_layer = pick(
- sequence_par,
- SequenceParallelAdd(dtype, tensor_model_parallel_size=TPSIZE),
- AddDef(dtype),
- )
- res_out = res_layer(
- TensorDef([BSIZE, SEQLEN, HIDDEN], dtype=dtype),
- TensorDef([BSIZE, SEQLEN, HIDDEN], dtype=dtype),
- )
- res_out | DefaultFormatter.with_subs(auto_symbol())
-
- with block("attn_linear_block", 2):
- st.markdown("#### Output Projection [Linear]")
- linear_layer = RowParallelLinear(
- dtype, HIDDEN, HIDDEN, False, TPSIZE, tensor_par_comm_type
- )
- linear_out = linear_layer(TensorDef([BSIZE, SEQLEN, HIDDEN], dtype=dtype))
- linear_out | DefaultFormatter.with_subs(auto_symbol())
-
- with block("attn_mha_block", 2):
- st.markdown("#### Multi-Head Attention")
- # TP 切分后,每个设备上的 heads 数量
- ATTNHEADS_PER_TP = ATTNHEADS / TPSIZE
-
- with block("attn_batch_matmul_block_1", 2):
- st.markdown("#### Score @ V [BatchMatmul]")
- batchmatmul_layer = BatchMatmulDef(dtype)
- # score: [B, heads/TP, S, S], V: [B, heads/TP, S, size]
- # output: [B, heads/TP, S, size]
- batchmatmul_out = batchmatmul_layer(
- TensorDef([BSIZE, ATTNHEADS_PER_TP, SEQLEN, SEQLEN], dtype=dtype),
- TensorDef([BSIZE, ATTNHEADS_PER_TP, SEQLEN, ATTNSIZE], dtype=dtype),
- )
- batchmatmul_out | DefaultFormatter.with_subs(auto_symbol())
-
- with block("attn_softmax_block", 2):
- st.markdown("#### QK Score [Softmax]")
- softmax_layer = SoftmaxDef(dtype)
- softmax_out = softmax_layer(
- TensorDef([BSIZE, ATTNHEADS_PER_TP, SEQLEN, SEQLEN], dtype=dtype)
- )
- softmax_out | DefaultFormatter.with_subs(auto_symbol())
-
- with block("attn_batch_matmul_block_2", 2):
- st.markdown("#### Q @ K^T [BatchMatmul]")
- batchmatmul_layer = BatchMatmulDef(dtype)
- # Q: [B, heads/TP, S, size], K^T: [B, heads/TP, size, S]
- # output: [B, heads/TP, S, S]
- batchmatmul_out = batchmatmul_layer(
- TensorDef([BSIZE, ATTNHEADS_PER_TP, SEQLEN, ATTNSIZE], dtype=dtype),
- TensorDef([BSIZE, ATTNHEADS_PER_TP, ATTNSIZE, SEQLEN], dtype=dtype),
- )
- batchmatmul_out | DefaultFormatter.with_subs(auto_symbol())
-
- r = row(3)
-
- with r.container(), block("attn_query_block", 2):
- st.markdown("#### Query Projection [Linear]")
-
- q_layer = ColumnParallelLinear(
- dtype, HIDDEN, HIDDEN, False, TPSIZE, tensor_par_comm_type
- )
-
- q_proj = q_layer(TensorDef(shape=[BSIZE, SEQLEN, HIDDEN], dtype=dtype))
-
- q_proj | DefaultFormatter.with_subs(auto_symbol())
-
- with r.container(), block("attn_key_block", 2):
- st.markdown("#### Key Projection [Linear]")
- k_layer = ColumnParallelLinear(
- dtype, HIDDEN, HIDDEN, False, TPSIZE, tensor_par_comm_type
- )
-
- k_proj = k_layer(TensorDef(shape=[BSIZE, SEQLEN, HIDDEN], dtype=dtype))
-
- k_proj | DefaultFormatter.with_subs(auto_symbol())
-
- with r.container(), block("attn_value_block", 2):
- st.markdown("#### Value Projection [Linear]")
- v_layer = ColumnParallelLinear(
- dtype, HIDDEN, HIDDEN, False, TPSIZE, tensor_par_comm_type
- )
-
- v_proj = v_layer(TensorDef(shape=[BSIZE, SEQLEN, HIDDEN], dtype=dtype))
-
- v_proj | DefaultFormatter.with_subs(auto_symbol())
-
- with block("attn_rms_block", 2):
- st.markdown("#### PreNorm [RMSNorm]")
- rms_layer = pick(
- sequence_par,
- SequenceParallelRMSNorm(
- dtype, HIDDEN, tensor_model_parallel_size=TPSIZE
- ),
- RMSNormDef(dtype, HIDDEN),
- )
- rms_out = rms_layer(TensorDef([BSIZE, SEQLEN, HIDDEN], dtype=dtype))
- rms_out | DefaultFormatter.with_subs(auto_symbol())
diff --git a/src/blueprinting/st/blocks/container.py b/src/blueprinting/st/blocks/container.py
deleted file mode 100755
index 1f2f818..0000000
--- a/src/blueprinting/st/blocks/container.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from streamlit_extras.stylable_container import stylable_container
-
-
-def block(key: str, border: int):
- return stylable_container(
- key,
- css_styles=f"""
- {{
- border: {border}px solid rgba(49, 51, 63, 0.2);
- border-radius: 0.5rem;
- padding: calc(1em - 1px)
- }}
- """,
- )
diff --git a/src/blueprinting/st/blocks/ffn.py b/src/blueprinting/st/blocks/ffn.py
deleted file mode 100755
index 944e5d6..0000000
--- a/src/blueprinting/st/blocks/ffn.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import hyperparameter as hp
-import streamlit as st
-from streamlit_extras.row import row
-from sympy import Symbol
-
-from blueprinting.nn import (
- AddDef,
- ColumnParallelLinear,
- MulDef,
- RMSNormDef,
- RowParallelLinear,
- SequenceParallelAdd,
- SequenceParallelRMSNorm,
- SiLUDef,
- TensorDef,
-)
-from blueprinting.st.blocks.container import block
-from blueprinting.st.blocks.formatter import DefaultFormatter, auto_symbol
-from blueprinting.types.base import DType
-from blueprinting.util import pick
-
-
-@hp.param("model")
-def ffn_block(hidden=1024, feedforward=1024):
- """FFN 模块计算
-
- 使用sympy的符号化计算,方便追踪计算过程:
- - 通过 `hp.param` 将全局超参中的`model.hidden`与`model.feedforward`映射给`hidden`和`feedforward`
- - 创建参数的(数值、符号)对,比如(hidden、HIDDEN)
- - 运算过程使用符号进行计算,但输出时同时显示数值与符号
- """
-
- # 读取hyperparameter配置,并创建 数值<->符号 对
- dtype = DType(hp.scope.exe.datatype | "float16")
- seqlen = hp.scope.model.seq_size | 64
- _bsize, BSIZE = hp.scope.exe.microbatch_size | 0, Symbol("bsize")
- seqlen, SEQLEN = seqlen, Symbol("seqlen")
- hidden, HIDDEN = hidden, Symbol("hidden")
- feedforward, FEEDFORWARD = feedforward, Symbol("feedforward")
- _tpsize, TPSIZE = hp.scope.exe.tensor_par | 1, Symbol("tpsize")
-
- tensor_par_comm_type = (
- hp.scope.exe.tensor_par_comm_type | "ar"
- ) # "ar" 表示 all reduce, "rs_ag" 表示 reduce scatter and all gather
- sequence_par = hp.scope.exe.sequence_par | False
-
- # 创建层级容器
- with block("ffn_block", 4):
- st.markdown("#### Feed Forward Block")
-
- with block("ffn_res_block", 2):
- st.markdown("#### Residual Connection")
- res_layer = pick(
- sequence_par,
- SequenceParallelAdd(dtype, tensor_model_parallel_size=TPSIZE),
- AddDef(dtype),
- )
- res_out = res_layer(
- TensorDef([BSIZE, SEQLEN, HIDDEN], dtype=dtype),
- TensorDef([BSIZE, SEQLEN, HIDDEN], dtype=dtype),
- )
- res_out | DefaultFormatter.with_subs(auto_symbol())
-
- with block("ffn_pro_block", 2):
- st.markdown("##### Down Projection [Linear]")
-
- # 创建输出层的定义
- output_layer = RowParallelLinear(
- dtype, FEEDFORWARD, HIDDEN, False, TPSIZE, tensor_par_comm_type
- )
-
- # 进行计算
- output_proj = output_layer(
- TensorDef(shape=[BSIZE, SEQLEN, FEEDFORWARD], dtype=dtype)
- )
-
- # 渲染结果
- output_proj | DefaultFormatter.with_subs(auto_symbol())
-
- with block("ffn_SwiGLU_mul", 2):
- st.markdown("##### SwiGLU Element-wise Mul: silu(gate) * up")
- mul_layer = MulDef(dtype)
- mul_out = mul_layer(
- TensorDef([BSIZE, SEQLEN, FEEDFORWARD], dtype=dtype), # silu(gate)
- TensorDef([BSIZE, SEQLEN, FEEDFORWARD], dtype=dtype), # up
- )
- mul_out | DefaultFormatter.with_subs(auto_symbol())
-
- with block("ffn_SwiGLU", 2):
- st.markdown("##### SiLU Activation on Gate")
- silu_layer = SiLUDef(dtype)
- silu_out = silu_layer(TensorDef([BSIZE, SEQLEN, FEEDFORWARD], dtype=dtype))
- silu_out | DefaultFormatter.with_subs(auto_symbol())
-
- r = row(2)
-
- with r.container(), block("ffn_input_block", 2):
- st.markdown("##### Up Projection [Linear]")
- input_layer = ColumnParallelLinear(
- dtype, HIDDEN, FEEDFORWARD, False, TPSIZE, tensor_par_comm_type
- )
- input_proj = input_layer(
- TensorDef(shape=[BSIZE, SEQLEN, HIDDEN], dtype=dtype)
- )
- input_proj | DefaultFormatter.with_subs(auto_symbol())
-
- with r.container(), block("ffn_gate_block", 2):
- st.markdown("##### Gate Projection [Linear]")
- gate_layer = ColumnParallelLinear(
- dtype, HIDDEN, FEEDFORWARD, False, TPSIZE, tensor_par_comm_type
- )
- gate_proj = gate_layer(
- TensorDef(shape=[BSIZE, SEQLEN, HIDDEN], dtype=dtype)
- )
- gate_proj | DefaultFormatter.with_subs(auto_symbol())
-
- with block("ffn_rms_block", 2):
- st.markdown("##### Pre-Norm [RMSNorm]")
- rms_layer = pick(
- sequence_par,
- SequenceParallelRMSNorm(
- dtype, HIDDEN, tensor_model_parallel_size=TPSIZE
- ),
- RMSNormDef(dtype, HIDDEN),
- )
- rms_out = rms_layer(TensorDef([BSIZE, SEQLEN, HIDDEN], dtype=dtype))
- rms_out | DefaultFormatter.with_subs(auto_symbol())
diff --git a/src/blueprinting/st/blocks/formatter.py b/src/blueprinting/st/blocks/formatter.py
deleted file mode 100755
index 705aade..0000000
--- a/src/blueprinting/st/blocks/formatter.py
+++ /dev/null
@@ -1,186 +0,0 @@
-import functools
-import html
-import sys
-
-import hyperparameter as hp
-import streamlit as st
-from htbuilder import span, styles
-from htbuilder.units import unit
-from sympy import Expr, Symbol, latex
-
-from blueprinting.nn.base import TensorDef
-from blueprinting.ui import NullFormatter, ReadableFLOPs, ReadableMem, ReadableTime
-
-PALETTE = [
- "#ff4b4b",
- "#ffa421",
- "#ffe312",
- "#21c354",
- "#00d4b1",
- "#00c0f2",
- "#1c83e1",
- "#803df5",
- "#808495",
-]
-
-OPACITIES = [
- "33",
- "66",
-]
-
-LABEL_SPACING = unit.rem(0.5)
-LABEL_FONT_SIZE = unit.rem(0.75)
-LABEL_OPACITY = 0.5
-LABEL_SPACING = unit.rem(0.5)
-PADDING = (unit.rem(0.25), unit.rem(0.5))
-BORDER_RADIUS = unit.rem(0.5)
-
-LABEL_DESC = {
- "wgt": "weight mem",
- "act": "activity mem",
- "grad": "grad mem",
- "hbm": "hbm throughput",
- "c2c": "card to card comminications",
-}
-
-
-def _render_html(element, help=None):
- return st.markdown(str(span(element)), unsafe_allow_html=True, help=help)
-
-
-def labeled_text(label, body, tooltip="-", background=None, color=None, **style):
- long_label = LABEL_DESC.get(label, label)
- color_style = {}
-
- if color:
- color_style["color"] = color
-
- if background:
- background_color = background
- else:
- label_sum = sum(ord(c) for c in label)
- background_color = PALETTE[label_sum % len(PALETTE)]
- background_opacity = OPACITIES[label_sum % len(OPACITIES)]
- background = background_color + background_opacity
-
- separator = (
- span(
- style=styles(
- border_bottom="1px solid",
- opacity=0.1,
- margin_bottom=LABEL_SPACING,
- align_self="stretch",
- )
- ),
- )
-
- label_element = (
- span(
- style=styles(
- margin_bottom=LABEL_SPACING,
- font_size=LABEL_FONT_SIZE,
- opacity=LABEL_OPACITY,
- ),
- title=long_label if long_label else None,
- )(
- html.escape(label),
- ),
- separator,
- )
- return _render_html(
- span(
- style=styles(
- display="inline-flex",
- flex_direction="column",
- align_items="center",
- background=background,
- border_radius=BORDER_RADIUS,
- padding=PADDING,
- overflow="hidden",
- line_height=0.75,
- **color_style,
- **style,
- )
- )(
- label_element,
- html.escape(body),
- ),
- help=tooltip,
- )
-
-
-def auto_symbol():
- frame = sys._getframe(0).f_back
- subs = {}
- for k, v in frame.f_locals.items():
- if isinstance(v, Symbol):
- subs[v.name] = frame.f_locals[k.lower()]
- return subs
-
-
-def AnnotatedFormatter(li: TensorDef, subs=None):
- if subs is None:
- subs = {}
- human_readable = hp.scope.blueprinting.formatter.human_readable | True
- mem_fmt = ReadableMem if human_readable else NullFormatter
- flops_fmt = ReadableFLOPs if human_readable else NullFormatter
- time_fmt = ReadableTime if human_readable else NullFormatter
-
- def format(fmt, val, subs):
- if isinstance(val, (Symbol, Expr)):
- expr = latex(val, mul_symbol=" \\times ")
- value = val.subs(subs)
- if value.is_number:
- value = float(value)
- return f"{fmt%value}", f"${expr}$"
- return (f"{fmt%val}",)
-
- fw, bw, shapes, timming = st.tabs(["forward", "backward", "shapes", "timming"])
- with fw:
- columns = [
- (labeled_text, "wgt", *format(mem_fmt, li.nbytes_weight, subs)),
- (labeled_text, "act", *format(mem_fmt, li.nbytes_activity, subs)),
- (labeled_text, "flops", *format(flops_fmt, li.flops_fw, subs)),
- (labeled_text, "hbm", *format(mem_fmt, li.memory_fw, subs)),
- # (labeled_text, "c2c", *format(flops_fmt, li.c2c_fw, subs)),
- ]
- cs = st.columns(len(columns))
- for col, args in zip(cs, columns):
- with col:
- args[0](*args[1:])
- with bw:
- columns = [
- (labeled_text, "wgt", *format(mem_fmt, li.nbytes_weight, subs)),
- (labeled_text, "act", *format(mem_fmt, li.nbytes_activity, subs)),
- (labeled_text, "grad", *format(mem_fmt, li.nbytes_weight_grads, subs)),
- (labeled_text, "flops", *format(flops_fmt, li.flops_bw, subs)),
- (labeled_text, "hbm", *format(mem_fmt, li.memory_bw, subs)),
- # (labeled_text, "c2c", *format(flops_fmt, li.c2c_bw, subs)),
- ]
- cs = st.columns(len(columns))
- for col, args in zip(cs, columns):
- with col:
- args[0](*args[1:])
-
- with shapes:
- ins = tuple(x.subs(subs) for x in li.inputs)
- out = li.subs(subs)
- st.markdown(f"${ins} \\rightarrow {out}$")
-
- with timming, hp.scope(**{"blueprinting.symbolic.subs": subs.items()}):
- columns = [
- (labeled_text, "fw", *format(time_fmt, li.time_fw, subs)),
- (labeled_text, "bw", *format(time_fmt, li.time_bw, subs)),
- # (labeled_text, "c2c", *format(flops_fmt, "#TODO", subs)),
- ]
- cs = st.columns(len(columns))
- for col, args in zip(cs, columns):
- with col:
- args[0](*args[1:])
-
-
-AnnotatedFormatter.with_subs = lambda subs: functools.partial(
- AnnotatedFormatter, subs=subs
-)
-
-DefaultFormatter = AnnotatedFormatter
diff --git a/src/blueprinting/types/__init__.py b/src/blueprinting/types/__init__.py
deleted file mode 100755
index 6d757cb..0000000
--- a/src/blueprinting/types/__init__.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""Types module for blueprinting."""
-
-from .counters import CommCounter
-from .dtypes import DType, bf16, bfloat16, float16, float32, fp8, fp16, fp32, fp64
-from .execution import Execution
-from .model import Model, ModelComm, ModelFlops, ModelParams
-from .operation import Calculation, Operation
-from .system import Memory, Network, Processor, System
-from .tensor import TensorDef, TensorLike
-
-__all__ = [
- "Execution",
- "Model",
- "ModelParams",
- "ModelFlops",
- "ModelComm",
- "CommCounter",
- "DType",
- "fp8",
- "fp16",
- "bf16",
- "fp32",
- "fp64",
- "float16",
- "bfloat16",
- "float32",
- "TensorDef",
- "TensorLike",
- "Operation",
- "Calculation",
- "System",
- "Memory",
- "Processor",
- "Network",
-]
diff --git a/src/blueprinting/types/base.py b/src/blueprinting/types/base.py
deleted file mode 100755
index edb7d6f..0000000
--- a/src/blueprinting/types/base.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""Base types for blueprinting.
-
-This module re-exports types from the new split modules for backward compatibility.
-"""
-
-# Re-export from dtypes
-from .dtypes import DType, bf16, bfloat16, float16, float32, fp8, fp16, fp32, fp64
-
-# Re-export from operation
-from .operation import Calculation, Operation
-
-# Re-export from tensor
-from .tensor import TensorDef, TensorLike
-
-__all__ = [
- "DType",
- "fp8",
- "fp16",
- "bf16",
- "fp32",
- "fp64",
- "float16",
- "bfloat16",
- "float32",
- "TensorDef",
- "TensorLike",
- "Operation",
- "Calculation",
-]
diff --git a/src/blueprinting/types/counters.py b/src/blueprinting/types/counters.py
deleted file mode 100755
index 769d8a5..0000000
--- a/src/blueprinting/types/counters.py
+++ /dev/null
@@ -1,75 +0,0 @@
-import dataclasses
-
-
-@dataclasses.dataclass
-class CommCounter:
- """Counter for communicate operations
-
- Examples
- --------
- >>> cnt = CommCounter()
- >>> cnt.n_all_reduce
- 0
-
- >>> cnt.n_all_gather += 1
- >>> cnt.n_all_gather
- 1
- """
-
- n_all_reduce: int = 0
- n_all_gather: int = 0
- n_reduce_scatter: int = 0
- n_send_recv: int = 0
-
- all_reduce: int = 0
- all_gather: int = 0
- reduce_scatter: int = 0
- send_recv: int = 0
-
- def to_dict(self):
- return {k: v for k, v in dataclasses.asdict(self).items() if v != 0}
-
- def __str__(self):
- nops = " / ".join(
- str(getattr(self, f.name))
- for f in dataclasses.fields(CommCounter)
- if f.name.startswith("n_")
- )
- ncomm = " / ".join(
- str(getattr(self, f.name))
- for f in dataclasses.fields(CommCounter)
- if not f.name.startswith("n_")
- )
- return f"{nops}\n{ncomm}"
-
- def __add__(self, other):
- """
- Examples
- --------
- >>> cnt1 = CommCounter(n_all_reduce=1, n_all_gather=2)
- >>> cnt2 = CommCounter(n_all_reduce=3, n_all_gather=4)
- >>> cnt1 + cnt2
- CommCounter(n_all_reduce=4, n_all_gather=6)
-
- >>> import pandas as pd
- >>> pd.DataFrame([cnt1, cnt2]) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
- n_all_reduce n_all_gather
- 0 1 2
- 1 3 4
- >>> pd.DataFrame({"a": [cnt1, cnt2]})["a"].sum()
- CommCounter(n_all_reduce=4, n_all_gather=6)
- """
- cnt = CommCounter()
- for f in dataclasses.fields(CommCounter):
- name = f.name
- setattr(cnt, name, getattr(self, name) + getattr(other, name))
- return cnt
-
- def __mul__(self, scale):
- cnt = CommCounter()
- for f in dataclasses.fields(CommCounter):
- name = f.name
- setattr(cnt, name, getattr(self, name) * scale)
- return cnt
-
- __rmul__ = __mul__
diff --git a/src/blueprinting/types/dtypes.py b/src/blueprinting/types/dtypes.py
deleted file mode 100644
index a8dec4b..0000000
--- a/src/blueprinting/types/dtypes.py
+++ /dev/null
@@ -1,70 +0,0 @@
-"""Data types for blueprinting."""
-
-from dataclasses import dataclass
-from enum import Enum
-
-__all__ = [
- "DType",
- "FloatPoint",
- "fp8",
- "fp16",
- "bf16",
- "fp32",
- "fp64",
- "float16",
- "bfloat16",
- "float32",
-]
-
-
-@dataclass
-class FloatPoint:
- kind: str
- size: int
-
-
-class DType(FloatPoint, Enum):
- """data types
-
- Examples
- --------
- >>> DType.fp8
- fp8
- >>> DType.fp16 == DType.float16
- True
- >>> DType("fp16")
- fp16
- >>> DType("float16")
- fp16
- >>> list(DType)
- [fp8, fp16, bf16, fp32, fp64]
- """
-
- fp8 = "fp8", 1
- fp16 = "fp16", 2
- bf16 = "bf16", 2
- fp32 = "fp32", 4
- fp64 = "fp64", 8
- float16 = "fp16", 2
- bfloat16 = "bf16", 2
- float32 = "fp32", 4
-
- def __repr__(self):
- return self.name
-
- @classmethod
- def _missing_(cls, value):
- for x in cls:
- if x.name == value:
- return x
- if value == "float16":
- return cls.fp16
- if value == "bfloat16":
- return cls.bf16
- if value == "float32":
- return cls.fp32
- return None
-
-
-fp8, fp16, bf16, fp32, fp64 = list(DType)
-float16, bfloat16, float32 = DType.float16, DType.bfloat16, DType.float32
diff --git a/src/blueprinting/types/execution.py b/src/blueprinting/types/execution.py
deleted file mode 100755
index 1915985..0000000
--- a/src/blueprinting/types/execution.py
+++ /dev/null
@@ -1,47 +0,0 @@
-class Execution:
- def __init__(self, cfg) -> None:
- self.num_procs = cfg.num_procs | 0
- self.tensor_par = cfg.tensor_par | 0
- self.pipeline_par = cfg.pipeline_par | 0
- self.data_par = cfg.data_par | 0
- self.tensor_par_net = cfg.tensor_par_net | 0
- self.pipeline_par_net = cfg.pipeline_par_net | 0
- self.data_par_net = cfg.data_par_net | 0
- self.global_batch_size = cfg.batch_size | 0
-
- self.microbatch_size = cfg.microbatch_size | 0
-
- self.datatype = cfg.datatype | 0
- self.fused_activation = cfg.fused_activation | 0
- self.attention_type = (
- cfg.attention_type | "multihead"
- ) # ['multihead', 'multiquery']
- self.activation_recompute = (
- cfg.activation_recompute | "none"
- ) # ['full', 'attn_only', 'none']
-
- self.pipeline_interleaving = cfg.pipeline_interleaving | 0
- self.optimizer_sharding = cfg.optimizer_sharding | 0
- self.tensor_par_comm_type = (
- cfg.tensor_par_comm_type | "ar"
- ) # ['ar', 'p2p_rs_ag', 'rs_ag']
-
- self.tensor_par_overlap = (
- cfg.tensor_par_overlap | "ring"
- ) # ['none', 'ring', 'pipe']
- self.seq_par_ag_redo = cfg.seq_par_ag_redo | 0
- self.data_par_overlap = cfg.data_par_overlap | 0
-
- self.weight_offload = cfg.weight_offload | 0
- self.activations_offload = cfg.activations_offload | 0
- self.optimizer_offload = cfg.optimizer_offload | 0
- self.training = cfg.training | 0
-
- self._local_batch_size = self.global_batch_size // self.data_par
- self._sequence_par = self.tensor_par_comm_type == "rs_ag"
- self._pipeline_par_rs_ag = self.tensor_par_comm_type in ["p2p_rs_ag", "rs_ag"]
- self.in_network_reduction = False
- self._num_microbatches = self._local_batch_size // self.microbatch_size
-
- def get_json(self):
- return self.__dict__
diff --git a/src/blueprinting/types/model/__init__.py b/src/blueprinting/types/model/__init__.py
deleted file mode 100644
index 3ebd133..0000000
--- a/src/blueprinting/types/model/__init__.py
+++ /dev/null
@@ -1,172 +0,0 @@
-"""Model module for blueprinting."""
-
-import hyperparameter as hp
-
-from .comm import ModelComm
-from .flops import ModelFlops
-from .params import ModelParams
-
-__all__ = ["Model", "ModelParams", "ModelFlops", "ModelComm"]
-
-
-class Model:
- """LLM Model configuration.
-
- Uses composition pattern instead of dynamic inheritance.
-
- Examples
- --------
- >>> import hyperparameter as hp
- >>> with hp.scope(hidden=512, feedforward=2048, attn_heads=8, attn_size=64, seq_size=1024, num_blocks=4):
- ... m = Model()
- ... m.hidden
- 512
- >>> m.nparam_total
- 39348224
- """
-
- @staticmethod
- def from_cfg(cfg=None) -> "Model":
- """Create Model from configuration.
-
- Args:
- cfg: hp.scope object or None to use current scope
-
- Returns:
- Model instance
- """
- return Model(cfg)
-
- def __init__(self, cfg=None) -> None:
- # Read configuration from hp.scope at runtime
- if cfg is None:
- cfg = hp.scope()
- self.hidden = cfg.hidden | 0
- self.feedforward = cfg.feedforward | 0
- self.seq_size = cfg.seq_size | 0
- self.attn_heads = cfg.attn_heads | 0
- self.attn_size = cfg.attn_size | 0
- self.num_blocks = cfg.num_blocks | 0
-
- # Composition pattern - delegate to specialized classes
- self._params = ModelParams(self)
- self._flops = ModelFlops(self)
- self._comm = ModelComm(self)
-
- def num_parameters(self) -> int:
- """Calculate total number of parameters.
-
- https://cs.stanford.edu/~matei/papers/2021/sc_megatron_lm.pdf
- Equation 2
- """
- p = 2 * self.hidden * self.feedforward # MLP weights
- p += 4 * self.hidden * self.attn_heads * self.attn_size # Attn weights
- p += self.hidden + self.feedforward # biases MLP
- p += 3 * self.attn_heads * self.attn_size + self.hidden # biases Attn
- p += 2 * 2 * self.hidden # layer norm
- p *= self.num_blocks # per each block
- p += (51200 + self.seq_size) * self.hidden # embeddings
- return p
-
- # Delegate parameter calculations to ModelParams
- @property
- def nparam_mlp(self) -> int:
- """MLP parameters."""
- return self._params.mlp()
-
- @property
- def nparam_attn(self) -> int:
- """Attention parameters."""
- return self._params.attn()
-
- @property
- def nparam_norm(self) -> int:
- """Normalization layer parameters."""
- return self._params.norm()
-
- @property
- def nparam_embedding(self) -> int:
- """Embedding parameters."""
- return self._params.embedding()
-
- @property
- def nparam_total(self) -> int:
- """Total number of parameters in the model."""
- return self._params.total()
-
- # Delegate FLOPs calculations to ModelFlops
- @property
- def flops_mlp(self) -> int:
- """MLP FLOPs."""
- return self._flops.mlp()
-
- @property
- def flops_attn(self) -> int:
- """Attention FLOPs."""
- return self._flops.attn()
-
- @property
- def flops_norm(self) -> int:
- """Normalization layer FLOPs."""
- return self._flops.norm()
-
- @property
- def flops_embedding(self) -> int:
- """Embedding FLOPs."""
- return self._flops.embedding()
-
- @property
- def flops_total(self) -> int:
- """Total FLOPs."""
- return self._flops.total()
-
- # Delegate communication calculations to ModelComm
- @property
- def comm_embedding_fw(self):
- """Forward embedding communication."""
- return self._comm.embedding_fw()
-
- @property
- def comm_embedding_bw(self):
- """Backward embedding communication."""
- return self._comm.embedding_bw()
-
- @property
- def comm_attn_fw(self):
- """Forward attention communication."""
- return self._comm.attn_fw()
-
- @property
- def comm_attn_bw(self):
- """Backward attention communication."""
- return self._comm.attn_bw()
-
- @property
- def comm_mlp_fw(self):
- """Forward MLP communication."""
- return self._comm.mlp_fw()
-
- @property
- def comm_mlp_bw(self):
- """Backward MLP communication."""
- return self._comm.mlp_bw()
-
- @property
- def comm_total_fw(self):
- """Total forward communication."""
- return self._comm.total_fw()
-
- @property
- def comm_total_bw(self):
- """Total backward communication."""
- return self._comm.total_bw()
-
- @property
- def comm_pipeline_parallel_fw(self):
- """Forward pipeline parallel communication."""
- return self._comm.pipeline_parallel_fw()
-
- @property
- def comm_pipeline_parallel_bw(self):
- """Backward pipeline parallel communication."""
- return self._comm.pipeline_parallel_bw()
diff --git a/src/blueprinting/types/model/comm.py b/src/blueprinting/types/model/comm.py
deleted file mode 100644
index 2155388..0000000
--- a/src/blueprinting/types/model/comm.py
+++ /dev/null
@@ -1,184 +0,0 @@
-"""Model communication calculations for blueprinting."""
-
-import hyperparameter as hp
-
-from ..counters import CommCounter
-
-__all__ = ["ModelComm"]
-
-
-class ModelComm:
- """Model communication calculations."""
-
- def __init__(self, model: "Model"):
- self._model = model
-
- @hp.param("exe")
- def embedding_fw(
- self, global_batch_size=0, microbatch_size=0, data_par=0, tensor_par=0
- ) -> CommCounter:
- """Forward embedding communication."""
- m = self._model
- cnt = CommCounter()
- if tensor_par > 0:
- cnt.n_all_reduce += 1
- cnt.all_reduce += microbatch_size * m.seq_size * m.hidden
-
- cnt.n_all_reduce += 1
- cnt.all_reduce += microbatch_size * m.seq_size
-
- cnt.n_all_reduce += 1
- cnt.all_reduce += tensor_par
- return cnt
-
- @hp.param("exe")
- def embedding_bw(
- self, global_batch_size=0, microbatch_size=0, data_par=0, tensor_par=0
- ) -> CommCounter:
- """Backward embedding communication."""
- return CommCounter()
-
- @hp.param("exe")
- def attn_fw(
- self,
- global_batch_size=0,
- microbatch_size=0,
- data_par=0,
- tensor_par=0,
- sequence_par=True,
- ) -> CommCounter:
- """Forward attention communication."""
- m = self._model
- cnt = CommCounter()
- if tensor_par > 0 and not sequence_par:
- cnt.n_all_reduce += 1
- cnt.all_reduce += microbatch_size * m.seq_size * m.hidden
- elif tensor_par > 0 and sequence_par:
- cnt.n_all_gather += 1
- cnt.all_gather += microbatch_size * m.seq_size * m.hidden
-
- cnt.n_reduce_scatter += 1
- cnt.reduce_scatter += microbatch_size * m.seq_size * m.hidden
- return cnt
-
- @hp.param("exe")
- def attn_bw(
- self,
- global_batch_size=0,
- microbatch_size=0,
- data_par=0,
- tensor_par=0,
- sequence_par=True,
- ) -> CommCounter:
- """Backward attention communication."""
- m = self._model
- cnt = CommCounter()
- if tensor_par > 0 and not sequence_par:
- cnt.n_all_reduce += 1
- cnt.all_reduce += microbatch_size * m.seq_size * m.hidden
- elif tensor_par > 0 and sequence_par:
- cnt.n_all_gather += 1
- cnt.all_gather += microbatch_size * m.seq_size * m.hidden
-
- cnt.n_reduce_scatter += 1
- cnt.reduce_scatter += microbatch_size * m.seq_size * m.hidden
- return cnt
-
- @hp.param("exe")
- def mlp_fw(
- self,
- microbatch_size=0,
- zero_optimizer=0,
- data_par=0,
- tensor_par=0,
- sequence_par=True,
- ) -> CommCounter:
- """Forward MLP communication."""
- m = self._model
- cnt = CommCounter()
- if tensor_par > 0 and not sequence_par:
- # forward: attn -> all_reduce -> mlp
- cnt.n_all_reduce += 1
- cnt.all_reduce += microbatch_size * m.seq_size * m.hidden
- elif tensor_par > 0 and sequence_par:
- cnt.n_all_gather += 1
- cnt.all_gather += microbatch_size * m.seq_size * m.hidden
-
- cnt.n_reduce_scatter += 1
- cnt.reduce_scatter += microbatch_size * m.seq_size * m.hidden
-
- if data_par > 0 and zero_optimizer == 3:
- cnt.n_all_gather += 1
- cnt.all_gather += m.hidden * m.hidden
- return cnt
-
- @hp.param("exe")
- def mlp_bw(
- self,
- microbatch_size=0,
- zero_optimizer=0,
- data_par=0,
- tensor_par=0,
- sequence_par=True,
- ) -> CommCounter:
- """Backward MLP communication."""
- m = self._model
- cnt = CommCounter()
- if tensor_par > 0 and not sequence_par:
- # backward: attn <- all_reduce <- mlp
- cnt.n_all_reduce += 1
- cnt.all_reduce += microbatch_size * m.seq_size * m.hidden
- elif tensor_par > 0 and sequence_par:
- cnt.n_all_gather += 1
- cnt.all_gather += microbatch_size * m.seq_size * m.hidden
-
- cnt.n_reduce_scatter += 1
- cnt.reduce_scatter += microbatch_size * m.seq_size * m.hidden
-
- if data_par > 0 and zero_optimizer == 0:
- # 梯度累加,梯度下发
- cnt.n_all_reduce += 1
- cnt.all_reduce += m.hidden * m.hidden
- elif data_par > 0 and zero_optimizer == 1:
- # 梯度累加,梯度下发
- cnt.n_all_reduce += 1
- cnt.all_reduce += m.hidden * m.hidden
-
- # 参数聚合
- cnt.n_all_gather += 1
- cnt.all_gather += m.hidden * m.hidden
- elif data_par > 0 and zero_optimizer == 2:
- # 梯度聚合
- cnt.n_reduce_scatter += 1
- cnt.reduce_scatter += m.hidden * m.hidden
-
- # 参数聚合
- cnt.n_all_gather += 1
- cnt.all_gather += m.hidden * m.hidden
- elif data_par > 0 and zero_optimizer == 3:
- # 参数聚合
- cnt.n_all_gather += 1
- cnt.all_gather += m.hidden * m.hidden
-
- # 梯度聚合
- cnt.n_reduce_scatter += 1
- cnt.reduce_scatter += m.hidden * m.hidden
- return cnt
-
- @hp.param("exe")
- def pipeline_parallel_fw(self, microbatch_size=0, pipeline_par=0) -> CommCounter:
- """Forward pipeline parallel communication."""
- return CommCounter()
-
- @hp.param("exe")
- def pipeline_parallel_bw(self, microbatch_size=0, pipeline_par=0) -> CommCounter:
- """Backward pipeline parallel communication."""
- return CommCounter()
-
- def total_fw(self) -> CommCounter:
- """Total forward communication."""
- return self.embedding_fw() + self.attn_fw() + self.mlp_fw()
-
- def total_bw(self) -> CommCounter:
- """Total backward communication."""
- return self.embedding_bw() + self.attn_bw() + self.mlp_bw()
diff --git a/src/blueprinting/types/model/flops.py b/src/blueprinting/types/model/flops.py
deleted file mode 100644
index 34f23cd..0000000
--- a/src/blueprinting/types/model/flops.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""Model FLOPs calculations for blueprinting."""
-
-import hyperparameter as hp
-
-__all__ = ["ModelFlops"]
-
-
-class ModelFlops:
- """Model FLOPs calculations."""
-
- def __init__(self, model: "Model"):
- self._model = model
-
- @hp.param("model")
- def mlp(self) -> int:
- """MLP FLOPs."""
- return 2 * self._model._params.mlp()
-
- @hp.param("model")
- def attn(self, use_qkv_bias=True, use_attn_bias=True) -> int:
- """Attention FLOPs."""
- m = self._model
- qkv_proj_input = m.hidden
- qkv_proj_output = m.attn_size
-
- attn_proj_input = m.attn_size * m.attn_heads
- attn_proj_output = m.hidden
-
- qkv_flops = 2 * 3 * m.attn_heads * qkv_proj_input * qkv_proj_output
- proj_flops = 2 * attn_proj_input * attn_proj_output
- mask_flops = 2 * m.seq_size * m.attn_heads * m.attn_size
-
- return qkv_flops + proj_flops + mask_flops
-
- @hp.param("model")
- def norm(self, type_norm="LN") -> int:
- """Normalization layer FLOPs."""
- m = self._model
- if type_norm == "LN":
- return 2 * m.hidden
- if type_norm == "RMS":
- return m.hidden
- return 0
-
- @hp.param("model")
- def embedding(self) -> int:
- """Embedding FLOPs."""
- return 0
-
- @hp.param("model")
- def total(self) -> int:
- """Total FLOPs."""
- return 0
diff --git a/src/blueprinting/types/model/params.py b/src/blueprinting/types/model/params.py
deleted file mode 100644
index 39ae7a3..0000000
--- a/src/blueprinting/types/model/params.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""Model parameter calculations for blueprinting."""
-
-import hyperparameter as hp
-
-__all__ = ["ModelParams"]
-
-
-class ModelParams:
- """Model parameter calculations.
-
- Examples
- --------
- >>> from blueprinting.types.model import Model
- >>> import hyperparameter as hp
- >>> with hp.scope(hidden=512, feedforward=2048, attn_heads=8, attn_size=64, seq_size=1024, num_blocks=4):
- ... m = Model()
- ... m.nparam_mlp
- 2099712
- """
-
- def __init__(self, model: "Model"):
- self._model = model
-
- @hp.param("model")
- def mlp(self, use_mlp_bias=True) -> int:
- """MLP parameters.
-
- Parameters
- ----------
- use_mlp_bias : bool, optional
- Whether to include bias parameters, by default True
- """
- m = self._model
- bias = m.hidden + m.feedforward if use_mlp_bias else 0
- return 2 * m.feedforward * m.hidden + bias
-
- @hp.param("model")
- def attn(self, use_qkv_bias=True, use_attn_bias=True) -> int:
- """Attention parameters.
-
- Parameters
- ----------
- use_qkv_bias : bool, optional
- Whether to include bias in QKV projection, by default True
- use_attn_bias : bool, optional
- Whether to include bias in attention projection, by default True
- """
- m = self._model
- qkv_proj_input = m.hidden
- qkv_proj_output = m.attn_size
- qkv_proj_bias = m.attn_size if use_qkv_bias else 0
-
- attn_proj_input = m.attn_size * m.attn_heads
- attn_proj_output = m.hidden
- attn_proj_bias = m.hidden if use_attn_bias else 0
-
- return 3 * m.attn_heads * (qkv_proj_input * qkv_proj_output + qkv_proj_bias) + (
- attn_proj_input * attn_proj_output + attn_proj_bias
- )
-
- @hp.param("model")
- def norm(self, type_norm="LN") -> int:
- """Normalization layer parameters."""
- m = self._model
- if type_norm == "LN":
- input_norm = 2 * m.hidden
- attn_norm = 2 * m.hidden
- elif type_norm == "RMS":
- input_norm = 0
- attn_norm = 0
- else:
- input_norm = 0
- attn_norm = 0
- return input_norm + attn_norm
-
- @hp.param("model")
- def embedding(self, vocab_size=51200, type_posemb="learned") -> int:
- """Embedding parameters."""
- m = self._model
- if type_posemb == "learned":
- posemb = m.seq_size * m.hidden
- elif type_posemb == "rope":
- posemb = 0
- else:
- posemb = 0
- return vocab_size * m.hidden + posemb
-
- def total(self) -> int:
- """Total number of parameters in the model."""
- m = self._model
- return (
- m.num_blocks * (self.mlp() + self.attn() + self.norm()) + self.embedding()
- )
diff --git a/src/blueprinting/types/operation.py b/src/blueprinting/types/operation.py
deleted file mode 100644
index 1f8a809..0000000
--- a/src/blueprinting/types/operation.py
+++ /dev/null
@@ -1,130 +0,0 @@
-"""Operation and Calculation definitions for blueprinting."""
-
-from dataclasses import dataclass, field
-from typing import List, Optional, Tuple
-
-import hyperparameter as hp
-
-from .tensor import TensorDef
-
-__all__ = [
- "Operation",
- "Calculation",
-]
-
-
-@dataclass
-class Operation:
- """Base operation class."""
-
- inputs: Tuple[TensorDef, ...] = ()
- output: TensorDef = field(default_factory=lambda: TensorDef())
-
- @property
- def shape(self):
- return self.output.shape
-
- @property
- def dtype(self):
- return self.output.dtype
-
- @property
- def nelems(self):
- return self.output.nelems
-
- @property
- def nbytes(self):
- return self.output.nbytes
-
- @property
- def dsize(self):
- return self.output.dsize
-
-
-@dataclass
-class Calculation(Operation):
- """Calculation node in computation graph."""
-
- function: Optional["LayerDef"] = None
-
- @property
- def nelems(self) -> int:
- return self.output.nelems
-
- @property
- def nbytes(self):
- return self.output.nelems * self.output.dsize
-
- @property
- def nbytes_weight(self) -> int:
- return self.function.nbytes_weight
-
- @property
- def nbytes_weight_grads(self) -> int:
- return self.function.nbytes_weight_grads
-
- @property
- def nbytes_activity(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.nbytes_activity
-
- @property
- def flops_fw(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.flops_fw
-
- @property
- def flops_bw(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.flops_bw
-
- @property
- def memrw_fw(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.memrw_fw
-
- @property
- def memrw_bw(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.memrw_bw
-
- @property
- def time_fw(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.time_fw
-
- @property
- def time_bw(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.time_bw
-
- @property
- def memory_fw(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.memory_fw
-
- @property
- def memory_bw(self) -> int:
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.memory_bw
-
- @property
- @hp.param("blueprinting.layerdef")
- def placement_weight(self, inputs: List[TensorDef] = None) -> Tuple[TensorDef, ...]:
- if inputs is None:
- inputs = []
- with hp.scope(**{"blueprinting.layerdef.inputs": self.inputs}):
- return self.function.placement_weight
-
- def subs(self, subs=None):
- """Substitute symbolic values in the output tensor."""
- if subs is None:
- subs = {}
- return self.output.subs(subs)
-
- def belike(self):
- """Return a TensorDef with the same shape and dtype as output."""
- return self.output.belike()
-
- def __or__(self, other):
- other(self)
diff --git a/src/blueprinting/types/system/__init__.py b/src/blueprinting/types/system/__init__.py
deleted file mode 100755
index e278426..0000000
--- a/src/blueprinting/types/system/__init__.py
+++ /dev/null
@@ -1,102 +0,0 @@
-"""System module for blueprinting."""
-
-from typing import List
-
-import hyperparameter as hp
-
-from .memory import Memory
-from .network import Network
-from .processor import Processor
-
-__all__ = ["System", "Memory", "Processor", "Network"]
-
-
-class System:
- """Hardware system configuration.
-
- Examples
- --------
- >>> import json
- >>> import hyperparameter as hp
- >>> sys_cfg = json.load(open("system.json"))
- >>> with hp.scope(sys=sys_cfg):
- ... system = System()
- ... system.proc_mode
- 'roofline'
- """
-
- TypeSizes = {
- "float8": 1,
- "float16": 2,
- "float32": 4,
- "bfloat16": 2,
- }
-
- @staticmethod
- def supported_datatypes() -> List[str]:
- """Return list of supported data types."""
- return list(System.TypeSizes.keys())
-
- def __init__(self, cfg=None) -> None:
- if cfg is None:
- cfg = hp.scope()
-
- self.matrix = Processor("matrix")
- self.vector = Processor("vector")
- self.datatype = None
-
- self.mem1 = Memory("mem1")
- self.mem2 = Memory("mem2")
-
- self.proc_mode = cfg.processing_mode | "roofline"
- assert self.proc_mode in ["roofline", "no_overlap"]
-
- # Networks is a list of dicts, create each with its own scope
- networks_cfg = cfg.networks | []
- self.networks = []
- for net_dict in networks_cfg:
- with hp.scope(**net_dict):
- self.networks.append(Network())
-
- @property
- def num_networks(self) -> int:
- """Number of networks."""
- return len(self.networks)
-
- def get_network(self, tier: int) -> Network:
- """Get network by tier."""
- assert tier < len(self.networks), f"Bad network tier ID: {tier}"
- return self.networks[tier]
-
- def set_datatype(self, datatype: str) -> None:
- """Set the data type for processing."""
- assert datatype in System.TypeSizes, f"Unsupported data type: {datatype}"
- self.datatype = datatype
-
- def get_matrix_throughput(self, flops: int) -> float:
- """Get matrix processor throughput."""
- return self.matrix.throughput(self.datatype, flops)
-
- def get_vector_throughput(self, flops: int) -> float:
- """Get vector processor throughput."""
- return self.vector.throughput(self.datatype, flops)
-
- def get_mem1_throughput(self, size: int) -> float:
- """Get primary memory throughput."""
- return self.mem1.throughput(size)
-
- def get_mem2_throughput(self, size: int) -> float:
- """Get secondary memory throughput."""
- return self.mem2.throughput(size)
-
- def compute_offload_time(self, size: int) -> float:
- """Compute time to offload data to secondary memory."""
- return size / self.mem2.throughput(size)
-
- def get_processing_time(self, flops_time: float, mem_time: float) -> float:
- """Get processing time based on processing mode."""
- if self.proc_mode == "roofline":
- return max(flops_time, mem_time)
- elif self.proc_mode == "no_overlap":
- return flops_time + mem_time
- return flops_time + mem_time
diff --git a/src/blueprinting/types/system/memory.py b/src/blueprinting/types/system/memory.py
deleted file mode 100755
index 803f8c5..0000000
--- a/src/blueprinting/types/system/memory.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""Memory configuration for blueprinting."""
-
-import hyperparameter as hp
-
-__all__ = ["Memory"]
-
-
-class Memory:
- """Memory configuration.
-
- Examples
- --------
- >>> import hyperparameter as hp
- >>> with hp.scope(**{"mem1.GiB": 80, "mem1.GBps": 2000, "mem1.MB_efficiency": [(100, 0.9)]}):
- ... mem = Memory("mem1")
- ... mem.capacity
- 85899345920
- """
-
- def __init__(self, prefix: str) -> None:
- """Initialize Memory.
-
- Args:
- prefix: The configuration prefix (e.g., "mem1", "mem2")
- """
- cfg = getattr(hp.scope(), prefix)
-
- self.capacity = (cfg.GiB | 0) * 1024**3
- self.bandwidth = (cfg.GBps | 0) * 1e9
- self._efficiency = []
- for mbytes, eff in cfg.MB_efficiency | []:
- bytes = mbytes * 1e6
- assert 0 < eff <= 1.0
- self._efficiency.append((bytes, eff))
-
- def efficiency(self, op_bytes: int) -> float:
- """Get efficiency for given operation size."""
- for bytes, eff in self._efficiency:
- if op_bytes >= bytes:
- return eff
- raise ValueError(f"OP bytes {op_bytes} wasn't covered by efficiency curve")
-
- def throughput(self, op_bytes: int) -> float:
- """Get throughput for given operation size."""
- return self.bandwidth * self.efficiency(op_bytes)
diff --git a/src/blueprinting/types/system/network.py b/src/blueprinting/types/system/network.py
deleted file mode 100644
index 09f22fe..0000000
--- a/src/blueprinting/types/system/network.py
+++ /dev/null
@@ -1,120 +0,0 @@
-"""Network configuration for blueprinting."""
-
-from dataclasses import dataclass
-
-import hyperparameter as hp
-
-__all__ = ["Network", "NetworkOp"]
-
-
-@dataclass
-class NetworkOp:
- """Network operation configuration."""
-
- scalar: float
- offset: float
-
-
-class Network:
- """Network configuration.
-
- Examples
- --------
- >>> import hyperparameter as hp
- >>> with hp.scope(bandwidth=100, efficiency=0.9, size=8, latency=1e-6):
- ... net = Network()
- ... net.bandwidth
- 100000000000.0
- """
-
- COLLECTIVES = {"reduce_scatter", "all_gather", "all_reduce"}
- NET_OPS = {"p2p", "reduce_scatter", "all_gather", "all_reduce"}
-
- def __init__(self) -> None:
- """Initialize Network from current hp.scope."""
- cfg = hp.scope()
-
- self._bandwidth = (cfg.bandwidth | 0) * 1e9 # GB/s -> B/s
- self._efficiency = cfg.efficiency | 1.0
- assert 0 < self._efficiency <= 1.0
- self._size = cfg.size | 0
- self._latency = cfg.latency | 0
- self._must_be_filled = cfg.must_be_filled | False
- self._processor_usage = cfg.processor_usage | 0.0
- assert 0.0 <= self._processor_usage < 1.0
-
- # Get ops configuration - extract op names from flat keys
- self._ops = {}
- all_keys = cfg.keys()
- ops_keys = [k for k in all_keys if k.startswith("ops.")]
- op_names = {k.split(".")[1] for k in ops_keys if len(k.split(".")) > 1}
-
- for op in op_names:
- op_cfg = getattr(cfg.ops, op)
- # op_cfg is a list [scalar, offset]
- params = op_cfg | [1.0, None]
- if isinstance(params, (list, tuple)):
- scalar, offset = params
- else:
- scalar = 1.0
- offset = None
-
- assert op in self.NET_OPS, f"Invalid network op: {op}"
- assert scalar > 0.0, f"Invalid network scalar for {op}: {scalar}"
-
- if op in self.COLLECTIVES:
- assert offset is not None, f"Must give offset for {op}"
- self._ops[op] = NetworkOp(scalar, offset)
- else:
- assert offset is None, f"Can't give offset for {op}"
- self._ops[op] = NetworkOp(scalar, 0)
-
- @property
- def bandwidth(self) -> float:
- """Bandwidth in bytes/second."""
- return self._bandwidth
-
- @property
- def size(self) -> int:
- """Network size (number of nodes)."""
- return self._size
-
- @property
- def must_be_filled(self) -> bool:
- """Whether network must be fully utilized."""
- return self._must_be_filled
-
- @property
- def processor_usage(self) -> float:
- """Processor usage during network operations."""
- return self._processor_usage
-
- def time(self, op: str, op_size: int, comm_size: int) -> float:
- """Compute time for a network operation.
-
- Args:
- op: Operation name (p2p, reduce_scatter, all_gather, all_reduce)
- op_size: Operation size in bytes
- comm_size: Number of participants in operation
-
- Returns:
- Time needed for operation in seconds
- """
- if op not in self.COLLECTIVES:
- assert comm_size == 2
- else:
- assert comm_size >= 2
- assert op in self.NET_OPS
- assert op_size >= 0
-
- net_op = self._ops[op]
-
- # Scale the op_size by the scalar
- scaled_size = op_size * net_op.scalar
-
- # Scale the op_size by the op offset
- chunk_size = scaled_size / comm_size
- total_size = scaled_size + chunk_size * net_op.offset
-
- # Calculate time based on raw bandwidth, efficiency, and latency
- return self._latency + total_size / (self._bandwidth * self._efficiency)
diff --git a/src/blueprinting/types/system/processor.py b/src/blueprinting/types/system/processor.py
deleted file mode 100644
index 8eed088..0000000
--- a/src/blueprinting/types/system/processor.py
+++ /dev/null
@@ -1,73 +0,0 @@
-"""Processor configuration for blueprinting."""
-
-import hyperparameter as hp
-
-__all__ = ["Processor"]
-
-
-class Processor:
- """Processor configuration with support for multiple data types.
-
- Examples
- --------
- >>> import hyperparameter as hp
- >>> with hp.scope(**{"matrix.float16.tflops": 312, "matrix.float16.gflops_efficiency": [(100, 0.9)]}):
- ... proc = Processor("matrix")
- ... proc.flops("float16")
- 312000000000000.0
- """
-
- def __init__(self, prefix: str) -> None:
- """Initialize Processor.
-
- Args:
- prefix: The configuration prefix (e.g., "matrix", "vector")
- """
- cfg = hp.scope()
- self._datatypes = {}
-
- # Get keys that match this prefix (e.g., "matrix.float16.tflops")
- all_keys = cfg.keys()
- prefix_dot = f"{prefix}."
-
- # Extract unique data types under this prefix
- # "matrix.float16.tflops" -> "float16"
- dtypes = set()
- for k in all_keys:
- if k.startswith(prefix_dot):
- parts = k[len(prefix_dot) :].split(".")
- if parts:
- dtypes.add(parts[0])
-
- # Build configuration for each data type
- for dtype in dtypes:
- dt_cfg = getattr(getattr(cfg, prefix), dtype)
- tflops = dt_cfg.tflops | 0
- gflops_eff = dt_cfg.gflops_efficiency | []
-
- self._datatypes[dtype] = {"flops": tflops * 1e12, "efficiency": []}
- last = None
- for gflops, eff in gflops_eff:
- flops = gflops * 1e9
- assert 0 < eff <= 1.0
- if last:
- assert flops < last
- last = flops
- self._datatypes[dtype]["efficiency"].append((flops, eff))
-
- def flops(self, datatype: str) -> float:
- """Get peak flops for given data type."""
- return self._datatypes[datatype]["flops"]
-
- def efficiency(self, datatype: str, op_flops: int) -> float:
- """Get efficiency for given data type and operation flops."""
- for flops, eff in self._datatypes[datatype]["efficiency"]:
- if op_flops >= flops:
- return eff
- raise ValueError(f"{op_flops} wasn't covered in {datatype} efficiency curve")
-
- def throughput(self, datatype: str, op_flops: int) -> float:
- """Get throughput for given data type and operation flops."""
- if datatype not in self._datatypes:
- raise ValueError(f"Unsupported data type: {datatype}")
- return self.flops(datatype) * self.efficiency(datatype, op_flops)
diff --git a/src/blueprinting/types/tensor.py b/src/blueprinting/types/tensor.py
deleted file mode 100644
index 19ef30a..0000000
--- a/src/blueprinting/types/tensor.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""Tensor definitions for blueprinting."""
-
-from dataclasses import dataclass, field
-from functools import reduce
-from operator import mul
-from typing import Tuple, Union
-
-from sympy import Expr
-
-from .dtypes import DType, fp32
-
-__all__ = [
- "TensorLike",
- "TensorDef",
-]
-
-
-class TensorLike:
- """Tensor protocol mixin."""
-
- @property
- def nelems(self):
- return reduce(mul, self.shape, 1)
-
- @property
- def nbytes(self):
- return self.dsize * self.nelems
-
- @property
- def dsize(self):
- return self.dtype.size
-
- @property
- def T(self):
- return TensorDef(list(reversed(self.shape)), self.dtype)
-
- def belike(self):
- return TensorDef(list(self.shape), self.dtype)
-
- def __repr__(self) -> str:
- return f"{self.dtype}{self.shape}"
-
- def subs(self, subs=None) -> "TensorDef":
- if subs is None:
- subs = {}
- return TensorDef(
- [int(x.subs(subs)) if isinstance(x, Expr) else x for x in self.shape],
- dtype=self.dtype,
- )
-
-
-@dataclass
-class TensorDef(TensorLike):
- """TensorDef
-
- Examples
- --------
- >>> TensorDef([1, 2, 3], dtype=DType.fp32)
- fp32[1, 2, 3]
- """
-
- shape: Tuple[Union[int, Expr], ...] = ()
- dtype: DType = field(default_factory=lambda: fp32)
-
- def __repr__(self) -> str:
- return f"{repr(self.dtype)}{self.shape}"
diff --git a/src/blueprinting/ui.py b/src/blueprinting/ui.py
deleted file mode 100755
index b178f68..0000000
--- a/src/blueprinting/ui.py
+++ /dev/null
@@ -1,666 +0,0 @@
-"""UI utilities for blueprinting - Streamlit 共享组件和工具函数"""
-
-import glob
-import hmac
-import json
-from dataclasses import dataclass
-from typing import Callable, Union
-
-import hyperparameter as hp
-import pandas as pd
-import streamlit as st
-from streamlit_extras.row import row as st_row
-
-from . import io
-
-# ============================================================================
-# 页面配置和初始化
-# ============================================================================
-
-
-def check_password():
- """Returns `True` if the user had a correct password."""
-
- def login_form():
- """Form with widgets to collect user information"""
- with st.form("Credentials"):
- st.text_input("Username", key="username")
- st.text_input("Password", type="password", key="password")
- st.form_submit_button("Log in", on_click=password_entered)
-
- def password_entered():
- """Checks whether a password entered by the user is correct."""
- if st.session_state["username"] in st.secrets[
- "passwords"
- ] and hmac.compare_digest(
- st.session_state["password"],
- st.secrets.passwords[st.session_state["username"]],
- ):
- st.session_state["password_correct"] = True
- del st.session_state["password"] # Don't store the username or password.
- del st.session_state["username"]
- else:
- st.session_state["password_correct"] = False
-
- # Return True if the username + password is validated.
- if st.session_state.get("password_correct", False):
- return True
-
- # Show inputs for username + password.
- login_form()
- if "password_correct" in st.session_state:
- st.error("😕 User not known or password incorrect")
- return False
-
-
-def ele(name, *args, scope=None, param=None, **kwargs):
- return (name, scope, param, args, kwargs)
-
-
-def row(*args):
- columns = st.columns([1 for _ in args], vertical_alignment="bottom")
- for element, container in zip(args, columns):
- with container:
- value = getattr(st, element[0])(*element[3], **element[4])
- if element[1] is not None and element[2] is not None:
- setattr(element[1], element[2], value)
-
-
-class Predefined:
- @property
- def models(self):
- return glob.glob("data/models/*.json")
-
- @property
- def model_names(self):
- return [x.replace("data/models/", "") for x in self.models]
-
- @property
- def systems(self):
- return glob.glob("data/systems/*.json")
-
- @property
- def system_names(self):
- return [x.replace("data/systems/", "") for x in self.systems]
-
- @property
- def executions(self):
- return glob.glob("data/examples/*.json")
-
- @property
- def execution_names(self):
- return [x.replace("data/examples/", "") for x in self.executions]
-
-
-predefined = Predefined()
-
-
-def value2widget(key, value, prefix=None):
- if prefix:
- key = f"{prefix}.{key}"
- if isinstance(value, bool):
- return st.checkbox(key, value=value)
- if isinstance(value, (int, float)):
- return st.number_input(key, value=value)
- if isinstance(value, str):
- return st.text_input(key, value=value)
- if isinstance(value, dict):
- return {k: value2widget(k, v, prefix=key) for k, v in value.items()}
- if isinstance(value, list):
- return [value2widget(str(k), v, prefix=key) for k, v in enumerate(value)]
- return value
-
-
-def make_json_conf(conf):
- if isinstance(conf, str):
- conf = io.read_json_file(conf)
- return {k: value2widget(k, v) for k, v in conf.items()}
-
-
-LEVELS = {"block": "块"}
-STAGES = {
- "fw": "前向",
- "agrad": "激活梯度",
- "wgrad": "权重梯度",
- "optim": "优化器",
- "re": "重计算",
-}
-CATEGORIES = {
- "flops": "flops",
- "flops_time": "flops时间",
- "mem_accessed": "显存占用",
- "mem_time": "访存时间",
- "time": "耗时",
-}
-
-PARSER = {
- f"{l}_{s}_{c}": (lv, sv, cv)
- for l, lv in LEVELS.items()
- for s, sv in STAGES.items()
- for c, cv in CATEGORIES.items()
-}
-
-PARSER.update(
- {
- "fw_time": ("整体", "前向", "耗时"),
- "bw_time": ("整体", "反向", "耗时"),
- "optim_step_time": ("整体", "优化器", "耗时"),
- "recompute_time": ("整体", "重计算", "耗时"),
- "recomm_exposed_time": ("整体", "重计算通信", "耗时"),
- "bubble_time": ("整体", "空泡", "耗时"),
- "total_time": ("整体", "整体", "耗时"),
- "compute_efficiency": ("整体", "计算效率", "-"),
- "system_efficiency": ("整体", "系统效率", "-"),
- "total_efficiency": ("整体", "总效率", "-"),
- "sample_rate": ("整体", "采样率", "-"),
- }
-)
-
-
-@dataclass
-class result:
- level: str
- stage: str
- category: str
- name: str
- value: Union[str, int, float]
-
- @staticmethod
- def parse(key, val):
- l, s, c = PARSER.get(key, (None, None, None))
- return result(l, s, c, key, val)
-
-
-def human_format(num, round_to=1, use_tera=False):
- magnitude = 0
- while abs(num) >= 1000:
- magnitude += 1
- num = round(num / 1000.0, round_to)
- formatter = ["", "K", "M", "G", "T"] if use_tera else ["", "K", "M", "B", "G"]
- return "{:.{}f}{}".format(num, round_to, formatter[magnitude])
-
-
-def human_readable(num, round_to=1, formatter=None):
- if isinstance(num, (str,)):
- return num
- for factor, suffix in formatter.items():
- if num >= factor:
- return "{:.{}f}{}".format(num / factor, round_to, suffix)
- return "{:.{}f}".format(num, round_to)
-
-
-def human_readable_flops(num, round_to=1):
- if not isinstance(num, (int, float)):
- return num
- return human_readable(
- num,
- round_to,
- formatter={
- 1e15: "P",
- 1e12: "T",
- 1e6: "M",
- 1e3: "K",
- },
- )
-
-
-def human_readable_mem(num, round_to=1):
- return human_readable(
- num,
- round_to,
- formatter={
- 1e12: "T",
- 1e9: "G",
- 1e6: "M",
- 1e3: "K",
- },
- )
-
-
-def human_readable_num(num, round_to=1):
- if not isinstance(num, (int, float)):
- return num
- return human_readable(
- num,
- round_to,
- formatter={
- 1e12: "T",
- 1e9: "B",
- 1e6: "M",
- 1e3: "K",
- },
- )
-
-
-def human_readable_time(num, round_to=2):
- if num >= 1:
- return "{:.{}f} s".format(num, round_to)
- if num >= 1e-3:
- return "{:.{}f} ms".format(num * 1000, round_to)
- if num >= 1e-6:
- return "{:.{}f} ms".format(num * 1e6, round_to)
-
-
-def make_summary(stats):
- data = pd.DataFrame(result.parse(k, v) for k, v in stats.items())
- summary = pd.pivot_table(
- data, index=["level", "stage"], columns="category", values="value"
- )
- summary = summary.reindex(
- [
- "前向",
- "反向",
- "激活梯度",
- "权重梯度",
- "重计算",
- "重计算通信",
- "优化器",
- "空泡",
- "整体",
- "计算效率",
- "系统效率",
- "总效率",
- "采样率",
- ],
- level=1,
- )
- return summary.style.format(
- {"flops": human_readable_flops, "显存占用": human_readable_mem}
- )
-
-
-class HumanReadableFormatter:
- def __init__(self, func):
- self.func = func
-
- def __call__(self, *args, **kwargs):
- return self.func(*args, **kwargs)
-
- def __mod__(self, other):
- return self.func(other)
-
-
-ReadableFLOPs = HumanReadableFormatter(human_readable_flops)
-ReadableMem = HumanReadableFormatter(human_readable_mem)
-ReadableNum = HumanReadableFormatter(human_readable_num)
-ReadableTime = HumanReadableFormatter(human_readable_time)
-NullFormatter = HumanReadableFormatter(lambda x: x)
-
-
-def setup_sidebar():
- """设置公共侧边栏配置,返回 (app_json, sys_json, exe_json)"""
- st.sidebar.title("参数配置")
- with st.sidebar:
- tabm, tabs, tabe = st.tabs(["模型", "系统", "执行"])
-
- with tabm:
- app_json = st.selectbox("模型配置", predefined.model_names)
- app_json = make_json_conf(f"data/models/{app_json}")
- with tabs:
- sys_json = st.selectbox("系统配置", predefined.system_names)
- sys_json = make_json_conf(f"data/systems/{sys_json}")
- with tabe:
- exe_json = st.selectbox("执行参数", predefined.execution_names)
- exe_json = make_json_conf(f"data/examples/{exe_json}")
-
- return app_json, sys_json, exe_json
-
-
-def setup_page(title="LLM训练计算器", icon=":eyeglasses:", layout="wide"):
- """设置页面配置"""
- st.set_page_config(page_title=title, page_icon=icon, layout=layout)
-
-
-# ============================================================================
-# 共享 UI 组件 - 可复用的页面元素
-# ============================================================================
-
-
-def parallel_config_row(
- ps: hp.scope,
- show_gbs: bool = True,
- show_mbs: bool = True,
- show_seqlen: bool = True,
- use_slider: bool = False,
- mbs_param: str = "exe.microbatch_size",
-) -> None:
- """渲染并行配置行 (TP/PP/DP/GBS/MBS/SeqLen)
-
- Args:
- ps: hyperparameter scope 对象
- show_gbs: 是否显示 global batch size
- show_mbs: 是否显示 micro batch size
- show_seqlen: 是否显示 sequence length
- use_slider: 是否使用滑块(用于范围选择实验)
- mbs_param: microbatch size 的参数路径
- """
- if use_slider:
- tp_options = [1, 2, 4, 8, 16]
- pp_options = [1, 2, 4, 8, 16]
- dp_options = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
- row(
- ele(
- "select_slider",
- "TP",
- value=(1, 1),
- options=tp_options,
- scope=ps,
- param="exp.tp",
- ),
- ele(
- "select_slider",
- "PP",
- value=(1, 1),
- options=pp_options,
- scope=ps,
- param="exp.pp",
- ),
- ele(
- "select_slider",
- "DP",
- value=(1, 1),
- options=dp_options,
- scope=ps,
- param="exp.dp",
- ),
- ele("text", ""),
- (
- ele(
- "number_input",
- "gbs",
- value=ps.exe.batch_size | 1,
- scope=ps,
- param="exe.batch_size",
- )
- if show_gbs
- else ele("text", "")
- ),
- (
- ele(
- "number_input",
- "mbs",
- value=ps.exe.microbatch_size | 1,
- scope=ps,
- param=mbs_param,
- )
- if show_mbs
- else ele("text", "")
- ),
- ele("text", ""),
- (
- ele(
- "number_input",
- "seqlen",
- value=ps.model.seq_size | 1,
- scope=ps,
- param="model.seq_size",
- )
- if show_seqlen
- else ele("text", "")
- ),
- )
- else:
- dp_value = int(
- (ps.exe.num_procs | 1) / (ps.exe.tensor_par | 1) / (ps.exe.pipeline_par | 1)
- )
- row(
- ele(
- "number_input",
- "TP",
- value=ps.exe.tensor_par | 1,
- scope=ps,
- param="exe.tensor_par",
- ),
- ele(
- "number_input",
- "PP",
- value=ps.exe.pipeline_par | 1,
- scope=ps,
- param="exe.pipeline_par",
- ),
- ele("number_input", "DP", value=dp_value, scope=ps, param="exe.data_par"),
- ele("text", ""),
- (
- ele(
- "number_input",
- "gbs",
- value=ps.exe.batch_size | 1,
- scope=ps,
- param="exe.batch_size",
- )
- if show_gbs
- else ele("text", "")
- ),
- (
- ele(
- "number_input",
- "mbs",
- value=ps.exe.microbatch_size | 1,
- scope=ps,
- param=mbs_param,
- )
- if show_mbs
- else ele("text", "")
- ),
- ele("text", ""),
- (
- ele(
- "number_input",
- "seqlen",
- value=ps.model.seq_size | 1,
- scope=ps,
- param="model.seq_size",
- )
- if show_seqlen
- else ele("text", "")
- ),
- )
-
-
-def config_popover(ps: hp.scope, label: str = "配置") -> dict:
- """渲染配置弹窗
-
- Args:
- ps: hyperparameter scope 对象
- label: 弹窗按钮标签
-
- Returns:
- 包含配置选项的字典
- """
- config = {}
- with st.popover(label, use_container_width=True):
- config["use_humanreadable"] = st.checkbox("显示人类可读数字", True)
- config["use_raw_output"] = st.checkbox("显示原始输出", False)
- config["count_add"] = st.checkbox("FLOPS统计含加法")
- ps.blueprinting.flops.count_add = config["count_add"]
- return config
-
-
-def page_header_with_config(
- title: str,
- ps: hp.scope,
- use_slider: bool = False,
- mbs_param: str = "exe.microbatch_size",
-) -> dict:
- """渲染页面头部,包含并行配置和配置弹窗
-
- Args:
- title: 页面标题
- ps: hyperparameter scope 对象
- use_slider: 是否使用滑块模式
- mbs_param: microbatch size 参数路径
-
- Returns:
- 配置字典
- """
- st.header(title, divider=True)
- c1, c2 = st.columns([0.9, 0.1], vertical_alignment="bottom")
-
- with c1:
- parallel_config_row(ps, use_slider=use_slider, mbs_param=mbs_param)
- if not use_slider:
- ps.exe.num_proc = (
- (ps.exe.tensor_par | 1)
- * (ps.exe.pipeline_par | 1)
- * (ps.exe.data_par | 1)
- )
-
- with c2:
- config = config_popover(ps)
-
- return config
-
-
-def transformer_config_expander(ps: hp.scope) -> dict:
- """Transformer 模型参数配置展开区
-
- Args:
- ps: hyperparameter scope 对象
-
- Returns:
- 配置字典
- """
- config = {}
- with st.expander("transformer参数", expanded=True):
- r = st_row(3)
- bias_flags = r.container()
- emb_flags = r.container()
- norm_flags = r.container()
-
- config.update(
- {
- "model.use_attn_bias": bias_flags.checkbox("attention投影bias", True),
- "model.use_qkv_bias": bias_flags.checkbox("attention输入bias", True),
- "model.use_mlp_bias": bias_flags.checkbox("MLP使用bias", True),
- "model.type_posemb": emb_flags.selectbox(
- "位置编码", ["learned", "rope"], 0
- ),
- "model.vocab_size": emb_flags.number_input(
- "词表大小", ps.model.vocab_size | 51200
- ),
- "model.type_norm": norm_flags.selectbox("归一化层", ["LN", "RMS"], 0),
- }
- )
-
- return config
-
-
-def raw_output_section(stats: dict, summary_fn: Callable = None) -> None:
- """渲染原始输出区域
-
- Args:
- stats: 统计数据字典
- summary_fn: 摘要生成函数
- """
- with st.expander("模拟器日志", expanded=False):
- st.text(json.dumps(stats, indent=2))
-
- if summary_fn:
- with st.expander("模拟器输出", expanded=False):
- st.dataframe(summary_fn(stats), use_container_width=True)
-
-
-# ============================================================================
-# 布局组件 - 页面结构和视觉元素
-# ============================================================================
-
-
-def page_title(title: str, subtitle: str = None, icon: str = None):
- """渲染页面标题区域
-
- Args:
- title: 主标题
- subtitle: 副标题/描述
- icon: 图标 emoji
- """
- if icon:
- st.markdown(f"# {icon} {title}")
- else:
- st.markdown(f"# {title}")
-
- if subtitle:
- st.caption(subtitle)
-
- st.markdown("---")
-
-
-def section_header(title: str, description: str = None):
- """渲染章节标题
-
- Args:
- title: 章节标题
- description: 章节描述
- """
- st.markdown(f"### {title}")
- if description:
- st.caption(description)
-
-
-def info_card(title: str, content: str, icon: str = "ℹ️"):
- """渲染信息卡片
-
- Args:
- title: 卡片标题
- content: 卡片内容
- icon: 图标
- """
- st.markdown(
- f"""
-
-
- {icon} {title}
-
-
- {content}
-
-
- """,
- unsafe_allow_html=True,
- )
-
-
-def metric_card(label: str, value, delta=None, help_text: str = None):
- """渲染指标卡片
-
- Args:
- label: 指标标签
- value: 指标值
- delta: 变化值
- help_text: 帮助文本
- """
- st.metric(label=label, value=value, delta=delta, help=help_text)
-
-
-def metrics_row(*metrics):
- """渲染一行指标
-
- Args:
- metrics: [(label, value, delta?, help?), ...] 列表
- """
- cols = st.columns(len(metrics))
- for col, metric in zip(cols, metrics):
- with col:
- if len(metric) == 2:
- metric_card(metric[0], metric[1])
- elif len(metric) == 3:
- metric_card(metric[0], metric[1], metric[2])
- else:
- metric_card(metric[0], metric[1], metric[2], metric[3])
-
-
-def two_column_layout(left_ratio: float = 0.5):
- """创建两列布局
-
- Args:
- left_ratio: 左列占比
-
- Returns:
- (left_col, right_col) 元组
- """
- return st.columns([left_ratio, 1 - left_ratio])
diff --git a/src/blueprinting/util.py b/src/blueprinting/util.py
deleted file mode 100755
index 2520179..0000000
--- a/src/blueprinting/util.py
+++ /dev/null
@@ -1,4 +0,0 @@
-def pick(en, a, b):
- if en:
- return a
- return b
diff --git a/src/blueprinting/validation/legacy/__init__.py b/src/blueprinting/validation/legacy/__init__.py
deleted file mode 100644
index 85cf98b..0000000
--- a/src/blueprinting/validation/legacy/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Retained oracle-only reproductions that do not exercise canonical derivation."""
diff --git a/src/blueprinting/validation/legacy/seqsel_fig1.py b/src/blueprinting/validation/legacy/seqsel_fig1.py
deleted file mode 100644
index 77832ac..0000000
--- a/src/blueprinting/validation/legacy/seqsel_fig1.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""Legacy Calculon reproduction of SeqSel figure 1.
-
-This compatibility check does not exercise Blueprinting's canonical derivation
-path; the strict production gate lives in :mod:`blueprinting.validation.calculon`.
-"""
-
-import logging
-
-import hyperparameter as hp
-import pandas as pd
-
-# Calculon is used here for validation comparison only
-import blueprinting.io as io
-from blueprinting.types import Execution, Model
-from calculon.llm import Llm
-from calculon.llm import System as CalculonSystem
-
-kProfile = {
- "megatron-22B": {
- "none": {"par_opt": 45.5625, "act": 59.25},
- "seqsel": {"par_opt": 45.5625, "act": 9.5625},
- },
- "gpt3-175B": {
- "none": {"par_opt": 45.5625, "act": 66.84375},
- "seqsel": {"par_opt": 45.5625, "act": 12.3515625},
- },
- "turing-530B": {
- "none": {"par_opt": 31.640625, "act": 114.0234375},
- "seqsel": {"par_opt": 31.640625, "act": 23.076171875},
- },
- "megatron-1T": {
- "none": {"par_opt": 32.958984375, "act": 131.25},
- "seqsel": {"par_opt": 32.958984375, "act": 26.5625},
- },
-}
-
-mem_usage = pd.DataFrame.from_records(
- [
- {
- "model": model,
- "system": "a100_80e",
- "mode": mode,
- "w+opt mem(GiB)": kProfile[model][mode]["par_opt"],
- "act mem(GiB)": kProfile[model][mode]["act"],
- }
- for model in kProfile
- for mode in kProfile[model]
- ]
-)
-
-
-def seqsel_fig1(show=False):
- models = mem_usage["model"].unique()
- systems = mem_usage["system"].unique()
- modes = mem_usage["mode"].unique()
-
- records = []
-
- for m, s, e in [(m, s, e) for m in models for s in systems for e in modes]:
- app_json = f"data/models/{m}.json"
- sys_json = f"data/systems/{s}.json"
- exe_json = f"data/validation/seqsel/fig1/{m}_{e}.json"
-
- logger = logging.getLogger()
-
- app_json = io.read_json_file(app_json)
- sys_json = io.read_json_file(sys_json)
- exe_json = io.read_json_file(exe_json)
-
- with hp.scope(app=app_json, sys=sys_json, exe=exe_json) as ps:
- app = Model(ps.app)
- exe = Execution(ps.exe)
- # Use calculon's System for validation
- syst = CalculonSystem(sys_json)
-
- model = Llm(app, logger)
- model.compile(syst, exe)
- model.run(syst)
- stats = model.get_stats_json(False)
- act_par_opt = (stats["weight_space"] + stats["weight_grad_space"] + stats["optimizer_space"]) / (1024**3)
- act_act = stats["act_space"] / (1024**3)
- records += [
- {
- "model": m,
- "system": s,
- "mode": e,
- "w+opt mem(GiB)": act_par_opt,
- "act mem(GiB)": act_act,
- }
- ]
- df = pd.DataFrame.from_records(records)
- result = (
- mem_usage.set_index(["model", "system", "mode"])
- .join(
- df.set_index(["model", "system", "mode"]),
- on=["model", "system", "mode"],
- lsuffix="[act]",
- rsuffix="[pred]",
- )
- .reset_index()
- )
-
- result["w+opt mem/rtol"] = (result["w+opt mem(GiB)[act]"] - result["w+opt mem(GiB)[pred]"]).abs() / result[
- "w+opt mem(GiB)[act]"
- ]
- result["act mem/rtol"] = (result["act mem(GiB)[act]"] - result["act mem(GiB)[pred]"]).abs() / result[
- "act mem(GiB)[act]"
- ]
-
- return (
- result.style.format(
- {
- "w+opt mem/rtol": lambda x: "%.2f%%" % (100 * x),
- "act mem/rtol": lambda x: "%.2f%%" % (100 * x),
- }
- )
- if show
- else result
- )
diff --git a/src/blueprinting/validation/legacy/seqsel_fig7.py b/src/blueprinting/validation/legacy/seqsel_fig7.py
deleted file mode 100644
index fe2c63f..0000000
--- a/src/blueprinting/validation/legacy/seqsel_fig7.py
+++ /dev/null
@@ -1,133 +0,0 @@
-"""Legacy Calculon reproduction of SeqSel figure 7.
-
-This compatibility check does not exercise Blueprinting's canonical derivation
-path; the strict production gate lives in :mod:`blueprinting.validation.calculon`.
-"""
-
-import logging
-
-import hyperparameter as hp
-import pandas as pd
-
-import blueprinting.io as io
-from blueprinting.types import Execution, Model
-
-# Calculon is used here for validation comparison only
-from calculon.llm import Llm
-from calculon.llm import System as CalculonSystem
-
-kProfile = {
- "megatron-22B": {
- "none": 100.00,
- "seq": 66.84,
- "sel": 49.42,
- "seqsel": 16.18,
- "full": 7.64,
- },
- "gpt3-175B": {
- "none": 100.00,
- "seq": 62.04,
- "sel": 56.53,
- "seqsel": 18.49,
- "full": 8.71,
- },
- "turing-530B": {
- "none": 100.00,
- "seq": 58.31,
- "sel": 62.04,
- "seqsel": 20.27,
- "full": 9.42,
- },
- "megatron-1T": {
- "none": 100.00,
- "seq": 58.31,
- "sel": 62.04,
- "seqsel": 20.27,
- "full": 9.42,
- },
-}
-
-
-mem_usage = pd.DataFrame.from_records(
- [
- {
- "model": model,
- "system": "a100_80e",
- "mode": mode,
- "act mem(%)": kProfile[model][mode],
- }
- for model in kProfile
- for mode in kProfile[model]
- ]
-)
-
-
-def seqsel_fig7(show=False):
- models = mem_usage["model"].unique()
- systems = mem_usage["system"].unique()
- modes = mem_usage["mode"].unique()
-
- records = []
-
- for m, s, e in [(m, s, e) for m in models for s in systems for e in modes]:
- app_json = f"data/models/{m}.json"
- sys_json = f"data/systems/{s}.json"
- exe_json = f"data/validation/seqsel/fig7/{m}_{e}.json"
-
- logger = logging.getLogger()
-
- app_json = io.read_json_file(app_json)
- sys_json = io.read_json_file(sys_json)
- exe_json = io.read_json_file(exe_json)
-
- with hp.scope(app=app_json, sys=sys_json, exe=exe_json) as ps:
- app = Model(ps.app)
- exe = Execution(ps.exe)
- # Use calculon's System for validation
- syst = CalculonSystem(sys_json)
-
- model = Llm(app, logger)
- model.compile(syst, exe)
- model.run(syst)
- stats = model.get_stats_json(False)
- act_act = stats["act_space"] + stats["act_checkpoint_size"]
- records += [
- {
- "model": m,
- "system": s,
- "mode": e,
- "act mem(%)": act_act,
- }
- ]
- df = pd.DataFrame.from_records(records)
- selected = df[df["mode"] == "none"]
- for _, row in df.iterrows():
- x = selected[(selected.model == row.model) & (selected.system == row.system)]
- df.loc[
- (df.model == row.model) & (df.system == row.system) & (df["mode"] == row["mode"]),
- "act mem(%)",
- ] = 100 * row["act mem(%)"] / x.iloc[0]["act mem(%)"]
- result = (
- mem_usage.set_index(["model", "system", "mode"])
- .join(
- df.set_index(["model", "system", "mode"]),
- on=["model", "system", "mode"],
- lsuffix="[act]",
- rsuffix="[pred]",
- )
- .reset_index()
- )
-
- result["act mem/rtol"] = (result["act mem(%)[act]"] - result["act mem(%)[pred]"]).abs() / result["act mem(%)[act]"]
-
- return (
- result.style.format(
- {
- "act mem/rtol": lambda x: "%.2f%%" % (100 * x),
- "act mem(%)[act]": "{:.2f}%",
- "act mem(%)[pred]": "{:.2f}%",
- }
- )
- if show
- else result
- )
diff --git a/src/blueprinting/validation/legacy/seqsel_tab5.py b/src/blueprinting/validation/legacy/seqsel_tab5.py
deleted file mode 100644
index 35f6ede..0000000
--- a/src/blueprinting/validation/legacy/seqsel_tab5.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""Legacy Calculon reproduction of SeqSel table 5.
-
-This compatibility check does not exercise Blueprinting's canonical derivation
-path; the strict production gate lives in :mod:`blueprinting.validation.calculon`.
-"""
-
-import logging
-
-import hyperparameter as hp
-import pandas as pd
-
-import blueprinting.io as io
-from blueprinting.types import Execution, Model
-
-# Calculon is used here for validation comparison only
-from calculon.llm import Llm
-from calculon.llm import System as CalculonSystem
-
-kProfile = {
- "megatron-22B": {"full": 1.42, "seqsel": 1.10},
- "gpt3-175B": {"full": 18.13, "seqsel": 13.75},
- "turing-530B": {"full": 49.05, "seqsel": 37.83},
- "megatron-1T": {"full": 94.42, "seqsel": 71.49},
-}
-
-iter_time = pd.DataFrame.from_records(
- [
- {
- "model": model,
- "system": "a100_80g",
- "mode": mode,
- "iter time(s)": kProfile[model][mode],
- }
- for model in kProfile
- for mode in kProfile[model]
- ]
-)
-
-
-def seqsel_tab5(show=False):
- models = iter_time["model"].unique()
- systems = iter_time["system"].unique()
- modes = iter_time["mode"].unique()
-
- records = []
-
- for m, s, e in [(m, s, e) for m in models for s in systems for e in modes]:
- app_json = f"data/models/{m}.json"
- sys_json = f"data/systems/{s}.json"
- exe_json = f"data/validation/seqsel/tab5/{m}_{e}.json"
-
- logger = logging.getLogger()
-
- app_json = io.read_json_file(app_json)
- sys_json = io.read_json_file(sys_json)
- exe_json = io.read_json_file(exe_json)
-
- with hp.scope(app=app_json, sys=sys_json, exe=exe_json) as ps:
- app = Model(ps.app)
- exe = Execution(ps.exe)
- # Use calculon's System for validation
- syst = CalculonSystem(sys_json)
-
- model = Llm(app, logger)
- model.compile(syst, exe)
- model.run(syst)
- stats = model.get_stats_json(False)
- act_act = stats["total_time"]
- records += [
- {
- "model": m,
- "system": s,
- "mode": e,
- "iter time(s)": act_act,
- }
- ]
- df = pd.DataFrame.from_records(records)
- result = (
- iter_time.set_index(["model", "system", "mode"])
- .join(
- df.set_index(["model", "system", "mode"]),
- on=["model", "system", "mode"],
- lsuffix="[act]",
- rsuffix="[pred]",
- )
- .reset_index()
- )
-
- result["iter time/rtol"] = (result["iter time(s)[act]"] - result["iter time(s)[pred]"]).abs() / result[
- "iter time(s)[act]"
- ]
-
- return result.style.format({"iter time/rtol": lambda x: "%.2f%%" % (100 * x)}) if show else result
diff --git a/src/blueprinting/workbench/nicegui_ui.py b/src/blueprinting/workbench/nicegui_ui.py
index 8155131..ad64a76 100644
--- a/src/blueprinting/workbench/nicegui_ui.py
+++ b/src/blueprinting/workbench/nicegui_ui.py
@@ -618,7 +618,6 @@ def build(self) -> None:
ui.colors(primary="#2563eb", secondary="#2563eb", positive="#15803d", warning="#b45309", negative="#b91c1c")
ui.dark_mode(False)
ui.page_title("Blueprinting · 硬件架构工作台")
- self._build_legacy_dialog()
with ui.left_drawer(value=True, bordered=False) as self.sidebar:
self.sidebar.props("width=288 breakpoint=980").classes("bp-sidebar")
@@ -672,9 +671,6 @@ def build(self) -> None:
ui.element("span").classes("bp-service-dot")
ui.label("本地服务可用").classes("bp-sidebar-meta")
ui.label("Formal plan · Numerical evidence").classes("bp-sidebar-meta bp-mono")
- ui.button("Legacy 工具", icon="history", on_click=self.legacy_dialog.open).props(
- "flat no-caps align=left"
- ).classes("bp-sidebar-legacy w-full")
with (
ui.dialog() as self.config_dialog,
@@ -708,20 +704,6 @@ def _toggle_sidebar(self) -> None:
if hasattr(self, "sidebar"):
self.sidebar.toggle()
- def _build_legacy_dialog(self) -> None:
- with ui.dialog() as self.legacy_dialog, ui.card().classes("bp-card").style("width: 560px; max-width: 92vw"):
- with ui.row().classes("items-center gap-3"):
- ui.icon("inventory_2", size="26px", color="secondary")
- with ui.column().classes("gap-0"):
- ui.label("Calculon / Streamlit Legacy").classes("bp-card-title")
- ui.label("旧工具保持隔离,不参与 Blueprinting 主分析路径。 ").classes("bp-card-copy")
- ui.separator().classes("my-2")
- ui.label("需要旧 Calculon 工具时,请单独启动:").classes("text-sm")
- ui.code("uv run streamlit run streamlit_app.py", language="bash").classes("bp-code")
- with ui.row().classes("w-full justify-end gap-2"):
- ui.link("打开 localhost:8501", "http://127.0.0.1:8501", new_tab=True).classes("text-secondary")
- ui.button("关闭", on_click=self.legacy_dialog.close).props("flat no-caps")
-
def _select_mode(self, mode: WorkbenchMode) -> None:
if self.busy or mode is self.mode:
return
diff --git a/src/blueprinting/workbench/streamlit_ui.py b/src/blueprinting/workbench/streamlit_ui.py
deleted file mode 100644
index 6b3636d..0000000
--- a/src/blueprinting/workbench/streamlit_ui.py
+++ /dev/null
@@ -1,677 +0,0 @@
-"""Reusable Streamlit components backed only by the Blueprinting service."""
-
-from __future__ import annotations
-
-import inspect
-import json
-from dataclasses import dataclass
-from typing import Any
-
-import pandas as pd
-import streamlit as st
-
-from blueprinting.analysis import CalibrationMode
-from blueprinting.application import (
- AnalysisDiagnostic,
- AnalysisDraft,
- AnalysisOutcome,
- BlueprintingService,
- DiagnosticLevel,
- SweepReport,
- SweepRequest,
-)
-
-from .catalog import ConfigCatalog, default_catalog
-
-_COMPILER_DTYPES = ("float16", "bfloat16", "float32", "float8")
-_PARALLEL_OPTIONS = (1, 2, 4, 8, 12, 16, 24, 32, 48, 64, 96, 128)
-
-
-def _stretch(widget: Any) -> dict[str, Any]:
- """Use the current width API while retaining Streamlit 1.40 compatibility."""
-
- if "width" in inspect.signature(widget).parameters:
- return {"width": "stretch"}
- return {"use_container_width": True}
-
-
-@dataclass(frozen=True)
-class _PresetSelection:
- catalog: ConfigCatalog
- model_name: str
- execution_name: str
- hardware_name: str
- model_data: dict[str, Any]
- execution_data: dict[str, Any]
- hardware_data: dict[str, Any]
-
- @property
- def namespace(self) -> str:
- return f"{self.model_name}:{self.execution_name}:{self.hardware_name}"
-
-
-def setup_workbench_page(title: str, icon: str) -> None:
- st.set_page_config(page_title=f"Blueprinting · {title}", page_icon=icon, layout="wide")
- st.title(f"{icon} {title}")
-
-
-def _preferred_index(names: tuple[str, ...], preferred: str) -> int:
- return names.index(preferred) if preferred in names else 0
-
-
-def _preset_selection(key_prefix: str) -> _PresetSelection:
- catalog = default_catalog()
- model_names = catalog.names("models")
- execution_names = catalog.names("examples")
- hardware_names = catalog.names("systems")
- with st.sidebar:
- st.header("分析输入")
- model_name = st.selectbox(
- "模型预设",
- model_names,
- index=_preferred_index(model_names, "gpt3-175B.json"),
- key=f"{key_prefix}.preset.model",
- )
- hardware_name = st.selectbox(
- "硬件证据",
- hardware_names,
- index=_preferred_index(hardware_names, "a100_80g.json"),
- key=f"{key_prefix}.preset.hardware",
- )
- execution_name = st.selectbox(
- "策略模板",
- execution_names,
- index=0,
- key=f"{key_prefix}.preset.execution",
- )
- return _PresetSelection(
- catalog=catalog,
- model_name=model_name,
- execution_name=execution_name,
- hardware_name=hardware_name,
- model_data=catalog.load("models", model_name),
- execution_data=catalog.load("examples", execution_name),
- hardware_data=catalog.load("systems", hardware_name),
- )
-
-
-def _model_fields(selection: _PresetSelection, key_prefix: str) -> dict[str, Any]:
- model = selection.model_data
- namespace = f"{key_prefix}.{selection.namespace}.model"
- with st.expander("模型语义", expanded=False):
- hidden = st.number_input("Hidden size", min_value=1, value=int(model["hidden"]), key=f"{namespace}.hidden")
- feedforward = st.number_input(
- "Feed-forward size",
- min_value=1,
- value=int(model["feedforward"]),
- key=f"{namespace}.feedforward",
- )
- sequence = st.number_input(
- "Sequence length",
- min_value=1,
- value=int(model["seq_size"]),
- key=f"{namespace}.sequence",
- )
- heads = st.number_input(
- "Attention heads",
- min_value=1,
- value=int(model["attn_heads"]),
- key=f"{namespace}.heads",
- )
- head_size = st.number_input(
- "Attention head size",
- min_value=1,
- value=int(model["attn_size"]),
- key=f"{namespace}.head_size",
- )
- blocks = st.number_input(
- "Transformer blocks",
- min_value=1,
- value=int(model["num_blocks"]),
- key=f"{namespace}.blocks",
- )
- return {
- "hidden": int(hidden),
- "feedforward": int(feedforward),
- "seq_size": int(sequence),
- "attn_heads": int(heads),
- "attn_size": int(head_size),
- "num_blocks": int(blocks),
- }
-
-
-def _supported_datatypes(selection: _PresetSelection) -> tuple[str, ...]:
- matrix = set(selection.hardware_data.get("matrix", {}))
- vector = set(selection.hardware_data.get("vector", {}))
- supported = tuple(item for item in _COMPILER_DTYPES if item in matrix and item in vector)
- if not supported:
- raise ValueError(f"硬件预设 {selection.hardware_name} 没有同时定义 matrix/vector datatype")
- return supported
-
-
-def _strategy_fields(
- selection: _PresetSelection,
- key_prefix: str,
- *,
- include_parallelism: bool,
-) -> tuple[dict[str, Any], tuple[int, int, int] | None]:
- execution = dict(selection.execution_data)
- namespace = f"{key_prefix}.{selection.namespace}.execution"
- parallelism = None
- if include_parallelism:
- st.markdown("**并行策略**")
- col1, col2, col3 = st.columns(3)
- with col1:
- tp = st.number_input(
- "Tensor parallel",
- min_value=1,
- value=int(execution["tensor_par"]),
- key=f"{namespace}.tp",
- )
- with col2:
- pp = st.number_input(
- "Pipeline parallel",
- min_value=1,
- value=int(execution["pipeline_par"]),
- key=f"{namespace}.pp",
- )
- with col3:
- dp = st.number_input(
- "Data parallel",
- min_value=1,
- value=int(execution["data_par"]),
- key=f"{namespace}.dp",
- )
- parallelism = (int(tp), int(pp), int(dp))
- st.caption(f"World size 由并行度推导:{int(tp) * int(pp) * int(dp):,}")
-
- st.markdown("**训练负载**")
- col1, col2 = st.columns(2)
- with col1:
- global_batch = st.number_input(
- "Global batch size",
- min_value=1,
- value=int(execution["batch_size"]),
- key=f"{namespace}.global_batch",
- )
- with col2:
- microbatch = st.number_input(
- "Microbatch size",
- min_value=1,
- value=int(execution["microbatch_size"]),
- key=f"{namespace}.microbatch",
- )
-
- dtypes = _supported_datatypes(selection)
- current_dtype = execution.get("datatype", dtypes[0])
- datatype = st.selectbox(
- "Datatype",
- dtypes,
- index=dtypes.index(current_dtype) if current_dtype in dtypes else 0,
- key=f"{namespace}.datatype",
- )
- recompute_options = ("none", "attn_only", "full")
- recompute = st.selectbox(
- "Activation recompute",
- recompute_options,
- index=recompute_options.index(execution.get("activation_recompute", "none")),
- key=f"{namespace}.recompute",
- )
- communication_options = ("ar", "rs_ag")
- current_communication = execution.get("tensor_par_comm_type", "ar")
- communication = st.selectbox(
- "TP communication",
- communication_options,
- index=communication_options.index(current_communication) if current_communication in communication_options else 0,
- key=f"{namespace}.communication",
- )
- interleaving = st.number_input(
- "Pipeline interleaving",
- min_value=1,
- value=int(execution.get("pipeline_interleaving", 1)),
- key=f"{namespace}.interleaving",
- )
- optimizer_sharding = st.checkbox(
- "Optimizer sharding",
- value=bool(execution.get("optimizer_sharding", False)),
- key=f"{namespace}.optimizer_sharding",
- )
-
- network_count = len(selection.hardware_data.get("networks", ()))
- network_options = tuple(range(network_count))
- if not network_options:
- raise ValueError(f"硬件预设 {selection.hardware_name} 没有网络层级")
- with st.expander("网络映射", expanded=False):
- tp_network = st.selectbox(
- "TP network tier",
- network_options,
- index=min(int(execution.get("tensor_par_net", 0)), network_count - 1),
- key=f"{namespace}.tp_network",
- )
- pp_network = st.selectbox(
- "PP network tier",
- network_options,
- index=min(int(execution.get("pipeline_par_net", 0)), network_count - 1),
- key=f"{namespace}.pp_network",
- )
- dp_network = st.selectbox(
- "DP network tier",
- network_options,
- index=min(int(execution.get("data_par_net", 0)), network_count - 1),
- key=f"{namespace}.dp_network",
- )
-
- execution.update(
- {
- "batch_size": int(global_batch),
- "microbatch_size": int(microbatch),
- "datatype": datatype,
- "activation_recompute": recompute,
- "tensor_par_comm_type": communication,
- "pipeline_interleaving": int(interleaving),
- "optimizer_sharding": bool(optimizer_sharding),
- "tensor_par_net": int(tp_network),
- "pipeline_par_net": int(pp_network),
- "data_par_net": int(dp_network),
- "attention_type": "multihead",
- "tensor_par_overlap": "none",
- "data_par_overlap": False,
- "weight_offload": False,
- "activations_offload": False,
- "optimizer_offload": False,
- "training": True,
- }
- )
- if parallelism is not None:
- execution.update(
- {
- "tensor_par": parallelism[0],
- "pipeline_par": parallelism[1],
- "data_par": parallelism[2],
- "num_procs": parallelism[0] * parallelism[1] * parallelism[2],
- }
- )
- return execution, parallelism
-
-
-def _calibration_field(selection: _PresetSelection, key_prefix: str) -> CalibrationMode:
- namespace = f"{key_prefix}.{selection.namespace}.calibration"
- labels = {
- "系统证据曲线": CalibrationMode.SYSTEM_EVIDENCE,
- "理论峰值基线": CalibrationMode.PEAK_ONLY,
- }
- selected = st.radio(
- "估算证据",
- tuple(labels),
- horizontal=True,
- key=namespace,
- )
- return labels[selected]
-
-
-def analysis_form(key_prefix: str, submit_label: str = "运行 Blueprinting 分析") -> AnalysisDraft | None:
- selection = _preset_selection(key_prefix)
- with st.sidebar, st.form(f"{key_prefix}.analysis_form"):
- model_data = _model_fields(selection, key_prefix)
- execution_data, _ = _strategy_fields(selection, key_prefix, include_parallelism=True)
- calibration = _calibration_field(selection, key_prefix)
- submitted = st.form_submit_button(submit_label, type="primary", **_stretch(st.form_submit_button))
- if not submitted:
- return None
- return AnalysisDraft.from_mappings(
- model_name=selection.model_name.removesuffix(".json"),
- model_data=model_data,
- execution_name=selection.execution_name,
- execution_data=execution_data,
- hardware_name=selection.hardware_name.removesuffix(".json"),
- hardware_data=selection.hardware_data,
- calibration_mode=calibration,
- )
-
-
-def sweep_form(key_prefix: str) -> SweepRequest | None:
- selection = _preset_selection(key_prefix)
- execution = selection.execution_data
- with st.sidebar, st.form(f"{key_prefix}.sweep_form"):
- model_data = _model_fields(selection, key_prefix)
- execution_data, _ = _strategy_fields(selection, key_prefix, include_parallelism=False)
- calibration = _calibration_field(selection, key_prefix)
- st.markdown("**候选空间**")
- options = tuple(
- sorted(
- set(
- _PARALLEL_OPTIONS
- + (
- int(execution["tensor_par"]),
- int(execution["pipeline_par"]),
- int(execution["data_par"]),
- )
- )
- )
- )
- tp_values = st.multiselect(
- "Tensor parallel candidates",
- options,
- default=[int(execution["tensor_par"])],
- key=f"{key_prefix}.{selection.namespace}.sweep.tp",
- )
- pp_values = st.multiselect(
- "Pipeline parallel candidates",
- options,
- default=[int(execution["pipeline_par"])],
- key=f"{key_prefix}.{selection.namespace}.sweep.pp",
- )
- dp_values = st.multiselect(
- "Data parallel candidates",
- options,
- default=[int(execution["data_par"])],
- key=f"{key_prefix}.{selection.namespace}.sweep.dp",
- )
- count = len(tp_values) * len(pp_values) * len(dp_values)
- st.caption(f"候选数量:{count} / 128")
- submitted = st.form_submit_button(
- "探索策略空间",
- type="primary",
- **_stretch(st.form_submit_button),
- )
- if not submitted:
- return None
- if not tp_values or not pp_values or not dp_values:
- st.sidebar.error("TP、PP、DP 候选集合不能为空。")
- return None
- base_tp = int(execution["tensor_par"])
- base_pp = int(execution["pipeline_par"])
- base_dp = int(execution["data_par"])
- execution_data.update(
- {
- "tensor_par": base_tp,
- "pipeline_par": base_pp,
- "data_par": base_dp,
- "num_procs": base_tp * base_pp * base_dp,
- }
- )
- draft = AnalysisDraft.from_mappings(
- model_name=selection.model_name.removesuffix(".json"),
- model_data=model_data,
- execution_name=selection.execution_name,
- execution_data=execution_data,
- hardware_name=selection.hardware_name.removesuffix(".json"),
- hardware_data=selection.hardware_data,
- calibration_mode=calibration,
- )
- try:
- return SweepRequest(
- base=draft,
- tensor_parallel=tuple(int(item) for item in tp_values),
- pipeline_parallel=tuple(int(item) for item in pp_values),
- data_parallel=tuple(int(item) for item in dp_values),
- )
- except ValueError as error:
- st.sidebar.error(str(error))
- return None
-
-
-@st.cache_data(show_spinner=False)
-def cached_analysis(draft: AnalysisDraft) -> AnalysisOutcome:
- return BlueprintingService().analyze(draft)
-
-
-@st.cache_data(show_spinner=False)
-def cached_sweep(request: SweepRequest) -> SweepReport:
- return BlueprintingService().sweep(request)
-
-
-def remember(key: str, value: Any) -> Any:
- st.session_state[key] = value
- return value
-
-
-def recalled(key: str) -> Any | None:
- return st.session_state.get(key)
-
-
-def format_seconds(value: float) -> str:
- if value >= 1:
- return f"{value:.3f} s"
- if value >= 1e-3:
- return f"{value * 1e3:.3f} ms"
- if value >= 1e-6:
- return f"{value * 1e6:.3f} µs"
- return f"{value * 1e9:.3f} ns"
-
-
-def format_bytes(value: int | float) -> str:
- number = float(value)
- units = ("B", "KiB", "MiB", "GiB", "TiB", "PiB")
- for unit in units:
- if abs(number) < 1024 or unit == units[-1]:
- return f"{number:.2f} {unit}"
- number /= 1024
- raise AssertionError("byte formatter did not terminate")
-
-
-def format_count(value: int | float) -> str:
- number = float(value)
- for scale, suffix in ((1e15, "P"), (1e12, "T"), (1e9, "G"), (1e6, "M"), (1e3, "K")):
- if abs(number) >= scale:
- return f"{number / scale:.2f}{suffix}"
- return f"{number:.0f}"
-
-
-def render_diagnostics(diagnostics: tuple[AnalysisDiagnostic, ...]) -> None:
- for diagnostic in diagnostics:
- location = ".".join(diagnostic.path)
- suffix = f" · `{location}`" if location else ""
- message = f"**{diagnostic.code}**{suffix} — {diagnostic.message}"
- if diagnostic.hint:
- message += f"\n\n建议:{diagnostic.hint}"
- if diagnostic.level is DiagnosticLevel.ERROR:
- st.error(message)
- elif diagnostic.level is DiagnosticLevel.WARNING:
- st.warning(message)
- else:
- st.info(message)
-
-
-def render_analysis_summary(outcome: AnalysisOutcome) -> None:
- render_diagnostics(outcome.diagnostics)
- report = outcome.report
- if report is None:
- st.caption(f"Request digest: `{outcome.request_digest}`")
- return
-
- if report.feasible:
- st.success("该候选在当前解析式内存模型下可放入单设备容量。")
- else:
- st.warning("该候选完成了分析,但不满足设备内存容量约束。")
-
- columns = st.columns(5)
- metrics = (
- ("迭代延迟", format_seconds(report.total_seconds)),
- ("Token/s", format_count(report.total_tokens_per_second)),
- ("Token/s/设备", format_count(report.tokens_per_second_per_device)),
- ("单设备内存", format_bytes(report.memory["total"])),
- ("主导项", report.bottleneck.replace("_", " ")),
- )
- for column, (label, value) in zip(columns, metrics):
- column.metric(label, value)
-
- tab_latency, tab_memory, tab_workload, tab_evidence = st.tabs(
- ["延迟分解", "内存分解", "工作量事实", "证据与边界"]
- )
- with tab_latency:
- latency = pd.DataFrame(
- ((name.replace("_", " "), value) for name, value in report.latency.items()),
- columns=("component", "seconds"),
- ).set_index("component")
- st.bar_chart(latency, horizontal=True)
- st.dataframe(
- latency.reset_index().assign(display=lambda frame: frame["seconds"].map(format_seconds)),
- hide_index=True,
- **_stretch(st.dataframe),
- )
- with tab_memory:
- memory = pd.DataFrame(
- (
- (name.replace("_", " "), value)
- for name, value in report.memory.items()
- if name not in {"total", "capacity"}
- ),
- columns=("component", "bytes"),
- ).set_index("component")
- st.bar_chart(memory, horizontal=True)
- used = report.memory["total"] / report.memory["capacity"]
- st.progress(min(float(used), 1.0), text=f"容量使用率 {used:.1%}")
- with tab_workload:
- workload = report.workload.to_dict()
- rows = [
- {"事实": "Portable tasks", "值": f"{workload['task_count']:,}"},
- {"事实": "Compute tasks", "值": f"{workload['compute_task_count']:,}"},
- {"事实": "Collective tasks", "值": f"{workload['collective_task_count']:,}"},
- {"事实": "Operations", "值": format_count(workload["operations"])},
- {"事实": "Read bytes", "值": format_bytes(workload["read_bytes"])},
- {"事实": "Write bytes", "值": format_bytes(workload["write_bytes"])},
- {"事实": "Message bytes", "值": format_bytes(workload["message_bytes"])},
- ]
- st.dataframe(pd.DataFrame(rows), hide_index=True, **_stretch(st.dataframe))
- with tab_evidence:
- evidence = report.evidence.to_dict()
- st.json(evidence)
- st.markdown("**当前实现边界**")
- for limitation in report.limitations:
- st.markdown(f"- {limitation}")
- st.caption(
- f"Plan `{report.plan_digest}` · Evidence `{report.evidence_revision}` · "
- f"Request `{report.request_digest}`"
- )
-
-
-def stage_dataframe(outcome: AnalysisOutcome) -> pd.DataFrame:
- if outcome.report is None:
- return pd.DataFrame()
- return pd.DataFrame(
- {
- "stage": stage.stage,
- "label": stage.label,
- "pass": stage.pass_name,
- "schema": stage.schema,
- "nodes": stage.node_count,
- "values/buffers": stage.value_count,
- "lowering_ms": stage.duration_ns / 1e6,
- "valid": stage.valid,
- "digest": stage.digest,
- }
- for stage in outcome.report.stages
- )
-
-
-def task_dataframe(outcome: AnalysisOutcome) -> pd.DataFrame:
- if outcome.report is None:
- return pd.DataFrame()
- return pd.DataFrame(
- {
- "operation": task.operation,
- "phase": task.phase,
- "engine": task.engine,
- "kind": task.kind,
- "ops": task.operations,
- "read_bytes": task.read_bytes,
- "write_bytes": task.write_bytes,
- "message_bytes": task.message_bytes,
- "compute_s": task.compute_seconds,
- "memory_s": task.memory_seconds,
- "network_s": task.network_seconds,
- "total_s": task.total_seconds,
- "dependencies": len(task.dependencies),
- "concurrency_group": task.concurrency_group,
- "task_id": task.task_id,
- }
- for task in outcome.report.tasks
- )
-
-
-def render_ir_explorer(outcome: AnalysisOutcome) -> None:
- render_diagnostics(outcome.diagnostics)
- report = outcome.report
- if report is None:
- return
- st.subheader("推导边界")
- st.dataframe(stage_dataframe(outcome), hide_index=True, **_stretch(st.dataframe))
- tabs = st.tabs([stage.label for stage in report.stages])
- for tab, stage in zip(tabs, report.stages):
- with tab:
- cols = st.columns(4)
- cols[0].metric("节点", stage.node_count)
- cols[1].metric("值 / Buffer", stage.value_count)
- cols[2].metric("Lowering 耗时", format_seconds(stage.duration_ns / 1e9))
- cols[3].metric("Verifier", "通过" if stage.valid else "失败")
- st.caption(f"`{stage.schema}` · `{stage.digest}`")
- render_diagnostics(stage.diagnostics)
- snapshot = json.loads(stage.snapshot_json)
- with st.expander("Canonical IR snapshot", expanded=False):
- st.json(snapshot)
- st.download_button(
- "下载 snapshot",
- data=json.dumps(snapshot, indent=2, ensure_ascii=False),
- file_name=f"{stage.stage}-{stage.digest[:12]}.json",
- mime="application/json",
- key=f"download.{stage.digest}",
- )
- st.subheader("Portable task audit")
- st.dataframe(task_dataframe(outcome), hide_index=True, **_stretch(st.dataframe))
-
-
-def sweep_dataframe(report: SweepReport) -> pd.DataFrame:
- return pd.DataFrame(
- {
- "tp": case.tensor_parallel,
- "pp": case.pipeline_parallel,
- "dp": case.data_parallel,
- "world_size": case.world_size,
- "status": case.status,
- "feasible": case.feasible,
- "pareto": case.pareto,
- "latency_s": case.total_seconds,
- "memory_gib": case.memory_bytes / 1024**3 if case.memory_bytes is not None else None,
- "tokens_s_device": case.tokens_per_second_per_device,
- "bottleneck": case.bottleneck,
- "diagnostic": "; ".join(item.message for item in case.diagnostics),
- "request_digest": case.request_digest,
- }
- for case in report.cases
- )
-
-
-def render_sweep(report: SweepReport) -> None:
- data = sweep_dataframe(report)
- columns = st.columns(4)
- columns[0].metric("候选", len(report.cases))
- columns[1].metric("成功", report.succeeded_count)
- columns[2].metric("可行", report.feasible_count)
- columns[3].metric("Pareto", int(data["pareto"].sum()))
-
- successful = data[(data["status"] == "success") & data["latency_s"].notna()]
- if not successful.empty:
- st.subheader("延迟—内存空间")
- st.scatter_chart(
- successful,
- x="memory_gib",
- y="latency_s",
- color="pareto",
- size="world_size",
- )
- st.subheader("全部候选")
- st.dataframe(
- data.sort_values(["pareto", "feasible", "latency_s"], ascending=[False, False, True]),
- hide_index=True,
- **_stretch(st.dataframe),
- )
- invalid = data[data["status"] != "success"]
- if not invalid.empty:
- with st.expander(f"无效候选诊断({len(invalid)})", expanded=False):
- st.dataframe(
- invalid[["tp", "pp", "dp", "diagnostic", "request_digest"]],
- hide_index=True,
- **_stretch(st.dataframe),
- )
- st.caption(f"Sweep request `{report.request_digest}`")
diff --git a/streamlit_app.py b/streamlit_app.py
deleted file mode 100755
index 59f4538..0000000
--- a/streamlit_app.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""Legacy Streamlit surface for Calculon and transitional Blueprinting pages.
-
-The primary workbench is now launched with ``blueprinting-workbench``. Existing
-Calculon pages remain available here as an independent baseline and are not
-part of the Blueprinting product analysis path.
-"""
-
-import streamlit as st
-
-# ============================================================================
-# 页面导航配置
-# ============================================================================
-pg = st.navigation(
- {
- # The primary Blueprinting workflow stays directly visible. Secondary
- # and legacy tools become collapsed groups in Streamlit's top nav.
- "": [
- st.Page(
- "pages/Blueprinting/overview.py",
- title="分析总览",
- icon="🧭",
- url_path="blueprinting-overview",
- default=True,
- ),
- ],
- "Calculon 基线(旧版)": [
- st.Page(
- "pages/LLM_Calc/overview.py",
- title="Overview",
- icon="📊",
- url_path="calculon-overview",
- ),
- st.Page(
- "pages/LLM_Calc/blockwise.py",
- title="块粒度",
- icon="🧱",
- ),
- st.Page(
- "pages/LLM_Calc/distexp.py",
- title="分布式实验",
- icon="🔄",
- ),
- ],
- },
- position="top",
-)
-
-
-# ============================================================================
-# 运行应用
-# ============================================================================
-pg.run()
diff --git a/tests/validation/legacy/test_seqsel_fig1.py b/tests/validation/legacy/test_seqsel_fig1.py
deleted file mode 100644
index d960e19..0000000
--- a/tests/validation/legacy/test_seqsel_fig1.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from blueprinting.console import print_rich_table
-
-
-def test_seqsel_fig1():
- from blueprinting.validation.legacy.seqsel_fig1 import seqsel_fig1
-
- ret = seqsel_fig1()
- print_rich_table(ret, caption="w+opt mem & act mem")
-
- assert ret["w+opt mem/rtol"].max() < 0.11
- assert ret["act mem/rtol"].max() < 0.1
diff --git a/tests/validation/legacy/test_seqsel_fig7.py b/tests/validation/legacy/test_seqsel_fig7.py
deleted file mode 100644
index dbd7753..0000000
--- a/tests/validation/legacy/test_seqsel_fig7.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from blueprinting.console import print_rich_table
-
-
-def test_seqsel_fig7():
- from blueprinting.validation.legacy.seqsel_fig7 import seqsel_fig7
-
- ret = seqsel_fig7()
- print_rich_table(ret, caption="act mem")
-
- assert ret["act mem/rtol"].max() < 0.31
diff --git a/tests/validation/legacy/test_seqsel_tab5.py b/tests/validation/legacy/test_seqsel_tab5.py
deleted file mode 100644
index 78019bd..0000000
--- a/tests/validation/legacy/test_seqsel_tab5.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from blueprinting.console import print_rich_table
-
-
-def test_seqsel_tab5():
- from blueprinting.validation.legacy.seqsel_tab5 import seqsel_tab5
-
- ret = seqsel_tab5()
- print_rich_table(ret, caption="iter time")
-
- assert ret["iter time/rtol"].max() < 0.09
diff --git a/tests/workbench/test_nicegui_workbench.py b/tests/workbench/test_nicegui_workbench.py
index 393ccee..7cf3787 100644
--- a/tests/workbench/test_nicegui_workbench.py
+++ b/tests/workbench/test_nicegui_workbench.py
@@ -51,7 +51,6 @@ async def test_nicegui_workbench_loads_without_eager_analysis(
await user.should_see("当前显示解析任务贡献,不是事件级 Timeline")
await user.should_see(marker="run-analysis")
await user.should_see(marker="sidebar-run-analysis")
- await user.should_see("Calculon / Streamlit Legacy")
await user.should_see(marker="mode-evidence")
await user.should_see(marker="mode-float")
diff --git a/tests/workbench/test_streamlit_workbench.py b/tests/workbench/test_streamlit_workbench.py
deleted file mode 100644
index dfdac84..0000000
--- a/tests/workbench/test_streamlit_workbench.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from __future__ import annotations
-
-from streamlit.testing.v1 import AppTest
-
-
-def test_workbench_page_loads_without_eager_analysis() -> None:
- app = AppTest.from_file("pages/Blueprinting/overview.py", default_timeout=30).run()
-
- assert not app.exception
- assert [button.label for button in app.button] == ["运行 Blueprinting 分析"]
- assert not app.metric
-
-
-def test_overview_runs_blueprinting_analysis() -> None:
- app = AppTest.from_file("pages/Blueprinting/overview.py", default_timeout=30).run()
-
- app.button[0].click().run()
-
- assert not app.exception
- assert not app.error
- assert app.success
- assert {metric.label for metric in app.metric} == {
- "迭代延迟",
- "Token/s",
- "Token/s/设备",
- "单设备内存",
- "主导项",
- }
-
-
-def test_navigation_defaults_to_blueprinting_without_path_collisions() -> None:
- app = AppTest.from_file("streamlit_app.py", default_timeout=30).run()
-
- assert not app.exception
- assert app.title[0].value == "🧭 分析总览"
diff --git a/uv.lock b/uv.lock
index c495006..bc1e5f5 100644
--- a/uv.lock
+++ b/uv.lock
@@ -176,36 +176,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
]
-[[package]]
-name = "altair"
-version = "6.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "jsonschema" },
- { name = "narwhals" },
- { name = "packaging" },
- { name = "typing-extensions", marker = "python_full_version < '3.15'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f7/c0/184a89bd5feba14ff3c41cfaf1dd8a82c05f5ceedbc92145e17042eb08a4/altair-6.0.0.tar.gz", hash = "sha256:614bf5ecbe2337347b590afb111929aa9c16c9527c4887d96c9bc7f6640756b4", size = 763834, upload-time = "2025-11-12T08:59:11.519Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/db/33/ef2f2409450ef6daa61459d5de5c08128e7d3edb773fefd0a324d1310238/altair-6.0.0-py3-none-any.whl", hash = "sha256:09ae95b53d5fe5b16987dccc785a7af8588f2dca50de1e7a156efa8a461515f8", size = 795410, upload-time = "2025-11-12T08:59:09.804Z" },
-]
-
-[[package]]
-name = "altex"
-version = "0.2.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "altair" },
- { name = "pandas" },
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/98/bc/200768a7faf5fcdbfefbefdbb1528a88fe552a1f6bf26f40097a22e854c7/altex-0.2.0.tar.gz", hash = "sha256:5ed6404af0d242a751111bd78421951d5e7025f9c559b7bcb151d3b4e63299d7", size = 100539, upload-time = "2025-06-30T17:02:33.437Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/84/ef/f94a52c48f8396f07411c8a0a244c4b551a6d57ecbcdc81d5d2345ebbd69/altex-0.2.0-py3-none-any.whl", hash = "sha256:b831e65f908f0197a1a092daab66c7059648ddfed2db8490bde6a331d524c85a", size = 25491, upload-time = "2025-06-30T17:02:32.343Z" },
-]
-
[[package]]
name = "annotated-doc"
version = "0.0.5"
@@ -238,15 +208,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" },
]
-[[package]]
-name = "asn1crypto"
-version = "1.5.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", size = 121080, upload-time = "2022-03-15T14:46:52.889Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045, upload-time = "2022-03-15T14:46:51.055Z" },
-]
-
[[package]]
name = "async-timeout"
version = "5.0.1"
@@ -297,19 +258,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/02/e3/a4fa1946722c4c7b063cc25043a12d9ce9b4323777f89643be74cef2993c/backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0", size = 381058, upload-time = "2025-11-15T14:52:06.698Z" },
]
-[[package]]
-name = "beautifulsoup4"
-version = "4.14.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "soupsieve" },
- { name = "typing-extensions" },
-]
-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/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 = "bidict"
version = "0.23.1"
@@ -319,27 +267,16 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5", size = 32764, upload-time = "2024-02-18T19:09:04.156Z" },
]
-[[package]]
-name = "blinker"
-version = "1.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" },
-]
-
[[package]]
name = "blueprinting"
source = { editable = "." }
dependencies = [
- { name = "hyperparameter" },
{ name = "nicegui" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "pandas" },
{ name = "psutil" },
{ name = "rich" },
- { name = "sympy" },
]
[package.optional-dependencies]
@@ -355,8 +292,6 @@ all = [
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "ruff" },
- { name = "streamlit" },
- { name = "streamlit-extras" },
]
dev = [
{ name = "pytest" },
@@ -373,12 +308,6 @@ docs = [
]
full = [
{ name = "plotly" },
- { name = "streamlit" },
- { name = "streamlit-extras" },
-]
-legacy-ui = [
- { name = "streamlit" },
- { name = "streamlit-extras" },
]
performance-data = [
{ name = "pyarrow" },
@@ -390,13 +319,10 @@ dev = [
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "ruff" },
- { name = "streamlit" },
- { name = "streamlit-extras" },
]
[package.metadata]
requires-dist = [
- { name = "hyperparameter", specifier = ">=0.4.0" },
{ name = "mkdocs", marker = "extra == 'all'", specifier = ">=1.5.0" },
{ name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.5.0" },
{ name = "mkdocs-material", marker = "extra == 'all'", specifier = ">=9.0.0" },
@@ -424,15 +350,8 @@ requires-dist = [
{ name = "rich", specifier = ">=12.0.0" },
{ name = "ruff", marker = "extra == 'all'", specifier = ">=0.1.0" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" },
- { name = "streamlit", marker = "extra == 'all'", specifier = ">=1.53,<2" },
- { name = "streamlit", marker = "extra == 'full'", specifier = ">=1.53,<2" },
- { name = "streamlit", marker = "extra == 'legacy-ui'", specifier = ">=1.53,<2" },
- { name = "streamlit-extras", marker = "extra == 'all'", specifier = ">=0.6,<1" },
- { name = "streamlit-extras", marker = "extra == 'full'", specifier = ">=0.6,<1" },
- { name = "streamlit-extras", marker = "extra == 'legacy-ui'", specifier = ">=0.6,<1" },
- { name = "sympy", specifier = ">=1.12.1" },
-]
-provides-extras = ["all", "dev", "docs", "full", "legacy-ui", "performance-data"]
+]
+provides-extras = ["all", "dev", "docs", "full", "performance-data"]
[package.metadata.requires-dev]
dev = [
@@ -440,45 +359,6 @@ dev = [
{ name = "pytest-asyncio", specifier = ">=0.24.0" },
{ name = "pytest-cov", specifier = ">=4.0.0" },
{ name = "ruff", specifier = ">=0.1.0" },
- { name = "streamlit", specifier = ">=1.53,<2" },
- { name = "streamlit-extras", specifier = ">=0.6,<1" },
-]
-
-[[package]]
-name = "boto3"
-version = "1.42.40"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "botocore" },
- { name = "jmespath" },
- { name = "s3transfer" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/d1/5a/bc190b2e8f2f625cbe50e1226fd7ce6b3719159add6d8d3c01a60defb7c4/boto3-1.42.40.tar.gz", hash = "sha256:e9e08059ae1bd47de411d361e9bfaaa6f35c8f996d68025deefff2b4dda79318", size = 112843, upload-time = "2026-02-02T20:35:04.404Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/e7/e0d57bcb17aa0ef1d234c9685b67d26e92ee5d2031415a3ca051803ebc6c/boto3-1.42.40-py3-none-any.whl", hash = "sha256:91d776b8b68006c1aca204d384be191883c2a36443f4a90561165986dae17b74", size = 140604, upload-time = "2026-02-02T20:35:01.913Z" },
-]
-
-[[package]]
-name = "botocore"
-version = "1.42.40"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jmespath" },
- { name = "python-dateutil" },
- { name = "urllib3" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c0/ea/912061bde3c36b951d7e56e53f48445f8ecf769c222ef46f604ecf8f574f/botocore-1.42.40.tar.gz", hash = "sha256:6cfa07cf35ad477daef4920324f6d81b8d3a10a35baeafaa5fca22fb3ad225e2", size = 14916776, upload-time = "2026-02-02T20:34:52.409Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ac/6a/a8b4fa14774b576bd7410c32a63d06a3a55287457d67c6bf11ebfef22aeb/botocore-1.42.40-py3-none-any.whl", hash = "sha256:b115cdfece8162cb30f387fdff2ee4693713744c97ebb4b89742e53675dc521c", size = 14592684, upload-time = "2026-02-02T20:34:48.087Z" },
-]
-
-[[package]]
-name = "cachetools"
-version = "6.2.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/39/91/d9ae9a66b01102a18cd16db0cf4cd54187ffe10f0865cc80071a4104fbb3/cachetools-6.2.6.tar.gz", hash = "sha256:16c33e1f276b9a9c0b49ab5782d901e3ad3de0dd6da9bf9bcd29ac5672f2f9e6", size = 32363, upload-time = "2026-01-27T20:32:59.956Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/90/45/f458fa2c388e79dd9d8b9b0c99f1d31b568f27388f2fdba7bb66bbc0c6ed/cachetools-6.2.6-py3-none-any.whl", hash = "sha256:8c9717235b3c651603fff0076db52d6acbfd1b338b8ed50256092f7ce9c85bda", size = 11668, upload-time = "2026-01-27T20:32:58.527Z" },
]
[[package]]
@@ -490,88 +370,6 @@ wheels = [
{ 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]]
-name = "cffi"
-version = "2.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pycparser", marker = "implementation_name != 'PyPy'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" },
- { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" },
- { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" },
- { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" },
- { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" },
- { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" },
- { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" },
- { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" },
- { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" },
- { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" },
- { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" },
- { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" },
- { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
- { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
- { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
- { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
- { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
- { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
- { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
- { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
- { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
- { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
- { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
- { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
- { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
- { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
- { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
- { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
- { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
- { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
- { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
- { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
- { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
- { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
- { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
- { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
- { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
- { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
- { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
- { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
- { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
- { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
- { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
- { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
- { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
- { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
- { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
- { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
- { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
- { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
- { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
- { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
- { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
- { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
- { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
- { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
- { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
- { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
- { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
- { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
- { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
- { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
- { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
- { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
- { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
- { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
- { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
- { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
- { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
- { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
- { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
-]
-
[[package]]
name = "charset-normalizer"
version = "3.4.4"
@@ -673,15 +471,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
]
-[[package]]
-name = "cloudpickle"
-version = "3.1.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113, upload-time = "2025-01-14T17:02:05.085Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992, upload-time = "2025-01-14T17:02:02.417Z" },
-]
-
[[package]]
name = "colorama"
version = "0.4.6"
@@ -691,163 +480,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
-[[package]]
-name = "contourpy"
-version = "1.3.2"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.11'",
-]
-dependencies = [
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" },
- { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" },
- { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" },
- { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" },
- { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" },
- { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" },
- { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" },
- { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" },
- { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" },
- { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" },
- { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" },
- { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" },
- { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" },
- { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" },
- { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" },
- { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" },
- { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" },
- { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" },
- { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" },
- { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" },
- { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" },
- { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" },
- { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" },
- { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" },
- { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" },
- { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" },
- { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" },
- { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" },
- { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" },
- { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" },
- { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" },
- { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" },
- { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" },
- { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" },
- { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" },
- { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" },
- { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" },
- { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" },
- { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" },
- { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" },
- { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" },
- { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" },
- { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" },
- { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" },
- { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" },
- { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" },
- { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" },
- { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" },
- { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" },
- { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" },
- { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" },
- { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" },
- { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" },
- { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" },
- { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" },
- { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" },
-]
-
-[[package]]
-name = "contourpy"
-version = "1.3.3"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version == '3.12.*'",
- "python_full_version == '3.11.*'",
-]
-dependencies = [
- { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" },
- { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" },
- { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" },
- { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" },
- { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" },
- { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" },
- { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" },
- { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" },
- { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" },
- { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" },
- { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" },
- { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" },
- { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" },
- { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" },
- { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" },
- { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" },
- { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" },
- { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" },
- { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" },
- { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" },
- { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" },
- { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" },
- { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" },
- { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" },
- { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" },
- { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" },
- { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" },
- { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" },
- { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" },
- { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" },
- { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" },
- { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" },
- { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" },
- { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" },
- { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" },
- { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" },
- { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" },
- { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" },
- { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" },
- { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" },
- { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" },
- { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" },
- { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" },
- { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" },
- { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" },
- { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" },
- { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" },
- { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" },
- { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" },
- { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" },
- { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" },
- { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" },
- { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" },
- { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" },
- { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" },
- { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" },
- { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" },
- { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" },
- { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" },
- { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" },
- { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" },
- { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" },
- { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" },
- { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" },
- { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" },
- { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" },
- { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" },
- { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" },
- { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" },
- { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" },
- { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
-]
-
[[package]]
name = "coverage"
version = "7.13.2"
@@ -952,75 +584,6 @@ 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 = "cycler"
-version = "0.12.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
-]
-
[[package]]
name = "docutils"
version = "0.23"
@@ -1030,15 +593,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/32/91/30151a39f7570f448ed84529390628a651d7f27c87d73c9b887f8189695e/docutils-0.23-py3-none-any.whl", hash = "sha256:25d013af9bf23bc1c7b2b093dff4208166c53a94786c9e447808335ef1185fea", size = 634701, upload-time = "2026-05-27T17:40:58.442Z" },
]
-[[package]]
-name = "entrypoints"
-version = "0.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ea/8d/a7121ffe5f402dc015277d2d31eb82d2187334503a011c18f2e78ecbb9b2/entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", size = 13974, upload-time = "2022-02-02T21:30:28.172Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/35/a8/365059bbcd4572cbc41de17fd5b682be5868b218c3c5479071865cab9078/entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f", size = 5294, upload-time = "2022-02-02T21:30:26.024Z" },
-]
-
[[package]]
name = "exceptiongroup"
version = "1.3.1"
@@ -1051,18 +605,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
]
-[[package]]
-name = "faker"
-version = "40.1.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "tzdata", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5e/77/1c3ff07b6739b9a1d23ca01ec0a90a309a33b78e345a3eb52f9ce9240e36/faker-40.1.2.tar.gz", hash = "sha256:b76a68163aa5f171d260fc24827a8349bc1db672f6a665359e8d0095e8135d30", size = 1949802, upload-time = "2026-01-13T20:51:49.917Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/46/ec/91a434c8a53d40c3598966621dea9c50512bec6ce8e76fa1751015e74cef/faker-40.1.2-py3-none-any.whl", hash = "sha256:93503165c165d330260e4379fd6dc07c94da90c611ed3191a0174d2ab9966a42", size = 1985633, upload-time = "2026-01-13T20:51:47.982Z" },
-]
-
[[package]]
name = "fastapi"
version = "0.141.1"
@@ -1079,85 +621,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" },
]
-[[package]]
-name = "favicon"
-version = "0.7.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "beautifulsoup4" },
- { name = "requests" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/64/68/d2646f40c05d3a501cddd232119f8c087a6fcba3c79255a062c73e80b42a/favicon-0.7.0.tar.gz", hash = "sha256:6d6b5a78de2a0d0084589f687f384b2ecd6a6527093fec564403b1a30605d7a8", size = 9284, upload-time = "2019-08-31T16:56:42.464Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/93/4c/8baf94bb789972634d933152d27529f2bad4e5d2397b8da9c30f6f5342ce/favicon-0.7.0-py2.py3-none-any.whl", hash = "sha256:7fec0617c73dcb8521ea788e1d38cdc7226c7cb8e28c81e11625d85fa1534880", size = 5921, upload-time = "2019-08-31T16:56:40.751Z" },
-]
-
-[[package]]
-name = "filelock"
-version = "3.20.3"
-source = { registry = "https://pypi.org/simple" }
-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/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]]
-name = "fonttools"
-version = "4.61.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756, upload-time = "2025-12-12T17:31:24.246Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799, upload-time = "2025-12-12T17:29:27.5Z" },
- { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032, upload-time = "2025-12-12T17:29:30.115Z" },
- { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863, upload-time = "2025-12-12T17:29:32.535Z" },
- { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076, upload-time = "2025-12-12T17:29:34.907Z" },
- { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623, upload-time = "2025-12-12T17:29:37.33Z" },
- { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327, upload-time = "2025-12-12T17:29:39.781Z" },
- { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180, upload-time = "2025-12-12T17:29:42.217Z" },
- { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654, upload-time = "2025-12-12T17:29:44.564Z" },
- { url = "https://files.pythonhosted.org/packages/69/12/bf9f4eaa2fad039356cc627587e30ed008c03f1cebd3034376b5ee8d1d44/fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09", size = 2852213, upload-time = "2025-12-12T17:29:46.675Z" },
- { url = "https://files.pythonhosted.org/packages/ac/49/4138d1acb6261499bedde1c07f8c2605d1d8f9d77a151e5507fd3ef084b6/fonttools-4.61.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37", size = 2401689, upload-time = "2025-12-12T17:29:48.769Z" },
- { url = "https://files.pythonhosted.org/packages/e5/fe/e6ce0fe20a40e03aef906af60aa87668696f9e4802fa283627d0b5ed777f/fonttools-4.61.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77efb033d8d7ff233385f30c62c7c79271c8885d5c9657d967ede124671bbdfb", size = 5058809, upload-time = "2025-12-12T17:29:51.701Z" },
- { url = "https://files.pythonhosted.org/packages/79/61/1ca198af22f7dd22c17ab86e9024ed3c06299cfdb08170640e9996d501a0/fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9", size = 5036039, upload-time = "2025-12-12T17:29:53.659Z" },
- { url = "https://files.pythonhosted.org/packages/99/cc/fa1801e408586b5fce4da9f5455af8d770f4fc57391cd5da7256bb364d38/fonttools-4.61.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0de30bfe7745c0d1ffa2b0b7048fb7123ad0d71107e10ee090fa0b16b9452e87", size = 5034714, upload-time = "2025-12-12T17:29:55.592Z" },
- { url = "https://files.pythonhosted.org/packages/bf/aa/b7aeafe65adb1b0a925f8f25725e09f078c635bc22754f3fecb7456955b0/fonttools-4.61.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58b0ee0ab5b1fc9921eccfe11d1435added19d6494dde14e323f25ad2bc30c56", size = 5158648, upload-time = "2025-12-12T17:29:57.861Z" },
- { url = "https://files.pythonhosted.org/packages/99/f9/08ea7a38663328881384c6e7777bbefc46fd7d282adfd87a7d2b84ec9d50/fonttools-4.61.1-cp311-cp311-win32.whl", hash = "sha256:f79b168428351d11e10c5aeb61a74e1851ec221081299f4cf56036a95431c43a", size = 2280681, upload-time = "2025-12-12T17:29:59.943Z" },
- { url = "https://files.pythonhosted.org/packages/07/ad/37dd1ae5fa6e01612a1fbb954f0927681f282925a86e86198ccd7b15d515/fonttools-4.61.1-cp311-cp311-win_amd64.whl", hash = "sha256:fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7", size = 2331951, upload-time = "2025-12-12T17:30:02.254Z" },
- { url = "https://files.pythonhosted.org/packages/6f/16/7decaa24a1bd3a70c607b2e29f0adc6159f36a7e40eaba59846414765fd4/fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e", size = 2851593, upload-time = "2025-12-12T17:30:04.225Z" },
- { url = "https://files.pythonhosted.org/packages/94/98/3c4cb97c64713a8cf499b3245c3bf9a2b8fd16a3e375feff2aed78f96259/fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2", size = 2400231, upload-time = "2025-12-12T17:30:06.47Z" },
- { url = "https://files.pythonhosted.org/packages/b7/37/82dbef0f6342eb01f54bca073ac1498433d6ce71e50c3c3282b655733b31/fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796", size = 4954103, upload-time = "2025-12-12T17:30:08.432Z" },
- { url = "https://files.pythonhosted.org/packages/6c/44/f3aeac0fa98e7ad527f479e161aca6c3a1e47bb6996b053d45226fe37bf2/fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d", size = 5004295, upload-time = "2025-12-12T17:30:10.56Z" },
- { url = "https://files.pythonhosted.org/packages/14/e8/7424ced75473983b964d09f6747fa09f054a6d656f60e9ac9324cf40c743/fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8", size = 4944109, upload-time = "2025-12-12T17:30:12.874Z" },
- { url = "https://files.pythonhosted.org/packages/c8/8b/6391b257fa3d0b553d73e778f953a2f0154292a7a7a085e2374b111e5410/fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0", size = 5093598, upload-time = "2025-12-12T17:30:15.79Z" },
- { url = "https://files.pythonhosted.org/packages/d9/71/fd2ea96cdc512d92da5678a1c98c267ddd4d8c5130b76d0f7a80f9a9fde8/fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261", size = 2269060, upload-time = "2025-12-12T17:30:18.058Z" },
- { url = "https://files.pythonhosted.org/packages/80/3b/a3e81b71aed5a688e89dfe0e2694b26b78c7d7f39a5ffd8a7d75f54a12a8/fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9", size = 2319078, upload-time = "2025-12-12T17:30:22.862Z" },
- { url = "https://files.pythonhosted.org/packages/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c", size = 2846454, upload-time = "2025-12-12T17:30:24.938Z" },
- { url = "https://files.pythonhosted.org/packages/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e", size = 2398191, upload-time = "2025-12-12T17:30:27.343Z" },
- { url = "https://files.pythonhosted.org/packages/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5", size = 4928410, upload-time = "2025-12-12T17:30:29.771Z" },
- { url = "https://files.pythonhosted.org/packages/b0/8d/6fb3494dfe61a46258cd93d979cf4725ded4eb46c2a4ca35e4490d84daea/fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd", size = 4984460, upload-time = "2025-12-12T17:30:32.073Z" },
- { url = "https://files.pythonhosted.org/packages/f7/f1/a47f1d30b3dc00d75e7af762652d4cbc3dff5c2697a0dbd5203c81afd9c3/fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3", size = 4925800, upload-time = "2025-12-12T17:30:34.339Z" },
- { url = "https://files.pythonhosted.org/packages/a7/01/e6ae64a0981076e8a66906fab01539799546181e32a37a0257b77e4aa88b/fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d", size = 5067859, upload-time = "2025-12-12T17:30:36.593Z" },
- { url = "https://files.pythonhosted.org/packages/73/aa/28e40b8d6809a9b5075350a86779163f074d2b617c15d22343fce81918db/fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c", size = 2267821, upload-time = "2025-12-12T17:30:38.478Z" },
- { url = "https://files.pythonhosted.org/packages/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b", size = 2318169, upload-time = "2025-12-12T17:30:40.951Z" },
- { url = "https://files.pythonhosted.org/packages/32/8f/4e7bf82c0cbb738d3c2206c920ca34ca74ef9dabde779030145d28665104/fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd", size = 2846094, upload-time = "2025-12-12T17:30:43.511Z" },
- { url = "https://files.pythonhosted.org/packages/71/09/d44e45d0a4f3a651f23a1e9d42de43bc643cce2971b19e784cc67d823676/fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e", size = 2396589, upload-time = "2025-12-12T17:30:45.681Z" },
- { url = "https://files.pythonhosted.org/packages/89/18/58c64cafcf8eb677a99ef593121f719e6dcbdb7d1c594ae5a10d4997ca8a/fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c", size = 4877892, upload-time = "2025-12-12T17:30:47.709Z" },
- { url = "https://files.pythonhosted.org/packages/8a/ec/9e6b38c7ba1e09eb51db849d5450f4c05b7e78481f662c3b79dbde6f3d04/fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75", size = 4972884, upload-time = "2025-12-12T17:30:49.656Z" },
- { url = "https://files.pythonhosted.org/packages/5e/87/b5339da8e0256734ba0dbbf5b6cdebb1dd79b01dc8c270989b7bcd465541/fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063", size = 4924405, upload-time = "2025-12-12T17:30:51.735Z" },
- { url = "https://files.pythonhosted.org/packages/0b/47/e3409f1e1e69c073a3a6fd8cb886eb18c0bae0ee13db2c8d5e7f8495e8b7/fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2", size = 5035553, upload-time = "2025-12-12T17:30:54.823Z" },
- { url = "https://files.pythonhosted.org/packages/bf/b6/1f6600161b1073a984294c6c031e1a56ebf95b6164249eecf30012bb2e38/fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c", size = 2271915, upload-time = "2025-12-12T17:30:57.913Z" },
- { url = "https://files.pythonhosted.org/packages/52/7b/91e7b01e37cc8eb0e1f770d08305b3655e4f002fc160fb82b3390eabacf5/fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c", size = 2323487, upload-time = "2025-12-12T17:30:59.804Z" },
- { url = "https://files.pythonhosted.org/packages/39/5c/908ad78e46c61c3e3ed70c3b58ff82ab48437faf84ec84f109592cabbd9f/fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa", size = 2929571, upload-time = "2025-12-12T17:31:02.574Z" },
- { url = "https://files.pythonhosted.org/packages/bd/41/975804132c6dea64cdbfbaa59f3518a21c137a10cccf962805b301ac6ab2/fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91", size = 2435317, upload-time = "2025-12-12T17:31:04.974Z" },
- { url = "https://files.pythonhosted.org/packages/b0/5a/aef2a0a8daf1ebaae4cfd83f84186d4a72ee08fd6a8451289fcd03ffa8a4/fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19", size = 4882124, upload-time = "2025-12-12T17:31:07.456Z" },
- { url = "https://files.pythonhosted.org/packages/80/33/d6db3485b645b81cea538c9d1c9219d5805f0877fda18777add4671c5240/fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba", size = 5100391, upload-time = "2025-12-12T17:31:09.732Z" },
- { url = "https://files.pythonhosted.org/packages/6c/d6/675ba631454043c75fcf76f0ca5463eac8eb0666ea1d7badae5fea001155/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7", size = 4978800, upload-time = "2025-12-12T17:31:11.681Z" },
- { url = "https://files.pythonhosted.org/packages/7f/33/d3ec753d547a8d2bdaedd390d4a814e8d5b45a093d558f025c6b990b554c/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118", size = 5006426, upload-time = "2025-12-12T17:31:13.764Z" },
- { url = "https://files.pythonhosted.org/packages/b4/40/cc11f378b561a67bea850ab50063366a0d1dd3f6d0a30ce0f874b0ad5664/fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5", size = 2335377, upload-time = "2025-12-12T17:31:16.49Z" },
- { url = "https://files.pythonhosted.org/packages/e4/ff/c9a2b66b39f8628531ea58b320d66d951267c98c6a38684daa8f50fb02f8/fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b", size = 2400613, upload-time = "2025-12-12T17:31:18.769Z" },
- { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" },
-]
-
[[package]]
name = "frozenlist"
version = "1.8.0"
@@ -1291,30 +754,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" },
]
-[[package]]
-name = "gitdb"
-version = "4.0.12"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "smmap" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" },
-]
-
-[[package]]
-name = "gitpython"
-version = "3.1.46"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "gitdb" },
-]
-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/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 = "griffe"
version = "1.15.0"
@@ -1336,12 +775,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
-[[package]]
-name = "htbuilder"
-version = "0.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/be/88/0f48d1125168e9eea33879ea820000702533d103f1123d27ca69839a0db4/htbuilder-0.9.0.tar.gz", hash = "sha256:58c0bc5502c1a46b42ae9e074c43ec0f6fdc24ed334936cb17e1ed5a8938aee2", size = 10591, upload-time = "2025-01-09T07:00:26.189Z" }
-
[[package]]
name = "httpcore"
version = "1.0.9"
@@ -1420,20 +853,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
-[[package]]
-name = "hyperparameter"
-version = "0.6.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "toml" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5a/dc/0a1729fb6c498b6578ac301605517b01bc30f21b56b54e493913c661b1fc/hyperparameter-0.6.0.tar.gz", hash = "sha256:065e3ea223b6f3774de8cc4cb7c31e21a7939663785941fedfe64ce93707a1a8", size = 72870, upload-time = "2025-12-14T15:36:12.894Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4e/98/b589281e6a0f65b1b3e80e5d5db80ad877fa39f1db99797e71939d64ebd5/hyperparameter-0.6.0-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:86316d438d490afa74b6d3f968aceb4fb0854b19bf9378944b4e66710c281d54", size = 384297, upload-time = "2025-12-14T15:36:09.15Z" },
- { url = "https://files.pythonhosted.org/packages/40/d6/282f94276b7e49491e76a7bd6a1b768e4ca5f7ca5a6dbda2fc379abe6506/hyperparameter-0.6.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:ba8a4155ef34ae45e3bc64c6b27efdc8779a9c82621b265286b917038f6e544b", size = 370865, upload-time = "2025-12-14T15:36:10.497Z" },
- { url = "https://files.pythonhosted.org/packages/a1/12/26a3aa69814f5138b9a36afd741645ead4d0d3b9b23efa094bed7428bdd9/hyperparameter-0.6.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96caedaca1160ae742859bc2e9b94352b1b01fa041936467db5e96e84e9ed578", size = 437103, upload-time = "2025-12-14T15:36:11.845Z" },
-]
-
[[package]]
name = "idna"
version = "3.18"
@@ -1482,150 +901,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
-[[package]]
-name = "jmespath"
-version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" },
-]
-
-[[package]]
-name = "jsonschema"
-version = "4.26.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "attrs" },
- { name = "jsonschema-specifications" },
- { name = "referencing" },
- { name = "rpds-py" },
-]
-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/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-specifications"
-version = "2025.9.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "referencing" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
-]
-
-[[package]]
-name = "kiwisolver"
-version = "1.4.9"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159, upload-time = "2025-08-10T21:25:35.472Z" },
- { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578, upload-time = "2025-08-10T21:25:36.73Z" },
- { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312, upload-time = "2025-08-10T21:25:37.658Z" },
- { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458, upload-time = "2025-08-10T21:25:39.067Z" },
- { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640, upload-time = "2025-08-10T21:25:40.489Z" },
- { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074, upload-time = "2025-08-10T21:25:42.221Z" },
- { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036, upload-time = "2025-08-10T21:25:43.801Z" },
- { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310, upload-time = "2025-08-10T21:25:45.045Z" },
- { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943, upload-time = "2025-08-10T21:25:46.393Z" },
- { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488, upload-time = "2025-08-10T21:25:48.074Z" },
- { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787, upload-time = "2025-08-10T21:25:49.442Z" },
- { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730, upload-time = "2025-08-10T21:25:51.102Z" },
- { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036, upload-time = "2025-08-10T21:25:52.063Z" },
- { url = "https://files.pythonhosted.org/packages/6f/ab/c80b0d5a9d8a1a65f4f815f2afff9798b12c3b9f31f1d304dd233dd920e2/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16", size = 124167, upload-time = "2025-08-10T21:25:53.403Z" },
- { url = "https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089", size = 66579, upload-time = "2025-08-10T21:25:54.79Z" },
- { url = "https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543", size = 65309, upload-time = "2025-08-10T21:25:55.76Z" },
- { url = "https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61", size = 1435596, upload-time = "2025-08-10T21:25:56.861Z" },
- { url = "https://files.pythonhosted.org/packages/67/1e/51b73c7347f9aabdc7215aa79e8b15299097dc2f8e67dee2b095faca9cb0/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1", size = 1246548, upload-time = "2025-08-10T21:25:58.246Z" },
- { url = "https://files.pythonhosted.org/packages/21/aa/72a1c5d1e430294f2d32adb9542719cfb441b5da368d09d268c7757af46c/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872", size = 1263618, upload-time = "2025-08-10T21:25:59.857Z" },
- { url = "https://files.pythonhosted.org/packages/a3/af/db1509a9e79dbf4c260ce0cfa3903ea8945f6240e9e59d1e4deb731b1a40/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26", size = 1317437, upload-time = "2025-08-10T21:26:01.105Z" },
- { url = "https://files.pythonhosted.org/packages/e0/f2/3ea5ee5d52abacdd12013a94130436e19969fa183faa1e7c7fbc89e9a42f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028", size = 2195742, upload-time = "2025-08-10T21:26:02.675Z" },
- { url = "https://files.pythonhosted.org/packages/6f/9b/1efdd3013c2d9a2566aa6a337e9923a00590c516add9a1e89a768a3eb2fc/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771", size = 2290810, upload-time = "2025-08-10T21:26:04.009Z" },
- { url = "https://files.pythonhosted.org/packages/fb/e5/cfdc36109ae4e67361f9bc5b41323648cb24a01b9ade18784657e022e65f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a", size = 2461579, upload-time = "2025-08-10T21:26:05.317Z" },
- { url = "https://files.pythonhosted.org/packages/62/86/b589e5e86c7610842213994cdea5add00960076bef4ae290c5fa68589cac/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464", size = 2268071, upload-time = "2025-08-10T21:26:06.686Z" },
- { url = "https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2", size = 73840, upload-time = "2025-08-10T21:26:07.94Z" },
- { url = "https://files.pythonhosted.org/packages/e2/2d/16e0581daafd147bc11ac53f032a2b45eabac897f42a338d0a13c1e5c436/kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7", size = 65159, upload-time = "2025-08-10T21:26:09.048Z" },
- { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" },
- { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" },
- { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" },
- { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" },
- { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" },
- { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" },
- { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" },
- { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" },
- { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" },
- { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" },
- { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" },
- { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" },
- { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" },
- { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" },
- { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" },
- { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" },
- { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" },
- { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" },
- { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" },
- { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" },
- { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" },
- { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" },
- { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" },
- { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" },
- { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" },
- { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" },
- { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" },
- { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" },
- { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" },
- { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" },
- { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" },
- { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" },
- { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" },
- { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" },
- { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" },
- { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" },
- { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" },
- { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" },
- { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" },
- { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" },
- { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" },
- { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" },
- { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" },
- { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" },
- { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" },
- { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" },
- { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" },
- { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" },
- { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" },
- { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" },
- { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" },
- { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" },
- { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" },
- { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" },
- { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" },
- { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" },
- { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" },
- { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" },
- { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" },
- { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" },
- { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" },
- { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" },
- { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" },
- { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" },
- { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183, upload-time = "2025-08-10T21:27:37.669Z" },
- { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675, upload-time = "2025-08-10T21:27:39.031Z" },
- { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277, upload-time = "2025-08-10T21:27:40.129Z" },
- { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994, upload-time = "2025-08-10T21:27:41.181Z" },
- { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744, upload-time = "2025-08-10T21:27:42.254Z" },
- { url = "https://files.pythonhosted.org/packages/a3/0f/36d89194b5a32c054ce93e586d4049b6c2c22887b0eb229c61c68afd3078/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5", size = 60104, upload-time = "2025-08-10T21:27:43.287Z" },
- { url = "https://files.pythonhosted.org/packages/52/ba/4ed75f59e4658fd21fe7dde1fee0ac397c678ec3befba3fe6482d987af87/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa", size = 58592, upload-time = "2025-08-10T21:27:44.314Z" },
- { url = "https://files.pythonhosted.org/packages/33/01/a8ea7c5ea32a9b45ceeaee051a04c8ed4320f5add3c51bfa20879b765b70/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2", size = 80281, upload-time = "2025-08-10T21:27:45.369Z" },
- { url = "https://files.pythonhosted.org/packages/da/e3/dbd2ecdce306f1d07a1aaf324817ee993aab7aee9db47ceac757deabafbe/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f", size = 78009, upload-time = "2025-08-10T21:27:46.376Z" },
- { url = "https://files.pythonhosted.org/packages/da/e9/0d4add7873a73e462aeb45c036a2dead2562b825aa46ba326727b3f31016/kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1", size = 73929, upload-time = "2025-08-10T21:27:48.236Z" },
-]
-
[[package]]
name = "lxml"
version = "6.1.1"
@@ -1786,24 +1061,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/43/af/4b3891eb0a49d6cfd5cbf3e9bf514c943afc2b0f13e2c57cc57cd88ecc21/markdown2-2.5.5-py3-none-any.whl", hash = "sha256:be798587e09d1f52d2e4d96a649c4b82a778c75f9929aad52a2c95747fa26941", size = 56250, upload-time = "2026-03-02T20:46:52.032Z" },
]
-[[package]]
-name = "markdownlit"
-version = "0.0.7"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "favicon" },
- { name = "htbuilder" },
- { name = "lxml" },
- { name = "markdown" },
- { name = "pymdown-extensions" },
- { name = "streamlit" },
- { name = "streamlit-extras" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/13/ab/c6eca20098fc7464df4f1626c65ca07b76903c5e2e9fa60763037e3554a4/markdownlit-0.0.7.tar.gz", hash = "sha256:553e2db454e2be4567caebef5176c98a40a7e24f7ea9c2fe8a1f05c1d9ea4005", size = 14289, upload-time = "2023-04-17T21:19:21.399Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5a/95/07e666469473043e16f263592c42e966e8b9a61fcc785ed39f48751adeb0/markdownlit-0.0.7-py3-none-any.whl", hash = "sha256:b58bb539dcb52e0b040ab2fed32f1f3146cbb2746dc3812940d9dd359c378bb6", size = 15266, upload-time = "2023-04-17T21:19:18.229Z" },
-]
-
[[package]]
name = "markupsafe"
version = "3.0.3"
@@ -1889,81 +1146,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
]
-[[package]]
-name = "matplotlib"
-version = "3.10.8"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "cycler" },
- { name = "fonttools" },
- { name = "kiwisolver" },
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "packaging" },
- { name = "pillow" },
- { name = "pyparsing" },
- { name = "python-dateutil" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" },
- { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" },
- { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" },
- { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" },
- { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" },
- { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" },
- { url = "https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160", size = 8251215, upload-time = "2025-12-10T22:55:16.175Z" },
- { url = "https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78", size = 8139625, upload-time = "2025-12-10T22:55:17.712Z" },
- { url = "https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4", size = 8712614, upload-time = "2025-12-10T22:55:20.8Z" },
- { url = "https://files.pythonhosted.org/packages/5a/f4/b8347351da9a5b3f41e26cf547252d861f685c6867d179a7c9d60ad50189/matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2", size = 9540997, upload-time = "2025-12-10T22:55:23.258Z" },
- { url = "https://files.pythonhosted.org/packages/9e/c0/c7b914e297efe0bc36917bf216b2acb91044b91e930e878ae12981e461e5/matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6", size = 9596825, upload-time = "2025-12-10T22:55:25.217Z" },
- { url = "https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9", size = 8135090, upload-time = "2025-12-10T22:55:27.162Z" },
- { url = "https://files.pythonhosted.org/packages/89/dd/a0b6588f102beab33ca6f5218b31725216577b2a24172f327eaf6417d5c9/matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2", size = 8012377, upload-time = "2025-12-10T22:55:29.185Z" },
- { url = "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", size = 8260453, upload-time = "2025-12-10T22:55:30.709Z" },
- { url = "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", size = 8148321, upload-time = "2025-12-10T22:55:33.265Z" },
- { url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" },
- { url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" },
- { url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" },
- { url = "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", size = 8142717, upload-time = "2025-12-10T22:55:41.103Z" },
- { url = "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", size = 8012751, upload-time = "2025-12-10T22:55:42.684Z" },
- { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" },
- { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" },
- { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" },
- { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" },
- { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" },
- { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" },
- { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" },
- { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" },
- { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" },
- { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" },
- { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" },
- { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" },
- { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" },
- { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" },
- { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" },
- { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" },
- { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" },
- { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" },
- { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" },
- { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" },
- { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" },
- { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" },
- { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" },
- { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" },
- { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" },
- { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" },
- { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" },
- { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" },
- { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" },
- { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" },
- { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" },
- { url = "https://files.pythonhosted.org/packages/04/30/3afaa31c757f34b7725ab9d2ba8b48b5e89c2019c003e7d0ead143aabc5a/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1", size = 8249198, upload-time = "2025-12-10T22:56:45.584Z" },
- { url = "https://files.pythonhosted.org/packages/48/2f/6334aec331f57485a642a7c8be03cb286f29111ae71c46c38b363230063c/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a", size = 8136817, upload-time = "2025-12-10T22:56:47.339Z" },
- { url = "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", size = 8713867, upload-time = "2025-12-10T22:56:48.954Z" },
-]
-
[[package]]
name = "mdurl"
version = "0.1.2"
@@ -2119,15 +1301,6 @@ wheels = [
{ 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]]
-name = "mpmath"
-version = "1.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
-]
-
[[package]]
name = "multidict"
version = "6.7.1"
@@ -2631,104 +1804,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" },
]
-[[package]]
-name = "pillow"
-version = "12.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d0/02/d52c733a2452ef1ffcc123b68e6606d07276b0e358db70eabad7e40042b7/pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9", size = 46977283, upload-time = "2026-01-02T09:13:29.892Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fe/41/f73d92b6b883a579e79600d391f2e21cb0df767b2714ecbd2952315dfeef/pillow-12.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:fb125d860738a09d363a88daa0f59c4533529a90e564785e20fe875b200b6dbd", size = 5304089, upload-time = "2026-01-02T09:10:24.953Z" },
- { url = "https://files.pythonhosted.org/packages/94/55/7aca2891560188656e4a91ed9adba305e914a4496800da6b5c0a15f09edf/pillow-12.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cad302dc10fac357d3467a74a9561c90609768a6f73a1923b0fd851b6486f8b0", size = 4657815, upload-time = "2026-01-02T09:10:27.063Z" },
- { url = "https://files.pythonhosted.org/packages/e9/d2/b28221abaa7b4c40b7dba948f0f6a708bd7342c4d47ce342f0ea39643974/pillow-12.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a40905599d8079e09f25027423aed94f2823adaf2868940de991e53a449e14a8", size = 6222593, upload-time = "2026-01-02T09:10:29.115Z" },
- { url = "https://files.pythonhosted.org/packages/71/b8/7a61fb234df6a9b0b479f69e66901209d89ff72a435b49933f9122f94cac/pillow-12.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:92a7fe4225365c5e3a8e598982269c6d6698d3e783b3b1ae979e7819f9cd55c1", size = 8027579, upload-time = "2026-01-02T09:10:31.182Z" },
- { url = "https://files.pythonhosted.org/packages/ea/51/55c751a57cc524a15a0e3db20e5cde517582359508d62305a627e77fd295/pillow-12.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f10c98f49227ed8383d28174ee95155a675c4ed7f85e2e573b04414f7e371bda", size = 6335760, upload-time = "2026-01-02T09:10:33.02Z" },
- { url = "https://files.pythonhosted.org/packages/dc/7c/60e3e6f5e5891a1a06b4c910f742ac862377a6fe842f7184df4a274ce7bf/pillow-12.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8637e29d13f478bc4f153d8daa9ffb16455f0a6cb287da1b432fdad2bfbd66c7", size = 7027127, upload-time = "2026-01-02T09:10:35.009Z" },
- { url = "https://files.pythonhosted.org/packages/06/37/49d47266ba50b00c27ba63a7c898f1bb41a29627ced8c09e25f19ebec0ff/pillow-12.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:21e686a21078b0f9cb8c8a961d99e6a4ddb88e0fc5ea6e130172ddddc2e5221a", size = 6449896, upload-time = "2026-01-02T09:10:36.793Z" },
- { url = "https://files.pythonhosted.org/packages/f9/e5/67fd87d2913902462cd9b79c6211c25bfe95fcf5783d06e1367d6d9a741f/pillow-12.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2415373395a831f53933c23ce051021e79c8cd7979822d8cc478547a3f4da8ef", size = 7151345, upload-time = "2026-01-02T09:10:39.064Z" },
- { url = "https://files.pythonhosted.org/packages/bd/15/f8c7abf82af68b29f50d77c227e7a1f87ce02fdc66ded9bf603bc3b41180/pillow-12.1.0-cp310-cp310-win32.whl", hash = "sha256:e75d3dba8fc1ddfec0cd752108f93b83b4f8d6ab40e524a95d35f016b9683b09", size = 6325568, upload-time = "2026-01-02T09:10:41.035Z" },
- { url = "https://files.pythonhosted.org/packages/d4/24/7d1c0e160b6b5ac2605ef7d8be537e28753c0db5363d035948073f5513d7/pillow-12.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:64efdf00c09e31efd754448a383ea241f55a994fd079866b92d2bbff598aad91", size = 7032367, upload-time = "2026-01-02T09:10:43.09Z" },
- { url = "https://files.pythonhosted.org/packages/f4/03/41c038f0d7a06099254c60f618d0ec7be11e79620fc23b8e85e5b31d9a44/pillow-12.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:f188028b5af6b8fb2e9a76ac0f841a575bd1bd396e46ef0840d9b88a48fdbcea", size = 2452345, upload-time = "2026-01-02T09:10:44.795Z" },
- { url = "https://files.pythonhosted.org/packages/43/c4/bf8328039de6cc22182c3ef007a2abfbbdab153661c0a9aa78af8d706391/pillow-12.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3", size = 5304057, upload-time = "2026-01-02T09:10:46.627Z" },
- { url = "https://files.pythonhosted.org/packages/43/06/7264c0597e676104cc22ca73ee48f752767cd4b1fe084662620b17e10120/pillow-12.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0", size = 4657811, upload-time = "2026-01-02T09:10:49.548Z" },
- { url = "https://files.pythonhosted.org/packages/72/64/f9189e44474610daf83da31145fa56710b627b5c4c0b9c235e34058f6b31/pillow-12.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40a8e3b9e8773876d6e30daed22f016509e3987bab61b3b7fe309d7019a87451", size = 6232243, upload-time = "2026-01-02T09:10:51.62Z" },
- { url = "https://files.pythonhosted.org/packages/ef/30/0df458009be6a4caca4ca2c52975e6275c387d4e5c95544e34138b41dc86/pillow-12.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800429ac32c9b72909c671aaf17ecd13110f823ddb7db4dfef412a5587c2c24e", size = 8037872, upload-time = "2026-01-02T09:10:53.446Z" },
- { url = "https://files.pythonhosted.org/packages/e4/86/95845d4eda4f4f9557e25381d70876aa213560243ac1a6d619c46caaedd9/pillow-12.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b022eaaf709541b391ee069f0022ee5b36c709df71986e3f7be312e46f42c84", size = 6345398, upload-time = "2026-01-02T09:10:55.426Z" },
- { url = "https://files.pythonhosted.org/packages/5c/1f/8e66ab9be3aaf1435bc03edd1ebdf58ffcd17f7349c1d970cafe87af27d9/pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0", size = 7034667, upload-time = "2026-01-02T09:10:57.11Z" },
- { url = "https://files.pythonhosted.org/packages/f9/f6/683b83cb9b1db1fb52b87951b1c0b99bdcfceaa75febf11406c19f82cb5e/pillow-12.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d70347c8a5b7ccd803ec0c85c8709f036e6348f1e6a5bf048ecd9c64d3550b8b", size = 6458743, upload-time = "2026-01-02T09:10:59.331Z" },
- { url = "https://files.pythonhosted.org/packages/9a/7d/de833d63622538c1d58ce5395e7c6cb7e7dce80decdd8bde4a484e095d9f/pillow-12.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fcc52d86ce7a34fd17cb04e87cfdb164648a3662a6f20565910a99653d66c18", size = 7159342, upload-time = "2026-01-02T09:11:01.82Z" },
- { url = "https://files.pythonhosted.org/packages/8c/40/50d86571c9e5868c42b81fe7da0c76ca26373f3b95a8dd675425f4a92ec1/pillow-12.1.0-cp311-cp311-win32.whl", hash = "sha256:3ffaa2f0659e2f740473bcf03c702c39a8d4b2b7ffc629052028764324842c64", size = 6328655, upload-time = "2026-01-02T09:11:04.556Z" },
- { url = "https://files.pythonhosted.org/packages/6c/af/b1d7e301c4cd26cd45d4af884d9ee9b6fab893b0ad2450d4746d74a6968c/pillow-12.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:806f3987ffe10e867bab0ddad45df1148a2b98221798457fa097ad85d6e8bc75", size = 7031469, upload-time = "2026-01-02T09:11:06.538Z" },
- { url = "https://files.pythonhosted.org/packages/48/36/d5716586d887fb2a810a4a61518a327a1e21c8b7134c89283af272efe84b/pillow-12.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9f5fefaca968e700ad1a4a9de98bf0869a94e397fe3524c4c9450c1445252304", size = 2452515, upload-time = "2026-01-02T09:11:08.226Z" },
- { url = "https://files.pythonhosted.org/packages/20/31/dc53fe21a2f2996e1b7d92bf671cdb157079385183ef7c1ae08b485db510/pillow-12.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b", size = 5262642, upload-time = "2026-01-02T09:11:10.138Z" },
- { url = "https://files.pythonhosted.org/packages/ab/c1/10e45ac9cc79419cedf5121b42dcca5a50ad2b601fa080f58c22fb27626e/pillow-12.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551", size = 4657464, upload-time = "2026-01-02T09:11:12.319Z" },
- { url = "https://files.pythonhosted.org/packages/ad/26/7b82c0ab7ef40ebede7a97c72d473bda5950f609f8e0c77b04af574a0ddb/pillow-12.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208", size = 6234878, upload-time = "2026-01-02T09:11:14.096Z" },
- { url = "https://files.pythonhosted.org/packages/76/25/27abc9792615b5e886ca9411ba6637b675f1b77af3104710ac7353fe5605/pillow-12.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5", size = 8044868, upload-time = "2026-01-02T09:11:15.903Z" },
- { url = "https://files.pythonhosted.org/packages/0a/ea/f200a4c36d836100e7bc738fc48cd963d3ba6372ebc8298a889e0cfc3359/pillow-12.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661", size = 6349468, upload-time = "2026-01-02T09:11:17.631Z" },
- { url = "https://files.pythonhosted.org/packages/11/8f/48d0b77ab2200374c66d344459b8958c86693be99526450e7aee714e03e4/pillow-12.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17", size = 7041518, upload-time = "2026-01-02T09:11:19.389Z" },
- { url = "https://files.pythonhosted.org/packages/1d/23/c281182eb986b5d31f0a76d2a2c8cd41722d6fb8ed07521e802f9bba52de/pillow-12.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670", size = 6462829, upload-time = "2026-01-02T09:11:21.28Z" },
- { url = "https://files.pythonhosted.org/packages/25/ef/7018273e0faac099d7b00982abdcc39142ae6f3bd9ceb06de09779c4a9d6/pillow-12.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616", size = 7166756, upload-time = "2026-01-02T09:11:23.559Z" },
- { url = "https://files.pythonhosted.org/packages/8f/c8/993d4b7ab2e341fe02ceef9576afcf5830cdec640be2ac5bee1820d693d4/pillow-12.1.0-cp312-cp312-win32.whl", hash = "sha256:aa0c9cc0b82b14766a99fbe6084409972266e82f459821cd26997a488a7261a7", size = 6328770, upload-time = "2026-01-02T09:11:25.661Z" },
- { url = "https://files.pythonhosted.org/packages/a7/87/90b358775a3f02765d87655237229ba64a997b87efa8ccaca7dd3e36e7a7/pillow-12.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:d70534cea9e7966169ad29a903b99fc507e932069a881d0965a1a84bb57f6c6d", size = 7033406, upload-time = "2026-01-02T09:11:27.474Z" },
- { url = "https://files.pythonhosted.org/packages/5d/cf/881b457eccacac9e5b2ddd97d5071fb6d668307c57cbf4e3b5278e06e536/pillow-12.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:65b80c1ee7e14a87d6a068dd3b0aea268ffcabfe0498d38661b00c5b4b22e74c", size = 2452612, upload-time = "2026-01-02T09:11:29.309Z" },
- { url = "https://files.pythonhosted.org/packages/dd/c7/2530a4aa28248623e9d7f27316b42e27c32ec410f695929696f2e0e4a778/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1", size = 4062543, upload-time = "2026-01-02T09:11:31.566Z" },
- { url = "https://files.pythonhosted.org/packages/8f/1f/40b8eae823dc1519b87d53c30ed9ef085506b05281d313031755c1705f73/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179", size = 4138373, upload-time = "2026-01-02T09:11:33.367Z" },
- { url = "https://files.pythonhosted.org/packages/d4/77/6fa60634cf06e52139fd0e89e5bbf055e8166c691c42fb162818b7fda31d/pillow-12.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0", size = 3601241, upload-time = "2026-01-02T09:11:35.011Z" },
- { url = "https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587", size = 5262410, upload-time = "2026-01-02T09:11:36.682Z" },
- { url = "https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac", size = 4657312, upload-time = "2026-01-02T09:11:38.535Z" },
- { url = "https://files.pythonhosted.org/packages/1d/fd/f5a0896839762885b3376ff04878f86ab2b097c2f9a9cdccf4eda8ba8dc0/pillow-12.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b", size = 6232605, upload-time = "2026-01-02T09:11:40.602Z" },
- { url = "https://files.pythonhosted.org/packages/98/aa/938a09d127ac1e70e6ed467bd03834350b33ef646b31edb7452d5de43792/pillow-12.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea", size = 8041617, upload-time = "2026-01-02T09:11:42.721Z" },
- { url = "https://files.pythonhosted.org/packages/17/e8/538b24cb426ac0186e03f80f78bc8dc7246c667f58b540bdd57c71c9f79d/pillow-12.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c", size = 6346509, upload-time = "2026-01-02T09:11:44.955Z" },
- { url = "https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc", size = 7038117, upload-time = "2026-01-02T09:11:46.736Z" },
- { url = "https://files.pythonhosted.org/packages/c7/a2/d40308cf86eada842ca1f3ffa45d0ca0df7e4ab33c83f81e73f5eaed136d/pillow-12.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644", size = 6460151, upload-time = "2026-01-02T09:11:48.625Z" },
- { url = "https://files.pythonhosted.org/packages/f1/88/f5b058ad6453a085c5266660a1417bdad590199da1b32fb4efcff9d33b05/pillow-12.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c", size = 7164534, upload-time = "2026-01-02T09:11:50.445Z" },
- { url = "https://files.pythonhosted.org/packages/19/ce/c17334caea1db789163b5d855a5735e47995b0b5dc8745e9a3605d5f24c0/pillow-12.1.0-cp313-cp313-win32.whl", hash = "sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171", size = 6332551, upload-time = "2026-01-02T09:11:52.234Z" },
- { url = "https://files.pythonhosted.org/packages/e5/07/74a9d941fa45c90a0d9465098fe1ec85de3e2afbdc15cc4766622d516056/pillow-12.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a", size = 7040087, upload-time = "2026-01-02T09:11:54.822Z" },
- { url = "https://files.pythonhosted.org/packages/88/09/c99950c075a0e9053d8e880595926302575bc742b1b47fe1bbcc8d388d50/pillow-12.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45", size = 2452470, upload-time = "2026-01-02T09:11:56.522Z" },
- { url = "https://files.pythonhosted.org/packages/b5/ba/970b7d85ba01f348dee4d65412476321d40ee04dcb51cd3735b9dc94eb58/pillow-12.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d", size = 5264816, upload-time = "2026-01-02T09:11:58.227Z" },
- { url = "https://files.pythonhosted.org/packages/10/60/650f2fb55fdba7a510d836202aa52f0baac633e50ab1cf18415d332188fb/pillow-12.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0", size = 4660472, upload-time = "2026-01-02T09:12:00.798Z" },
- { url = "https://files.pythonhosted.org/packages/2b/c0/5273a99478956a099d533c4f46cbaa19fd69d606624f4334b85e50987a08/pillow-12.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554", size = 6268974, upload-time = "2026-01-02T09:12:02.572Z" },
- { url = "https://files.pythonhosted.org/packages/b4/26/0bf714bc2e73d5267887d47931d53c4ceeceea6978148ed2ab2a4e6463c4/pillow-12.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e", size = 8073070, upload-time = "2026-01-02T09:12:04.75Z" },
- { url = "https://files.pythonhosted.org/packages/43/cf/1ea826200de111a9d65724c54f927f3111dc5ae297f294b370a670c17786/pillow-12.1.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82", size = 6380176, upload-time = "2026-01-02T09:12:06.626Z" },
- { url = "https://files.pythonhosted.org/packages/03/e0/7938dd2b2013373fd85d96e0f38d62b7a5a262af21ac274250c7ca7847c9/pillow-12.1.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4", size = 7067061, upload-time = "2026-01-02T09:12:08.624Z" },
- { url = "https://files.pythonhosted.org/packages/86/ad/a2aa97d37272a929a98437a8c0ac37b3cf012f4f8721e1bd5154699b2518/pillow-12.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0", size = 6491824, upload-time = "2026-01-02T09:12:10.488Z" },
- { url = "https://files.pythonhosted.org/packages/a4/44/80e46611b288d51b115826f136fb3465653c28f491068a72d3da49b54cd4/pillow-12.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b", size = 7190911, upload-time = "2026-01-02T09:12:12.772Z" },
- { url = "https://files.pythonhosted.org/packages/86/77/eacc62356b4cf81abe99ff9dbc7402750044aed02cfd6a503f7c6fc11f3e/pillow-12.1.0-cp313-cp313t-win32.whl", hash = "sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65", size = 6336445, upload-time = "2026-01-02T09:12:14.775Z" },
- { url = "https://files.pythonhosted.org/packages/e7/3c/57d81d0b74d218706dafccb87a87ea44262c43eef98eb3b164fd000e0491/pillow-12.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0", size = 7045354, upload-time = "2026-01-02T09:12:16.599Z" },
- { url = "https://files.pythonhosted.org/packages/ac/82/8b9b97bba2e3576a340f93b044a3a3a09841170ab4c1eb0d5c93469fd32f/pillow-12.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8", size = 2454547, upload-time = "2026-01-02T09:12:18.704Z" },
- { url = "https://files.pythonhosted.org/packages/8c/87/bdf971d8bbcf80a348cc3bacfcb239f5882100fe80534b0ce67a784181d8/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91", size = 4062533, upload-time = "2026-01-02T09:12:20.791Z" },
- { url = "https://files.pythonhosted.org/packages/ff/4f/5eb37a681c68d605eb7034c004875c81f86ec9ef51f5be4a63eadd58859a/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796", size = 4138546, upload-time = "2026-01-02T09:12:23.664Z" },
- { url = "https://files.pythonhosted.org/packages/11/6d/19a95acb2edbace40dcd582d077b991646b7083c41b98da4ed7555b59733/pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd", size = 3601163, upload-time = "2026-01-02T09:12:26.338Z" },
- { url = "https://files.pythonhosted.org/packages/fc/36/2b8138e51cb42e4cc39c3297713455548be855a50558c3ac2beebdc251dd/pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13", size = 5266086, upload-time = "2026-01-02T09:12:28.782Z" },
- { url = "https://files.pythonhosted.org/packages/53/4b/649056e4d22e1caa90816bf99cef0884aed607ed38075bd75f091a607a38/pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e", size = 4657344, upload-time = "2026-01-02T09:12:31.117Z" },
- { url = "https://files.pythonhosted.org/packages/6c/6b/c5742cea0f1ade0cd61485dc3d81f05261fc2276f537fbdc00802de56779/pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643", size = 6232114, upload-time = "2026-01-02T09:12:32.936Z" },
- { url = "https://files.pythonhosted.org/packages/bf/8f/9f521268ce22d63991601aafd3d48d5ff7280a246a1ef62d626d67b44064/pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5", size = 8042708, upload-time = "2026-01-02T09:12:34.78Z" },
- { url = "https://files.pythonhosted.org/packages/1a/eb/257f38542893f021502a1bbe0c2e883c90b5cff26cc33b1584a841a06d30/pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de", size = 6347762, upload-time = "2026-01-02T09:12:36.748Z" },
- { url = "https://files.pythonhosted.org/packages/c4/5a/8ba375025701c09b309e8d5163c5a4ce0102fa86bbf8800eb0d7ac87bc51/pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9", size = 7039265, upload-time = "2026-01-02T09:12:39.082Z" },
- { url = "https://files.pythonhosted.org/packages/cf/dc/cf5e4cdb3db533f539e88a7bbf9f190c64ab8a08a9bc7a4ccf55067872e4/pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a", size = 6462341, upload-time = "2026-01-02T09:12:40.946Z" },
- { url = "https://files.pythonhosted.org/packages/d0/47/0291a25ac9550677e22eda48510cfc4fa4b2ef0396448b7fbdc0a6946309/pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a", size = 7165395, upload-time = "2026-01-02T09:12:42.706Z" },
- { url = "https://files.pythonhosted.org/packages/4f/4c/e005a59393ec4d9416be06e6b45820403bb946a778e39ecec62f5b2b991e/pillow-12.1.0-cp314-cp314-win32.whl", hash = "sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030", size = 6431413, upload-time = "2026-01-02T09:12:44.944Z" },
- { url = "https://files.pythonhosted.org/packages/1c/af/f23697f587ac5f9095d67e31b81c95c0249cd461a9798a061ed6709b09b5/pillow-12.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94", size = 7176779, upload-time = "2026-01-02T09:12:46.727Z" },
- { url = "https://files.pythonhosted.org/packages/b3/36/6a51abf8599232f3e9afbd16d52829376a68909fe14efe29084445db4b73/pillow-12.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4", size = 2543105, upload-time = "2026-01-02T09:12:49.243Z" },
- { url = "https://files.pythonhosted.org/packages/82/54/2e1dd20c8749ff225080d6ba465a0cab4387f5db0d1c5fb1439e2d99923f/pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2", size = 5268571, upload-time = "2026-01-02T09:12:51.11Z" },
- { url = "https://files.pythonhosted.org/packages/57/61/571163a5ef86ec0cf30d265ac2a70ae6fc9e28413d1dc94fa37fae6bda89/pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61", size = 4660426, upload-time = "2026-01-02T09:12:52.865Z" },
- { url = "https://files.pythonhosted.org/packages/5e/e1/53ee5163f794aef1bf84243f755ee6897a92c708505350dd1923f4afec48/pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51", size = 6269908, upload-time = "2026-01-02T09:12:54.884Z" },
- { url = "https://files.pythonhosted.org/packages/bc/0b/b4b4106ff0ee1afa1dc599fde6ab230417f800279745124f6c50bcffed8e/pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc", size = 8074733, upload-time = "2026-01-02T09:12:56.802Z" },
- { url = "https://files.pythonhosted.org/packages/19/9f/80b411cbac4a732439e629a26ad3ef11907a8c7fc5377b7602f04f6fe4e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14", size = 6381431, upload-time = "2026-01-02T09:12:58.823Z" },
- { url = "https://files.pythonhosted.org/packages/8f/b7/d65c45db463b66ecb6abc17c6ba6917a911202a07662247e1355ce1789e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8", size = 7068529, upload-time = "2026-01-02T09:13:00.885Z" },
- { url = "https://files.pythonhosted.org/packages/50/96/dfd4cd726b4a45ae6e3c669fc9e49deb2241312605d33aba50499e9d9bd1/pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924", size = 6492981, upload-time = "2026-01-02T09:13:03.314Z" },
- { url = "https://files.pythonhosted.org/packages/4d/1c/b5dc52cf713ae46033359c5ca920444f18a6359ce1020dd3e9c553ea5bc6/pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef", size = 7191878, upload-time = "2026-01-02T09:13:05.276Z" },
- { url = "https://files.pythonhosted.org/packages/53/26/c4188248bd5edaf543864fe4834aebe9c9cb4968b6f573ce014cc42d0720/pillow-12.1.0-cp314-cp314t-win32.whl", hash = "sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988", size = 6438703, upload-time = "2026-01-02T09:13:07.491Z" },
- { url = "https://files.pythonhosted.org/packages/b8/0e/69ed296de8ea05cb03ee139cee600f424ca166e632567b2d66727f08c7ed/pillow-12.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6", size = 7182927, upload-time = "2026-01-02T09:13:09.841Z" },
- { url = "https://files.pythonhosted.org/packages/fc/f5/68334c015eed9b5cff77814258717dec591ded209ab5b6fb70e2ae873d1d/pillow-12.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831", size = 2545104, upload-time = "2026-01-02T09:13:12.068Z" },
- { url = "https://files.pythonhosted.org/packages/8b/bc/224b1d98cffd7164b14707c91aac83c07b047fbd8f58eba4066a3e53746a/pillow-12.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ca94b6aac0d7af2a10ba08c0f888b3d5114439b6b3ef39968378723622fed377", size = 5228605, upload-time = "2026-01-02T09:13:14.084Z" },
- { url = "https://files.pythonhosted.org/packages/0c/ca/49ca7769c4550107de049ed85208240ba0f330b3f2e316f24534795702ce/pillow-12.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:351889afef0f485b84078ea40fe33727a0492b9af3904661b0abbafee0355b72", size = 4622245, upload-time = "2026-01-02T09:13:15.964Z" },
- { url = "https://files.pythonhosted.org/packages/73/48/fac807ce82e5955bcc2718642b94b1bd22a82a6d452aea31cbb678cddf12/pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb0984b30e973f7e2884362b7d23d0a348c7143ee559f38ef3eaab640144204c", size = 5247593, upload-time = "2026-01-02T09:13:17.913Z" },
- { url = "https://files.pythonhosted.org/packages/d2/95/3e0742fe358c4664aed4fd05d5f5373dcdad0b27af52aa0972568541e3f4/pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84cabc7095dd535ca934d57e9ce2a72ffd216e435a84acb06b2277b1de2689bd", size = 6989008, upload-time = "2026-01-02T09:13:20.083Z" },
- { url = "https://files.pythonhosted.org/packages/5a/74/fe2ac378e4e202e56d50540d92e1ef4ff34ed687f3c60f6a121bcf99437e/pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53d8b764726d3af1a138dd353116f774e3862ec7e3794e0c8781e30db0f35dfc", size = 5313824, upload-time = "2026-01-02T09:13:22.405Z" },
- { url = "https://files.pythonhosted.org/packages/f3/77/2a60dee1adee4e2655ac328dd05c02a955c1cd683b9f1b82ec3feb44727c/pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5da841d81b1a05ef940a8567da92decaa15bc4d7dedb540a8c219ad83d91808a", size = 5963278, upload-time = "2026-01-02T09:13:24.706Z" },
- { url = "https://files.pythonhosted.org/packages/2d/71/64e9b1c7f04ae0027f788a248e6297d7fcc29571371fe7d45495a78172c0/pillow-12.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:75af0b4c229ac519b155028fa1be632d812a519abba9b46b20e50c6caa184f19", size = 7029809, upload-time = "2026-01-02T09:13:26.541Z" },
-]
-
[[package]]
name = "platformdirs"
version = "4.5.1"
@@ -2760,15 +1835,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
-[[package]]
-name = "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 = "propcache"
version = "0.5.2"
@@ -2897,21 +1963,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" },
]
-[[package]]
-name = "protobuf"
-version = "6.33.5"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" },
- { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" },
- { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" },
- { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" },
- { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" },
- { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" },
- { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" },
-]
-
[[package]]
name = "psutil"
version = "7.2.2"
@@ -2997,15 +2048,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/72/9c/47693463894b610f8439b2e970b82ef81e9599c757bf2049365e40ff963c/pyarrow-23.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:427deac1f535830a744a4f04a6ac183a64fcac4341b3f618e693c41b7b98d2b0", size = 28338905, upload-time = "2026-01-18T16:19:32.93Z" },
]
-[[package]]
-name = "pycparser"
-version = "3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
-]
-
[[package]]
name = "pydantic"
version = "2.13.4"
@@ -3137,20 +2179,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" },
]
-[[package]]
-name = "pydeck"
-version = "0.9.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" },
-]
-
[[package]]
name = "pygments"
version = "2.20.0"
@@ -3160,15 +2188,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
]
-[[package]]
-name = "pyjwt"
-version = "2.11.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" },
-]
-
[[package]]
name = "pymdown-extensions"
version = "10.21.3"
@@ -3182,28 +2201,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/85/545a951eecc270fcd688288c600017e2050a1aacb56c711d208586d3e470/pymdown_extensions-10.21.3-py3-none-any.whl", hash = "sha256:d7a5d08014fc571e80ca21dd6f854e31f94c489800350564d55d15b3c41e76b6", size = 269002, upload-time = "2026-05-13T12:57:30.296Z" },
]
-[[package]]
-name = "pyopenssl"
-version = "25.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cryptography" },
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/80/be/97b83a464498a79103036bc74d1038df4a7ef0e402cfaf4d5e113fb14759/pyopenssl-25.3.0.tar.gz", hash = "sha256:c981cb0a3fd84e8602d7afc209522773b94c1c2446a3c710a75b06fe1beae329", size = 184073, upload-time = "2025-09-17T00:32:21.037Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/81/ef2b1dfd1862567d573a4fdbc9f969067621764fbb74338496840a1d2977/pyopenssl-25.3.0-py3-none-any.whl", hash = "sha256:1fda6fc034d5e3d179d39e59c1895c9faeaf40a79de5fc4cbbfbe0d36f4a77b6", size = 57268, upload-time = "2025-09-17T00:32:19.474Z" },
-]
-
-[[package]]
-name = "pyparsing"
-version = "3.3.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
-]
-
[[package]]
name = "pytest"
version = "9.0.2"
@@ -3395,20 +2392,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" },
]
-[[package]]
-name = "referencing"
-version = "0.37.0"
-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" }
-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" },
-]
-
[[package]]
name = "requests"
version = "2.32.5"
@@ -3437,128 +2420,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" },
]
-[[package]]
-name = "rpds-py"
-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]]
name = "ruff"
version = "0.14.14"
@@ -3585,27 +2446,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" },
]
-[[package]]
-name = "s3transfer"
-version = "0.16.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "botocore" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" },
-]
-
-[[package]]
-name = "setuptools"
-version = "80.10.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/76/95/faf61eb8363f26aa7e1d762267a8d602a1b26d4f3a1e758e92cb3cb8b054/setuptools-80.10.2.tar.gz", hash = "sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70", size = 1200343, upload-time = "2026-01-25T22:38:17.252Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/94/b8/f1f62a5e3c0ad2ff1d189590bfa4c46b4f3b6e49cef6f26c6ee4e575394d/setuptools-80.10.2-py3-none-any.whl", hash = "sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173", size = 1064234, upload-time = "2026-01-25T22:38:15.216Z" },
-]
-
[[package]]
name = "simple-websocket"
version = "1.1.0"
@@ -3627,135 +2467,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
-[[package]]
-name = "smmap"
-version = "5.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" },
-]
-
-[[package]]
-name = "snowflake-connector-python"
-version = "4.2.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "asn1crypto" },
- { name = "boto3" },
- { name = "botocore" },
- { name = "certifi" },
- { name = "charset-normalizer" },
- { name = "cryptography" },
- { name = "filelock" },
- { name = "idna" },
- { name = "packaging" },
- { name = "platformdirs" },
- { name = "pyjwt" },
- { name = "pyopenssl" },
- { name = "pytz" },
- { name = "requests" },
- { name = "sortedcontainers" },
- { name = "tomlkit" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/13/d2/4ae9fc7a0df36ad0ac06bc959757dfbfc58f160f58e1d62e7cebe9901fc7/snowflake_connector_python-4.2.0.tar.gz", hash = "sha256:74b1028caee3af4550a366ef89b33de80940bbf856844dd4d788a6b7a6511aff", size = 915327, upload-time = "2026-01-07T16:44:32.541Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a4/34/2c5c059b12db84113bb01761bd3fdab3e0c0d8d4ccc0c9631be5479960c2/snowflake_connector_python-4.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e1c60e578ddcdf99b46d7c329706aa87ea98c1c877cbe50560e034cc904231e", size = 11908869, upload-time = "2026-01-07T16:44:35.243Z" },
- { url = "https://files.pythonhosted.org/packages/c9/27/07ab3485f43d92c139fefb30b68a60498b508f2e941d9191f1ec3ac42a20/snowflake_connector_python-4.2.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:cf1805be7e124aa12bdcbb6c7f7f7bd11277aa4fe4d616cfee7633617bba9651", size = 11921560, upload-time = "2026-01-07T16:44:37.995Z" },
- { url = "https://files.pythonhosted.org/packages/d5/12/ba6bb6cd26bc584637aa63f3e579cb929b9c3637fa830e43b77c2b2e8901/snowflake_connector_python-4.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b877cf5fc086818d86e289fc88453bc354df87a664e57f9b75d8dd7550d2df3", size = 2786595, upload-time = "2026-01-07T16:44:14.314Z" },
- { url = "https://files.pythonhosted.org/packages/9f/80/bf900ac5ddd5b60a72f0c3f7c276c9b0f29b375997c294f28bd746e9f721/snowflake_connector_python-4.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3654c3923b7ce88aab3be459bad3dba39fe4f989a4871421925a8a48f9a553ca", size = 2814560, upload-time = "2026-01-07T16:44:15.988Z" },
- { url = "https://files.pythonhosted.org/packages/8e/04/e070116ff779fcd16c5e25ef8b045afb8cc53b12b3494663457718a7d877/snowflake_connector_python-4.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cdaf91edf94d801fef6cb15c90ba321826b8342826a82375799319d509e6787a", size = 12059955, upload-time = "2026-01-07T16:45:05.556Z" },
- { url = "https://files.pythonhosted.org/packages/24/5f/2e3ac52d4b433e850c83f91b801b7c4e9935a4d1c4f2ea4fd0c3782c5a3d/snowflake_connector_python-4.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2971212e2bf38b19ed3d71d433102b09cda09ddca02fe4c813cb73f504a31e8", size = 11908767, upload-time = "2026-01-07T16:44:39.982Z" },
- { url = "https://files.pythonhosted.org/packages/31/f6/74d75623ed75244c4aad1722b83923c806a67f601b41314e8a6b30e160c0/snowflake_connector_python-4.2.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:786d9ad591439996ff5a6014c3730441bcfdc8c6d60f05d98f6576cb2cfa0f05", size = 11921016, upload-time = "2026-01-07T16:44:41.917Z" },
- { url = "https://files.pythonhosted.org/packages/31/53/ab0d2eed42f1309de2e7656651fdab6ae454032bcc485089ce5e0697b5c2/snowflake_connector_python-4.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74d3d2bcce62bbb7a8fb3adaae37dc2aaeb4e93549509db2f957fb704ce4aa18", size = 2797881, upload-time = "2026-01-07T16:44:17.319Z" },
- { url = "https://files.pythonhosted.org/packages/2a/6f/2aa88f57107fdf0daabd113b479ba50e22d566ae36e860d4dbe68bcb6437/snowflake_connector_python-4.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cbdffcf5b12199f3060297353e69c5a4c1fc4dfacd0062acbe9a1ace7e50882", size = 2827340, upload-time = "2026-01-07T16:44:19.434Z" },
- { url = "https://files.pythonhosted.org/packages/f4/5b/d03f1d8dfeab8c81bd1f65cad93385932789971a640db1c6369b5850cc5b/snowflake_connector_python-4.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:939e687ec4667d903b3bca3644b22946606361a2201158e137e448a6cd44605d", size = 12059905, upload-time = "2026-01-07T16:45:07.679Z" },
- { url = "https://files.pythonhosted.org/packages/3c/90/90df1e0bbc8ba22534af48518e71eb669a3bb6243989a93d59f9db9d8897/snowflake_connector_python-4.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b6e5dde4794fb190add6baee616f0f9a9b5c31502089b680a5be4441926b5173", size = 11907736, upload-time = "2026-01-07T16:44:44.598Z" },
- { url = "https://files.pythonhosted.org/packages/8e/d1/4e9015d37a869022729a146f4c7f312f089938e1f51ac7620f6961f7ce66/snowflake_connector_python-4.2.0-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:f80f180092d218b578f05da145dd2640edb3c8807264d69169bc4dfb88b8b86c", size = 11919401, upload-time = "2026-01-07T16:44:47.524Z" },
- { url = "https://files.pythonhosted.org/packages/c3/5a/c65134dedd438f9d8d6eaeb7f573cb95abe4141385a4353cfe88d8c96fb1/snowflake_connector_python-4.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94a59566d3096a662b09423770aede8f99f1d06807d7b884dba8d9f767f0b2cd", size = 2854461, upload-time = "2026-01-07T16:44:21.305Z" },
- { url = "https://files.pythonhosted.org/packages/94/6d/dd526a07042ca33ce05b8c642ef3da4a72e2cbe09e305170cb866021acd6/snowflake_connector_python-4.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11241089efc6e8d69ea1aa58bb17abe85298e66d278fed4d13381fc362f02564", size = 2887953, upload-time = "2026-01-07T16:44:23.221Z" },
- { url = "https://files.pythonhosted.org/packages/3f/e0/d2db617da5791ec03d17bfd96db6f4c867a3498c4b4d480befc6a1854522/snowflake_connector_python-4.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:823ca257d9639b5468f53a816dc5acaea7c56991f518633c9c5f0fcf0d324721", size = 12058975, upload-time = "2026-01-07T16:45:10.293Z" },
- { url = "https://files.pythonhosted.org/packages/7a/34/cb523e85f9da46e22ee3c07a4f66a090ab935a1c6e59e4e9638cf8e7bc36/snowflake_connector_python-4.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d103ab3d9175251c1e391c4a155d99faaadd6a1e3c1c36429a711862f7ab021", size = 11908616, upload-time = "2026-01-07T16:44:49.512Z" },
- { url = "https://files.pythonhosted.org/packages/5b/eb/7a5c2a4dc275048e0b0b67b6b542b4cfdf60da158af8a315e5dd1021f443/snowflake_connector_python-4.2.0-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:2db02486bf72b2d4da6338bad59c58e18d0be4026b33d62b894db8cb04de403e", size = 11920460, upload-time = "2026-01-07T16:44:51.845Z" },
- { url = "https://files.pythonhosted.org/packages/37/a2/7f85a01fc13982391166c5458f4fd1078546e6f19f9e0bb184dbf6ec5f53/snowflake_connector_python-4.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b93b0195746c7734ab66889430a418ac7fd66441c11addb683bc15e364bb77c8", size = 2820920, upload-time = "2026-01-07T16:44:24.728Z" },
- { url = "https://files.pythonhosted.org/packages/aa/80/322dafc03f77f28f1ede160e4989ae758dd27dc94529e424348865bba501/snowflake_connector_python-4.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4773949e33c2503f369c20ac8fd59697e493670fed653fea7349d465ea5a0171", size = 2854097, upload-time = "2026-01-07T16:44:26.817Z" },
- { url = "https://files.pythonhosted.org/packages/06/05/64d3de8c98f783a3065e60107519b701d1ab7ef15efefa279d338f3fba64/snowflake_connector_python-4.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:3665eae47a6ccaf00ca567936cb16d5cbdd5b9f8ab3ee3a3f072bf3c4b76986c", size = 12058956, upload-time = "2026-01-07T16:45:13.063Z" },
-]
-
-[package.optional-dependencies]
-pandas = [
- { name = "pandas" },
- { name = "pyarrow" },
-]
-
-[[package]]
-name = "snowflake-snowpark-python"
-version = "1.45.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cloudpickle" },
- { name = "protobuf" },
- { name = "python-dateutil" },
- { name = "pyyaml" },
- { name = "setuptools" },
- { name = "snowflake-connector-python" },
- { name = "typing-extensions" },
- { name = "tzlocal" },
- { name = "wheel" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/36/6a/f5d5944a3e33ae9e7d81b7f125182333e11905d30c8eac8e3dbb3c9dace8/snowflake_snowpark_python-1.45.0.tar.gz", hash = "sha256:2b557d11d0369109b6205dfafbe7b1b42b2b3ffb17f35321dcd53d6532928b80", size = 1731888, upload-time = "2026-02-02T23:10:47.926Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e5/ed/b55d5425a916529a2e4d080950a6284f9fd42cd3f9ee63d2203e306e1c97/snowflake_snowpark_python-1.45.0-py3-none-any.whl", hash = "sha256:2f5edd96c9f735a79b7aeba07cb629dacd729d9f8fbe769c2a0087da648f0693", size = 1822927, upload-time = "2026-02-02T23:10:46.144Z" },
-]
-
-[package.optional-dependencies]
-pandas = [
- { name = "snowflake-connector-python", extra = ["pandas"] },
-]
-
-[[package]]
-name = "sortedcontainers"
-version = "2.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" },
-]
-
-[[package]]
-name = "soupsieve"
-version = "2.8.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" },
-]
-
-[[package]]
-name = "st-annotated-text"
-version = "4.0.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "htbuilder" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/4d/76/c0f64a56b779bfb5c87437687a9970d12dad37b5aea2541823d0c5010818/st_annotated_text-4.0.2.tar.gz", hash = "sha256:b0134dcf734697cc3dbdb11862c4174071e7fb6f365af55a37c710d357fd88a5", size = 7915, upload-time = "2025-01-23T15:09:02.88Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d9/70/f29d4359ddfd1a0afa00065e4dac32bd4931f589185f5f0f499db9aee4fe/st_annotated_text-4.0.2-py3-none-any.whl", hash = "sha256:712c45821f020eccafad3a58c10b9d2d51d449f4db999a06308ecf39a753a4df", size = 9084, upload-time = "2025-01-23T15:09:00.863Z" },
-]
-
-[[package]]
-name = "st-theme"
-version = "1.2.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/31/84/3e2fd95f5acaf6c92a51e7f038f1120235c65bb0b8be9bf43aa9fb4267e5/st-theme-1.2.3.tar.gz", hash = "sha256:ca97aece1a48ded6e83fd742c27cb0851e1bce2100ab4b6c37c7b6e003b65b42", size = 73873, upload-time = "2024-05-03T19:25:50.657Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/30/09/cd7134f1ed5074a7d456640e7ba9a8c8e68a831837b4e7bfd9f29e5700a4/st_theme-1.2.3-py3-none-any.whl", hash = "sha256:0a54d9817dd5f8a6d7b0d071b25ae72eacf536c63a5fb97374923938021b1389", size = 75205, upload-time = "2024-05-03T19:25:47.858Z" },
-]
-
[[package]]
name = "starlette"
version = "1.6.0"
@@ -3769,216 +2480,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/cb/6a6a47d5b464bd08695d254f3da6e7986cc70c9fa5d778eda57538edfe56/starlette-1.6.0-py3-none-any.whl", hash = "sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c", size = 75969, upload-time = "2026-08-08T18:27:56.196Z" },
]
-[[package]]
-name = "streamlit"
-version = "1.53.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "altair" },
- { name = "blinker" },
- { name = "cachetools" },
- { name = "click" },
- { name = "gitpython" },
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "packaging" },
- { name = "pandas" },
- { name = "pillow" },
- { name = "protobuf" },
- { name = "pyarrow" },
- { name = "pydeck" },
- { name = "requests" },
- { name = "tenacity" },
- { name = "toml" },
- { name = "tornado" },
- { name = "typing-extensions" },
- { name = "watchdog", marker = "sys_platform != 'darwin'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/31/cc/347730d06e3950e426bf2ee06eaf9b281e18387bffa82bd69aa0f281eee3/streamlit-1.53.1.tar.gz", hash = "sha256:ae656af3b68b4bb2d669fa977606096f2021bcbaa14a454a290f8e0a37bab277", size = 8650843, upload-time = "2026-01-22T21:39:04.087Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/23/c3/5e6f6a9328d57436b658447b6f05969d435df8d31344e34ec75c3044657b/streamlit-1.53.1-py3-none-any.whl", hash = "sha256:9534d151feea485b69200dd36448f95f418c511e8c81186ceb57133bdf1443f7", size = 9111505, upload-time = "2026-01-22T21:39:01.344Z" },
-]
-
-[[package]]
-name = "streamlit-avatar"
-version = "0.1.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/b7/a2/09c6046b1934f79ce2c6d767f3c7760ce632685498e67af455a2ae962d4a/streamlit_avatar-0.1.3.tar.gz", hash = "sha256:023893bd80db5a923d397fc64d27c7a972107da4f0bea0feb99a32f9363f2691", size = 402071, upload-time = "2024-06-03T06:37:38.406Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/cf/77/4b82378aa4caf7ef7701841b25a5406b3a3bb6171c8b9eee540553843b7f/streamlit_avatar-0.1.3-py3-none-any.whl", hash = "sha256:873e6f0022635eda29d6b76e570adcbc095d8e6d23f18dd1e288720b4736364d", size = 779612, upload-time = "2024-06-03T06:37:35.671Z" },
-]
-
-[[package]]
-name = "streamlit-camera-input-live"
-version = "0.2.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/91/e1/770277a024a02916120fb396a2ec9524fba2a00d077f98295e71bbb698e7/streamlit-camera-input-live-0.2.0.tar.gz", hash = "sha256:20ceb952b98410084176fcfeb9148e02ea29033a88d4a923161ac7890cedae0f", size = 5761, upload-time = "2022-09-27T16:09:30.726Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/23/9b/1c0ea2b4cc39ff1be6ad7171ac9455203d08e725a9e101cc3e7311589842/streamlit_camera_input_live-0.2.0-py3-none-any.whl", hash = "sha256:dacb56cdedbb0d6c07e35a66b755b9145b5023e5c855c64193c3d3e73198e9be", size = 6586, upload-time = "2022-09-27T16:09:29.528Z" },
-]
-
-[[package]]
-name = "streamlit-card"
-version = "1.0.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/67/39/765f93dc09cabb8d4762714bf3a0d25892671450eda2c48e4dbdb2292d6b/streamlit_card-1.0.2.tar.gz", hash = "sha256:8001cd5edd8a6e2db36ee81f37dc645f08f78c21a2ba968403176c68b4f33cb1", size = 672326, upload-time = "2024-05-10T14:04:03.571Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/05/22/1d81bd6b2552c67f50a19501c00c7ee08022eacffb21c6900929359f7ce5/streamlit_card-1.0.2-py3-none-any.whl", hash = "sha256:f5d01ce57d6481eb3ba44e504146f56a7b82907d6700f0c19266ed6381a9c58f", size = 680848, upload-time = "2024-05-10T14:04:00.723Z" },
-]
-
-[[package]]
-name = "streamlit-embedcode"
-version = "0.1.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/61/ef/9c254c44bb5cb276bb2503b1b7101274cdc9f714206fb73d3ec59bec6085/streamlit-embedcode-0.1.2.tar.gz", hash = "sha256:22a50eb43407bab3d0ed2d4b58e89819da477cd0592ef87edbd373c286712e3a", size = 3567, upload-time = "2021-05-17T19:03:18.29Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/72/0b/5c68315b24d7565a1c51611c94124cafce5463ed869270c762552d74d96b/streamlit_embedcode-0.1.2-py3-none-any.whl", hash = "sha256:b3c9520c1b48f2eef3c702b5a967f64c9a8ff2ea8e74ebb26c0e9195965bb923", size = 3534, upload-time = "2021-05-17T19:03:17.286Z" },
-]
-
-[[package]]
-name = "streamlit-extras"
-version = "0.7.8"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "altex" },
- { name = "entrypoints" },
- { name = "htbuilder" },
- { name = "markdownlit" },
- { name = "plotly" },
- { name = "prometheus-client" },
- { name = "protobuf" },
- { name = "snowflake-snowpark-python", extra = ["pandas"], marker = "sys_platform != 'emscripten'" },
- { name = "st-annotated-text" },
- { name = "st-theme" },
- { name = "streamlit" },
- { name = "streamlit-avatar" },
- { name = "streamlit-camera-input-live" },
- { name = "streamlit-card" },
- { name = "streamlit-embedcode" },
- { name = "streamlit-faker" },
- { name = "streamlit-image-coordinates" },
- { name = "streamlit-keyup" },
- { name = "streamlit-notify" },
- { name = "streamlit-toggle-switch" },
- { name = "streamlit-vertical-slider" },
- { name = "validators" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/00/f0/9c4f3240990f4f2c9630c4741b69f13f10b2c35246264aa0c03db5c3f62d/streamlit_extras-0.7.8.tar.gz", hash = "sha256:63e482150e69fcbadfe7440477422b76a9494e6841c071749204670681d7d272", size = 284868, upload-time = "2025-09-09T13:34:12.326Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0c/be/db83a8ca4f88b7d46ed4fc6752d075e3c562e65c66f0b10f109fa58ae53e/streamlit_extras-0.7.8-py3-none-any.whl", hash = "sha256:3511c3e58e5fea8cd11e2d7eedd828f93ca44eaaf3d8a14e4aab7b8f4285e5db", size = 88784, upload-time = "2025-09-09T13:34:10.823Z" },
-]
-
-[[package]]
-name = "streamlit-faker"
-version = "0.0.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "altex" },
- { name = "faker" },
- { name = "matplotlib" },
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/65/a9/312137533f56a634d7774eaaf0c5438322ae7c6ac84429a2cfd2d5f6f841/streamlit_faker-0.0.4.tar.gz", hash = "sha256:dc94cbfafcf035b171cb989531a1f19f4241e5b3c2e61311ed91a02ed188fed0", size = 14526, upload-time = "2025-07-01T15:14:44.738Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/aa/52/19755b6a87750640a1f5232c8c80ad46df3c2afcaf25594a5884756d220c/streamlit_faker-0.0.4-py3-none-any.whl", hash = "sha256:1802e92b5f1164f1f7c640c44fd5a349415b9a7f023fa912a10e03161de08ec8", size = 14156, upload-time = "2025-07-01T15:14:36.89Z" },
-]
-
-[[package]]
-name = "streamlit-image-coordinates"
-version = "0.4.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/77/de/eaa06642d53c7d35d3759da9cc4640323afb7dcd0283e6424be1a392fbe8/streamlit_image_coordinates-0.4.0.tar.gz", hash = "sha256:3c6657df55ad48ba0bc62db4ca4374f762ada6595f390f4ca6fecf18e0a350b4", size = 5816, upload-time = "2025-08-05T16:21:46.314Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/0c/29d4ddfde865ce480b8aacbb9a764e872dfd2e141c7e6c15f075436ea807/streamlit_image_coordinates-0.4.0-py3-none-any.whl", hash = "sha256:20d482384edf2d5442b43bc738ecc52bd27c081de28b825986802b5dc6789223", size = 7592, upload-time = "2025-08-05T16:21:45.499Z" },
-]
-
-[[package]]
-name = "streamlit-keyup"
-version = "0.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/88/25/d153babd6d8dff72ee2e56976862ec7fdd7856ec7da2cb6ffd4b3b12f336/streamlit_keyup-0.3.0.tar.gz", hash = "sha256:8595a14892423243669e5d50e982853ffb7eb201b65952a48676133ab9bbc937", size = 7387, upload-time = "2025-01-02T14:48:40.245Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/14/ed/facce225c0d360b9bfa053c827029312e1b255e84d79320bda3539417b50/streamlit_keyup-0.3.0-py3-none-any.whl", hash = "sha256:ec7221617b1c832526db52859196c417578d6b4285942fbd10a0b2ff313899b3", size = 7493, upload-time = "2025-01-02T14:48:35.414Z" },
-]
-
-[[package]]
-name = "streamlit-notify"
-version = "0.3.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "packaging" },
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9e/1e/2e6f3747bd98658985403ba32e8aa91bfc9f561b479c2d9450fa0537f340/streamlit_notify-0.3.1.tar.gz", hash = "sha256:89e2f0655059571df84952a2875ce27e6e43cfea871e69da95dfcf3a23caa445", size = 15679, upload-time = "2025-07-01T15:54:32.867Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bb/71/ce3b6c85a793f1ec2d4c4ea3b70ea5a86b7554003cc179a84fd105cc9f0f/streamlit_notify-0.3.1-py3-none-any.whl", hash = "sha256:83154dd247a8daa70db8d254fe01247aa17c46101ccf40bb133a94dbee4fc958", size = 9913, upload-time = "2025-07-01T15:54:32.117Z" },
-]
-
-[[package]]
-name = "streamlit-toggle-switch"
-version = "1.0.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/12/ce/9b8b93bc87223b0680fb73ea3c877d4f02390da12a6a1a561b90ec438d0d/streamlit_toggle_switch-1.0.2.tar.gz", hash = "sha256:991b103cd3448b0f6507f8051777b996a17b4630956d5b6fa13344175b20e572", size = 627850, upload-time = "2022-10-25T22:57:42.206Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7c/bc/f3ffd4143379756c494b8cb5faf1fffae48b722fe7b05794068d05bec965/streamlit_toggle_switch-1.0.2-py3-none-any.whl", hash = "sha256:0081212d80d178bda337acf2432425e2016d757f57834b18645d4c5b928d4c0f", size = 635443, upload-time = "2022-10-25T22:57:40.055Z" },
-]
-
-[[package]]
-name = "streamlit-vertical-slider"
-version = "2.5.5"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "streamlit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/29/b6/c4596692276174e01899e4da3bdcef101451bc45f1a738aad54a0e60bcfe/streamlit_vertical_slider-2.5.5.tar.gz", hash = "sha256:d6854cf81a606f5c021df2037d2c49036df2d03ce5082a5227a2acca8322ca74", size = 635192, upload-time = "2023-12-22T03:04:41.177Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4e/3f/f16875fa650cc6f6c1bc19ef9ccae5a1a7f434ec08747181743b48c31830/streamlit_vertical_slider-2.5.5-py3-none-any.whl", hash = "sha256:8182e861444fcd69e05c05e7109a636d459560c249f1addf78b58e525a719cb6", size = 1769408, upload-time = "2023-12-22T03:04:38.527Z" },
-]
-
-[[package]]
-name = "sympy"
-version = "1.14.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mpmath" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
-]
-
-[[package]]
-name = "tenacity"
-version = "9.1.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" },
-]
-
[[package]]
name = "tinycss2"
version = "1.5.1"
@@ -3991,15 +2492,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/60/45/c7b5c3168458db837e8ceab06dc77824e18202679d0463f0e8f002143a97/tinycss2-1.5.1-py3-none-any.whl", hash = "sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661", size = 28404, upload-time = "2025-11-23T10:29:08.676Z" },
]
-[[package]]
-name = "toml"
-version = "0.10.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" },
-]
-
[[package]]
name = "tomli"
version = "2.4.0"
@@ -4054,34 +2546,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
]
-[[package]]
-name = "tomlkit"
-version = "0.14.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" },
-]
-
-[[package]]
-name = "tornado"
-version = "6.5.4"
-source = { registry = "https://pypi.org/simple" }
-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/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]]
name = "typing-extensions"
version = "4.15.0"
@@ -4112,18 +2576,6 @@ wheels = [
{ 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]]
-name = "tzlocal"
-version = "5.3.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "tzdata", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" },
-]
-
[[package]]
name = "urllib3"
version = "2.6.3"
@@ -4202,15 +2654,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" },
]
-[[package]]
-name = "validators"
-version = "0.35.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399, upload-time = "2025-05-01T05:42:06.7Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712, upload-time = "2025-05-01T05:42:04.203Z" },
-]
-
[[package]]
name = "watchdog"
version = "6.0.0"
@@ -4608,18 +3051,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/09/ce/3929538b2b9918f5eee623fbf3346893973191f6df93f19bbda097bd7bb7/websockets-17.0.1-py3-none-any.whl", hash = "sha256:c6be9cba65c65cc76dfa3d4619e359ff02a4476c74e179b215236c11a0b32345", size = 206718, upload-time = "2026-07-31T11:31:26.037Z" },
]
-[[package]]
-name = "wheel"
-version = "0.46.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "packaging" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/89/24/a2eb353a6edac9a0303977c4cb048134959dd2a51b48a269dfc9dde00c8a/wheel-0.46.3.tar.gz", hash = "sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803", size = 60605, upload-time = "2026-01-22T12:39:49.136Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/87/22/b76d483683216dde3d67cba61fb2444be8d5be289bf628c13fc0fd90e5f9/wheel-0.46.3-py3-none-any.whl", hash = "sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d", size = 30557, upload-time = "2026-01-22T12:39:48.099Z" },
-]
-
[[package]]
name = "wsproto"
version = "1.3.2"