diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..585b79be --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,26 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + exclude: ^(KernelBench/|results/.*\.json$) + - id: end-of-file-fixer + exclude: ^(KernelBench/|results/.*\.json$) + - id: check-yaml + - id: check-toml + - id: check-added-large-files + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.2 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + exclude: ^(KernelBench/|src/kernelbench/prompts/model_ex_2\.py|src/kernelbench/prompts/model_new_ex_2\.py|results/.*\.json$) + - id: ruff-format + exclude: ^(KernelBench/|src/kernelbench/prompts/model_ex_2\.py|src/kernelbench/prompts/model_new_ex_2\.py|results/.*\.json$) + +- repo: https://github.com/psf/black + rev: 24.2.0 + hooks: + - id: black + exclude: ^(KernelBench/|src/kernelbench/prompts/model_ex_2\.py|src/kernelbench/prompts/model_new_ex_2\.py|results/.*\.json$) diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..c8cfe395 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10 diff --git a/LICENSE b/LICENSE index c6aaaad2..eaae6b11 100644 --- a/LICENSE +++ b/LICENSE @@ -19,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 6fa12cd0..8da695e1 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ We construct Kernel Bench to have 4 Levels of categories: - **Level 2 🔗**: Simple fusion patterns (100 Problems) A fused kernel would be faster than separated kernels (Conv + Bias + ReLU, Matmul + Scale + Sigmoid) - **Level 3 ⚛️**: Full model architectures (50 Problems) - Optimize entire model architectures end-to-end (MobileNet, VGG, MiniGPT, Mamba) -- **Level 4 🤗**: Level Hugging Face + Optimize entire model architectures end-to-end (MobileNet, VGG, MiniGPT, Mamba) +- **Level 4 🤗**: Level Hugging Face Optimize whole model architectures from HuggingFace ## ⚖️ Evaluation @@ -27,9 +27,9 @@ To evaluate model-generated kernels, we need to check if they: - **is correct ✅**: check against reference torch operators `n_correctness` times on randomized inputs. - **is performant ⏱️**: compare against reference torch operators `n_trial` times to measure speedup between runtimes. -Check out `src/eval.py` for details on how we implement correctness check and timing. +Check out `src/eval.py` for details on how we implement correctness check and timing. -We provide a convenient script `scripts/run_and_check.py` to evaluate one single sample source code against a reference source code, check correctness and compute speedup. You can use this to evaluate a model-generated kernel. +We provide a convenient script `scripts/run_and_check.py` to evaluate one single sample source code against a reference source code, check correctness and compute speedup. You can use this to evaluate a model-generated kernel. #### Overall Benchmark Metric @@ -44,7 +44,7 @@ You can increase speedup threshold `p` to make the task more challenging. #### Compute Overall Benchmark Performance -We provide a script `scripts/greedy_analysis.py` to compute the overall benchmark performance. +We provide a script `scripts/greedy_analysis.py` to compute the overall benchmark performance. Since we need to capture **both** correctness and performance, we use a metric `fast_p`: fraction of tasks that are both correct and have a speedup greater than threshold `p`; speedup is computed as the ratio of PyTorch reference wall-clock time to generated kernel time. @@ -56,11 +56,11 @@ KernelBench/ ├── assets/ ├── KernelBench/ # Benchmark dataset files ├── src/ # KernelBench logic code -│ ├── unit_tests/ +│ ├── unit_tests/ │ ├── prompts/ │ ├── .... ├── scripts/ # helpful scripts to run the benchmark -├── results/ # baseline times across hardware +├── results/ # baseline times across hardware ├── runs/ # where your runs will be stored ``` @@ -69,16 +69,29 @@ KernelBench/ conda create --name kernel-bench python=3.10 conda activate kernel-bench pip install -r requirements.txt -pip install -e . +pip install -e . +``` + +### Alternative setup using `uv` +You can also use `uv` as a faster alternative to conda and pip: + +``` +# Install a Python environment using uv +uv python install 3.10 + +# Create a virtual environment and install dependencies +uv venv +source .venv/bin/activate +uv pip install -e . ``` To call LLM API providers, set your `{INFERENCE_SERVER_PROVIDER}_API_KEY` API key. -Running and profiling kernels require a GPU. +Running and profiling kernels require a GPU. If you don't have GPU available locally, you can set up [Modal](https://modal.com/). Set up your modal token after creating an account by running `modal token new`. Then, use the `generate_and_eval_single_sample_modal.py` script. ## 🚀 Usage -### Run on a single problem +### Run on a single problem It is easier to get started with a single problem. This will fetch the problem, generate a sample, and evaluate the sample. ``` @@ -90,7 +103,7 @@ python3 scripts/generate_and_eval_single_sample.py dataset_src="huggingface" lev # add .verbose_logging for more visbility ``` -### Run on all problems +### Run on all problems ``` # 1. Generate responses and store kernels locally to runs/{run_name} directory @@ -103,7 +116,7 @@ python3 scripts/eval_from_generations.py run_name=test_hf_level_1 dataset_src=lo # add build_cache=True and num_cpu_workers= to the command ``` ### Analyze the eval results to compute Benchmark Performance -We provide `scripts/benchmark_eval_analysis.py` to analyze the eval results to compute success rate, timing metric, and overall benchmark performance `fast_p`. +We provide `scripts/benchmark_eval_analysis.py` to analyze the eval results to compute success rate, timing metric, and overall benchmark performance `fast_p`. ``` python3 scripts/benchmark_eval_analysis.py run_name=test_hf_level_1 level=1 hardware=L40S_matx3 baseline=baseline_time_torch @@ -114,7 +127,7 @@ We provide some reference baseline times a variety of NVIDIA GPUs across generat ## 🛣️ Upcoming Roadmap - [ ] Triton Variant (Ongoing) - [ ] Easy to use CoLab Notebook Example -- [ ] Push button flow on Modal / Cloud Provider +- [ ] Push button flow on Modal / Cloud Provider - [ ] Integrate with more frameworks, such as [ThunderKittens](https://github.com/HazyResearch/ThunderKittens) - [ ] Add backward pass - [ ] Integrate with toolchains such as NCU @@ -134,12 +147,12 @@ MIT. Check `LICENSE.md` for more details. ## Citation ```bibtex @misc{ouyang2025kernelbenchllmswriteefficient, - title={KernelBench: Can LLMs Write Efficient GPU Kernels?}, + title={KernelBench: Can LLMs Write Efficient GPU Kernels?}, author={Anne Ouyang and Simon Guo and Simran Arora and Alex L. Zhang and William Hu and Christopher Ré and Azalia Mirhoseini}, year={2025}, eprint={2502.10517}, archivePrefix={arXiv}, primaryClass={cs.LG}, - url={https://arxiv.org/abs/2502.10517}, + url={https://arxiv.org/abs/2502.10517}, } ``` diff --git a/curl.sh b/curl.sh new file mode 100644 index 00000000..77fe2b55 --- /dev/null +++ b/curl.sh @@ -0,0 +1,14 @@ +curl -X POST "https://tcapelle--kernel-benchmark-server-benchmarkservice-fastapi-app.modal.run/benchmark" \ + -F "ref_file=@src/kernelbench/prompts/model_ex_1.py" \ + -F "kernel_file=@src/kernelbench/prompts/model_new_ex_1.py" \ + -F "num_correct_trials=5" \ + -F "num_perf_trials=100" \ + -F "verbose=false" | python -m json.tool + + +curl -X POST "https://tcapelle--kernel-benchmark-server-benchmarkservice-f-d98c17-dev.modal.run/benchmark" \ + -F "ref_file=@src/kernelbench/prompts/model_ex_1.py" \ + -F "kernel_file=@src/kernelbench/prompts/model_new_ex_1.py" \ + -F "num_correct_trials=5" \ + -F "num_perf_trials=100" \ + -F "verbose=false" | python -m json.tool diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..ffcc2ccc --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,79 @@ +[project] +name = "kernelbench" +version = "0.1.0" +description = "Benchmarking Cuda/Triton Kernels made easy" +readme = "README.md" +requires-python = "==3.10.*" +dependencies = [ + "anthropic>=0.34.2", + "archon-ai>=0.1.4", + "datasets>=3.5.0", + "einops>=0.8.1", + "google-generativeai>=0.8.4", + "modal>=0.73.136", + "ninja>=1.11.1.4", + "numpy>=2.2.4", + "openai>=1.69.0", + "packaging>=24.2", + "pydra-config>=0.0.14", + "pytest>=8.3.5", + "together>=1.5.4", + "torch==2.5.0", + "tqdm>=4.67.1", + "transformers>=4.50.3", +] + + + [tool.setuptools] + package-dir = {"" = "src"} + packages = ["kernelbench"] + +[project.optional-dependencies] +dev = [ + "weave>=0.51.39", + "black>=24.2.0", + "ruff>=0.2.2", + "pre-commit>=3.5.0", + "pytest>=8.3.5", +] + +[tool.black] +line-length = 88 +target-version = ["py310"] +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | KernelBench + | results +)/ +''' + +[tool.ruff] +line-length = 88 +target-version = "py310" +select = ["E", "F", "I", "W", "B", "C4", "N"] +ignore = [] +exclude = [ + ".git", + ".venv", + "dist", + "build", + "KernelBench", + "results", +] + +[tool.ruff.isort] +known-first-party = ["kernelbench"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = "test_*.py" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..2476a922 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +testpaths = tests +python_files = test_*.py +python_functions = test_* +addopts = -v diff --git a/results/timing/README.md b/results/timing/README.md index 2aaea696..043d5658 100644 --- a/results/timing/README.md +++ b/results/timing/README.md @@ -56,4 +56,4 @@ Learn more about Torch Compile [backends](https://pytorch.org/docs/stable/torch. Thank you to [@PaliC](https://github.com/PaliC) from the PyTorch team for the exerptise on various Torch Configurations. -Thanks to [Modal](https://modal.com/) for sponsoring compute credits for us to collect runtime baseline on a vareity range of NVIDIA GPUs. \ No newline at end of file +Thanks to [Modal](https://modal.com/) for sponsoring compute credits for us to collect runtime baseline on a vareity range of NVIDIA GPUs. diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..a4be8e38 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,142 @@ +# KernelBench Scripts Guide + +This directory contains scripts for generating GPU kernels using LLMs and evaluating their performance compared to PyTorch baselines. + +## Script Categories + +The scripts can be organized into several categories: + +1. **Generation scripts** - Generate GPU kernels using LLMs +2. **Evaluation scripts** - Evaluate kernel correctness and performance +3. **Analysis scripts** - Analyze evaluation results +4. **Inspection/Debugging scripts** - Tools for debugging and inspecting kernels +5. **Modal variants** - Cloud-based versions of scripts using Modal + +## Core Scripts + +### Generation Scripts + +- **`generate_samples.py`** - Generate kernels for multiple problems + ```bash + # Example: Generate kernels for all level 1 problems using the DeepSeek model + python generate_samples.py run_name=test_hf_level_1 dataset_src=huggingface level=1 num_workers=50 server_type=deepseek model_name=deepseek-chat temperature=0 + ``` + +- **`generate_and_eval_single_sample.py`** - Generate and evaluate a kernel for a single problem + ```bash + # Example: Generate and evaluate a kernel for level 2, problem 40 + python generate_and_eval_single_sample.py dataset_src="huggingface" level=2 problem_id=40 + ``` + +### Evaluation Scripts + +- **`run_and_check.py`** - Evaluate a generated kernel against a reference implementation + ```bash + # Example: Evaluate a generated kernel + python run_and_check.py --kernel_path=path/to/kernel.py --reference_path=path/to/reference.py + ``` + +- **`eval_from_generations.py`** - Evaluate all generated kernels in a run directory + ```bash + # Example: Evaluate all kernels from a previous generation run + python eval_from_generations.py run_name=test_hf_level_1 dataset_src=local level=1 num_gpu_devices=8 timeout=300 + ``` + +- **`verify_generation.py`** - Verify if a generated kernel is correct + ```bash + # Example: Verify a kernel's correctness + python verify_generation.py --kernel_path=path/to/kernel.py --reference_path=path/to/reference.py + ``` + +### Analysis Scripts + +- **`benchmark_eval_analysis.py`** - Analyze evaluation results to compute benchmark metrics + ```bash + # Example: Analyze results from evaluation + python benchmark_eval_analysis.py run_name=test_hf_level_1 level=1 hardware=L40S_matx3 baseline=baseline_time_torch + ``` + +- **`generate_baseline_time.py`** - Generate baseline timing results for PyTorch implementations + ```bash + # Example: Generate baseline timing for level 1 problems + python generate_baseline_time.py level=1 run_name=baseline_torch_l1 n_trials=100 + ``` + +### Inspection Scripts + +- **`inspect_baseline.py`** - Inspect baseline PyTorch implementation details + ```bash + # Example: Inspect baseline for a specific problem + python inspect_baseline.py level=1 problem_id=10 + ``` + +- **`inspect_triton.py`** - Inspect Triton kernel implementation details + ```bash + # Example: Inspect Triton kernel for a specific problem + python inspect_triton.py level=1 problem_id=10 + ``` + +- **`inspect_kernel_pytorch_profiler.py`** - Profile kernels with PyTorch profiler + ```bash + # Example: Profile a kernel with PyTorch profiler + python inspect_kernel_pytorch_profiler.py --kernel_path=path/to/kernel.py + ``` + +## Modal Variants + +These scripts use [Modal](https://modal.com/) for cloud-based execution: + +- **`generate_and_eval_single_sample_modal.py`** - Cloud version of single sample generation/evaluation + ```bash + # Example: Generate and evaluate a kernel on Modal + python generate_and_eval_single_sample_modal.py dataset_src="huggingface" level=2 problem_id=40 + ``` + +- **`generate_baseline_time_modal.py`** - Cloud version of baseline timing generation + ```bash + # Example: Generate baseline timing on Modal + python generate_baseline_time_modal.py level=1 run_name=baseline_torch_l1_modal n_trials=100 + ``` + +- **`run_and_check_modal.py`** - Cloud version of kernel evaluation + ```bash + # Example: Evaluate a kernel on Modal + python run_and_check_modal.py --kernel_path=path/to/kernel.py --reference_path=path/to/reference.py + ``` + +- **`server_run_and_check.py`** and **`server_run_and_check_modal.py`** - Server variants for continuous evaluation + +## Workflow Examples + +### Complete Local Workflow + +1. Generate kernels for all level 1 problems: + ```bash + python generate_samples.py run_name=test_level_1 dataset_src=huggingface level=1 num_workers=50 server_type=deepseek model_name=deepseek-chat temperature=0 + ``` + +2. Evaluate the generated kernels: + ```bash + python eval_from_generations.py run_name=test_level_1 dataset_src=local level=1 num_gpu_devices=8 timeout=300 build_cache=True num_cpu_workers=16 + ``` + +3. Analyze the results: + ```bash + python benchmark_eval_analysis.py run_name=test_level_1 level=1 hardware=L40S_matx3 baseline=baseline_time_torch + ``` + +### Cloud-based Single Problem Workflow + +1. Set up Modal: + ```bash + modal token new + ``` + +2. Generate and evaluate a kernel on Modal: + ```bash + python generate_and_eval_single_sample_modal.py dataset_src="huggingface" level=2 problem_id=40 + ``` + +## Note on Code Reuse + +There is significant opportunity for code reuse and consolidation between the standard and Modal versions of scripts. Consider refactoring to create a common core library that both local and cloud variants can leverage. diff --git a/src/__init__.py b/scripts/__init__.py similarity index 100% rename from src/__init__.py rename to scripts/__init__.py diff --git a/scripts/benchmark_eval_analysis.py b/scripts/benchmark_eval_analysis.py index fe2d220c..346e61c4 100644 --- a/scripts/benchmark_eval_analysis.py +++ b/scripts/benchmark_eval_analysis.py @@ -2,7 +2,7 @@ from tabulate import tabulate import pydra from pydra import REQUIRED, Config -from src.dataset import construct_kernelbench_dataset +from kernelbench.dataset import construct_kernelbench_dataset """ Benchmark Eval Analysis @@ -18,21 +18,23 @@ ``` python3 scripts/benchmark_eval_analysis.py run_name= level= hardware= baseline= ``` -hardware + baseline should correspond to the results/timing/hardware/baseline.json file +hardware + baseline should correspond to the results/timing/hardware/baseline.json file + +""" -""" class AnalysisConfig(Config): def __init__(self): - self.run_name = REQUIRED # name of the run to evaluate - self.level = REQUIRED # level to evaluate + self.run_name = REQUIRED # name of the run to evaluate + self.level = REQUIRED # level to evaluate - self.hardware = REQUIRED # hardware to evaluate - self.baseline = REQUIRED # baseline to compare against + self.hardware = REQUIRED # hardware to evaluate + self.baseline = REQUIRED # baseline to compare against def __repr__(self): return f"AnalysisConfig({self.to_dict()})" + def patch(eval_results, dataset): """ Patch the eval results with the dataset @@ -40,15 +42,16 @@ def patch(eval_results, dataset): for pid in range(1, len(dataset) + 1): if str(pid) not in eval_results: eval_results[str(pid)] = { - "sample_id": 0, - "compiled": False, - "correctness": False, + "sample_id": 0, + "compiled": False, + "correctness": False, "metadata": {}, - "runtime": -1.0, - "runtime_stats": {} + "runtime": -1.0, + "runtime_stats": {}, } return eval_results + def analyze_greedy_eval(run_name, hardware, baseline, level): """ Analyze the greedy eval results for a run of a particular level @@ -57,16 +60,20 @@ def analyze_greedy_eval(run_name, hardware, baseline, level): dataset = construct_kernelbench_dataset(level) # load json - eval_file_path = f'runs/{run_name}/eval_results.json' - assert os.path.exists(eval_file_path), f"Eval file does not exist at {eval_file_path}" + eval_file_path = f"runs/{run_name}/eval_results.json" + assert os.path.exists( + eval_file_path + ), f"Eval file does not exist at {eval_file_path}" - baseline_file_path = f'results/timing/{hardware}/{baseline}.json' - assert os.path.exists(baseline_file_path), f"Baseline file does not exist at {baseline_file_path}" + baseline_file_path = f"results/timing/{hardware}/{baseline}.json" + assert os.path.exists( + baseline_file_path + ), f"Baseline file does not exist at {baseline_file_path}" - with open(eval_file_path, 'r') as f: + with open(eval_file_path, "r") as f: eval_results = json.load(f) - with open(baseline_file_path, 'r') as f: + with open(baseline_file_path, "r") as f: baseline_results = json.load(f) # Initialize counters @@ -95,29 +102,41 @@ def analyze_greedy_eval(run_name, hardware, baseline, level): print(f"\nSuccess rates:") print(f"Compilation rate: {compiled_count/total_count*100:.1f}%") - print(f"Correctness rate: {correct_count/total_count*100:.1f}%") - + print(f"Correctness rate: {correct_count/total_count*100:.1f}%") # Calculate speedup metrics - from src.score import geometric_mean_speed_ratio_correct_only, geometric_mean_speed_ratio_correct_and_faster_only, fastp + from src.score import ( + geometric_mean_speed_ratio_correct_only, + geometric_mean_speed_ratio_correct_and_faster_only, + fastp, + ) import numpy as np # Extract the speedup values is_correct = np.array([entry["correctness"] for entry in eval_results.values()]) - baseline_speed = np.array([entry["mean"] for entry in baseline_results[f'level{level}'].values()]) + baseline_speed = np.array( + [entry["mean"] for entry in baseline_results[f"level{level}"].values()] + ) actual_speed = np.array([entry["runtime"] for entry in eval_results.values()]) n = len(is_correct) - assert len(baseline_speed) == n, "Baseline speedup values do not match the number of eval results" - assert len(actual_speed) == n, "Actual speedup values do not match the number of eval results" + assert ( + len(baseline_speed) == n + ), "Baseline speedup values do not match the number of eval results" + assert ( + len(actual_speed) == n + ), "Actual speedup values do not match the number of eval results" # Calculate the metrics - gmsr_correct = geometric_mean_speed_ratio_correct_only(is_correct, baseline_speed, actual_speed, n) + gmsr_correct = geometric_mean_speed_ratio_correct_only( + is_correct, baseline_speed, actual_speed, n + ) # list of speedup thresholds p p_values = [0.0, 0.5, 0.8, 1.0, 1.5, 2.0] - results = [[p, fastp(is_correct, baseline_speed, actual_speed, n, p)] for p in p_values] - + results = [ + [p, fastp(is_correct, baseline_speed, actual_speed, n, p)] for p in p_values + ] # Print the results print("\nSpeedup Metrics:") @@ -125,12 +144,17 @@ def analyze_greedy_eval(run_name, hardware, baseline, level): # Print table print("\nFast_p Results:") - print(tabulate(results, headers=["Speedup Threshold (p)", "Fast_p Score"], tablefmt="grid")) + print( + tabulate( + results, headers=["Speedup Threshold (p)", "Fast_p Score"], tablefmt="grid" + ) + ) @pydra.main(base=AnalysisConfig) def main(config: AnalysisConfig): analyze_greedy_eval(config.run_name, config.hardware, config.baseline, config.level) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/debug_stddout.py b/scripts/debug_stddout.py index b3eb81f0..18edc096 100644 --- a/scripts/debug_stddout.py +++ b/scripts/debug_stddout.py @@ -1,33 +1,32 @@ -# from src.eval import build_compile_cache_with_capturing -import subprocess -import os -import ninja - -from src.utils import set_gpu_arch -from src.eval import build_compile_cache_with_capturing +from kernelbench.utils import set_gpu_arch +from kernelbench.eval import build_compile_cache_with_capturing ################################################################################ # Test for checking if we can capture nvcc errors ################################################################################ -correct_kernel_code = "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n# Define the custom CUDA kernel for 4D tensor-matrix multiplication\ntensor_matmul_source = \"\"\"\n#include \n#include \n\n__global__ void tensor_matmul_kernel(const float* A, const float* B, float* C, int b, int i, int j, int l, int k) {\n int idx_b = blockIdx.x;\n int idx_i = blockIdx.y;\n int idx_j = blockIdx.z;\n int idx_k = threadIdx.x;\n\n if (idx_b < b && idx_i < i && idx_j < j && idx_k < k) {\n float sum = 0.0f;\n for (int idx_l = 0; idx_l < l; ++idx_l) {\n sum += A[idx_b * i * j * l + idx_i * j * l + idx_j * l + idx_l] * B[idx_l * k + idx_k];\n }\n C[idx_b * i * j * k + idx_i * j * k + idx_j * k + idx_k] = sum;\n }\n}\n\ntorch::Tensor tensor_matmul_cuda(torch::Tensor A, torch::Tensor B) {\n int b = A.size(0);\n int i = A.size(1);\n int j = A.size(2);\n int l = A.size(3);\n int k = B.size(1);\n\n auto C = torch::zeros({b, i, j, k}, A.options());\n\n dim3 blocks(b, i, j);\n int threads = k;\n\n tensor_matmul_kernel<<>>(A.data_ptr(), B.data_ptr(), C.data_ptr(), b, i, j, l, k);\n\n return C;\n}\n\"\"\"\n\ntensor_matmul_cpp_source = (\n \"torch::Tensor tensor_matmul_cuda(torch::Tensor A, torch::Tensor B);\"\n)\n\n# Compile the inline CUDA code for 4D tensor-matrix multiplication\ntensor_matmul = load_inline(\n name=\"tensor_matmul\",\n cpp_sources=tensor_matmul_cpp_source,\n cuda_sources=tensor_matmul_source,\n functions=[\"tensor_matmul_cuda\"],\n verbose=True,\n extra_cflags=[\"\"],\n extra_ldflags=[\"\"],\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super(ModelNew, self).__init__()\n self.tensor_matmul = tensor_matmul\n\n def forward(self, A, B):\n return self.tensor_matmul.tensor_matmul_cuda(A, B)" +correct_kernel_code = 'import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n# Define the custom CUDA kernel for 4D tensor-matrix multiplication\ntensor_matmul_source = """\n#include \n#include \n\n__global__ void tensor_matmul_kernel(const float* A, const float* B, float* C, int b, int i, int j, int l, int k) {\n int idx_b = blockIdx.x;\n int idx_i = blockIdx.y;\n int idx_j = blockIdx.z;\n int idx_k = threadIdx.x;\n\n if (idx_b < b && idx_i < i && idx_j < j && idx_k < k) {\n float sum = 0.0f;\n for (int idx_l = 0; idx_l < l; ++idx_l) {\n sum += A[idx_b * i * j * l + idx_i * j * l + idx_j * l + idx_l] * B[idx_l * k + idx_k];\n }\n C[idx_b * i * j * k + idx_i * j * k + idx_j * k + idx_k] = sum;\n }\n}\n\ntorch::Tensor tensor_matmul_cuda(torch::Tensor A, torch::Tensor B) {\n int b = A.size(0);\n int i = A.size(1);\n int j = A.size(2);\n int l = A.size(3);\n int k = B.size(1);\n\n auto C = torch::zeros({b, i, j, k}, A.options());\n\n dim3 blocks(b, i, j);\n int threads = k;\n\n tensor_matmul_kernel<<>>(A.data_ptr(), B.data_ptr(), C.data_ptr(), b, i, j, l, k);\n\n return C;\n}\n"""\n\ntensor_matmul_cpp_source = (\n "torch::Tensor tensor_matmul_cuda(torch::Tensor A, torch::Tensor B);"\n)\n\n# Compile the inline CUDA code for 4D tensor-matrix multiplication\ntensor_matmul = load_inline(\n name="tensor_matmul",\n cpp_sources=tensor_matmul_cpp_source,\n cuda_sources=tensor_matmul_source,\n functions=["tensor_matmul_cuda"],\n verbose=True,\n extra_cflags=[""],\n extra_ldflags=[""],\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super(ModelNew, self).__init__()\n self.tensor_matmul = tensor_matmul\n\n def forward(self, A, B):\n return self.tensor_matmul.tensor_matmul_cuda(A, B)' faulty_kernel_code = 'import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n# Define the custom CUDA kernel for Max Pooling 3D\nmaxpool3d_source = """\n#include \n#include \n\n__global__ void maxpool3d_kernel(const float* input, float* output, int* indices, \n int batch_size, int channels, int dim1, int dim2, int dim3,\n int kernel_size, int stride, int padding, int dilation,\n int out_dim1, int out_dim2, int out_dim3) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int b = idx / (channels * out_dim1 * out_dim2 * out_dim3);\n int c = (idx / (out_dim1 * out_dim2 * out_dim3)) % channels;\n int d1 = (idx / (out_dim2 * out_dim3)) % out_dim1;\n int d2 = (idx / out_dim3) % out_dim2;\n int d3 = idx % out_dim3;\n\n if (b < batch_size && c < channels && d1 < out_dim1 && d2 < out_dim2 && d3 < out_dim3) {\n float max_val = -FLT_MAX;\n int max_idx = -1;\n\n for (int k1 = 0; k1 < kernel_size; ++k1) {\n for (int k2 = 0; k2 < kernel_size; ++k2) {\n for (int k3 = 0; k3 < kernel_size; ++k3) {\n int in_d1 = d1 * stride - padding + k1 * dilation;\n int in_d2 = d2 * stride - padding + k2 * dilation;\n int in_d3 = d3 * stride - padding + k3 * dilation;\n\n if (in_d1 >= 0 && in_d1 < dim1 && in_d2 >= 0 && in_d2 < dim2 && in_d3 >= 0 && in_d3 < dim3) {\n int in_idx = ((b * channels + c) * dim1 + in_d1) * dim2 * dim3 + in_d2 * dim3 + in_d3;\n float val = input[in_idx];\n if (val > max_val) {\n max_val = val;\n max_idx = in_idx;\n }\n }\n }\n }\n }\n\n output[idx] = max_val;\n if (indices != nullptr) {\n indices[idx] = max_idx;\n }\n }\n}\n\ntorch::Tensor maxpool3d_cuda(torch::Tensor input, int kernel_size, int stride, int padding, int dilation, bool return_indices) {\n int batch_size = input.size(0);\n int channels = input.size(1);\n int dim1 = input.size(2);\n int dim2 = input.size(3);\n int dim3 = input.size(4);\n\n int out_dim1 = (dim1 + 2 * padding - dilation * (kernel_size - 1) - 1) / stride + 1;\n int out_dim2 = (dim2 + 2 * padding - dilation * (kernel_size - 1) - 1) / stride + 1;\n int out_dim3 = (dim3 + 2 * padding - dilation * (kernel_size - 1) - 1) / stride + 1;\n\n auto output = torch::zeros({batch_size, channels, out_dim1, out_dim2, out_dim3}, input.options());\n auto indices = return_indices ? torch::zeros({batch_size, channels, out_dim1, out_dim2, out_dim3}, input.options().dtype(torch::kInt32)) : torch::Tensor();\n\n int size = batch_size * channels * out_dim1 * out_dim2 * out_dim3;\n const int block_size = 256;\n const int num_blocks = (size + block_size - 1) / block_size;\n\n maxpool3d_kernel<<>>(input.data_ptr(), output.data_ptr(), \n return_indices ? indices.data_ptr() : nullptr,\n batch_size, channels, dim1, dim2, dim3,\n kernel_size, stride, padding, dilation,\n out_dim1, out_dim2, out_dim3);\n\n if (return_indices) {\n return torch::make_tuple(output, indices);\n } else {\n return output;\n }\n}\n"""\n\nmaxpool3d_cpp_source = (\n "torch::Tensor maxpool3d_cuda(torch::Tensor input, int kernel_size, int stride, int padding, int dilation, bool return_indices);"\n)\n\n# Compile the inline CUDA code for Max Pooling 3D\nmaxpool3d = load_inline(\n name="maxpool3d",\n cpp_sources=maxpool3d_cpp_source,\n cuda_sources=maxpool3d_source,\n functions=["maxpool3d_cuda"],\n verbose=True,\n extra_cflags=[""],\n extra_ldflags=[""],\n)\n\n\nclass ModelNew(nn.Module):\n """\n Optimized model that performs Max Pooling 3D using custom CUDA kernels.\n """\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0, dilation: int = 1, return_indices: bool = False, ceil_mode: bool = False):\n """\n Initializes the Max Pooling 3D layer.\n\n Args:\n kernel_size (int): Size of the kernel for the max pooling operation.\n stride (int, optional): Stride of the pooling operation. Defaults to None, which means stride is equal to kernel_size.\n padding (int, optional): Padding applied to the input tensor. Defaults to 0.\n dilation (int, optional): Spacing between kernel elements. Defaults to 1.\n return_indices (bool, optional): Whether to return indices of the maximum values. Defaults to False.\n ceil_mode (bool, optional): When True, the output size is ceil(input_size / stride) instead of floor. Defaults to False.\n """\n super(ModelNew, self).__init__()\n self.kernel_size = kernel_size\n self.stride = stride if stride is not None else kernel_size\n self.padding = padding\n self.dilation = dilation\n self.return_indices = return_indices\n self.ceil_mode = ceil_mode\n self.maxpool3d = maxpool3d\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n """\n Applies Max Pooling 3D to the input tensor using custom CUDA kernels.\n\n Args:\n x (torch.Tensor): Input tensor of shape (batch_size, channels, dim1, dim2, dim3).\n\n Returns:\n torch.Tensor: Output tensor with Max Pooling 3D applied.\n """\n return self.maxpool3d.maxpool3d_cuda(x, self.kernel_size, self.stride, self.padding, self.dilation, self.return_indices)' -set_gpu_arch(["Ada"]) # replace with whatever device architecthre you have +set_gpu_arch(["Ada"]) # replace with whatever device architecthre you have test_build_dir = "test_build_dir" print("Testing Correct Kernel Code") -status, stdout, err = build_compile_cache_with_capturing(correct_kernel_code, verbose=False, build_dir=test_build_dir) +status, stdout, err = build_compile_cache_with_capturing( + correct_kernel_code, verbose=False, build_dir=test_build_dir +) print("status: ", status) print("stdout: ", stdout) print("err: ", err) assert status == 0, "Correct Code should compile" print("Testing Faulty Kernel Code") -status, stdout, err = build_compile_cache_with_capturing(faulty_kernel_code, verbose=False, build_dir=test_build_dir) +status, stdout, err = build_compile_cache_with_capturing( + faulty_kernel_code, verbose=False, build_dir=test_build_dir +) print("status: ", status) print("stdout: ", stdout) print("err: ", err) diff --git a/scripts/eval_from_generations.py b/scripts/eval_from_generations.py index 82913fce..20db46c0 100644 --- a/scripts/eval_from_generations.py +++ b/scripts/eval_from_generations.py @@ -6,7 +6,6 @@ import json from tqdm import tqdm -from src import eval, utils, compile import torch import os import multiprocessing as mp @@ -14,9 +13,15 @@ from datasets import load_dataset -from src.dataset import construct_kernelbench_dataset -from src.eval import build_compile_cache, eval_kernel_against_ref, KernelExecResult, check_metadata_serializable_all_types -from src.utils import set_gpu_arch, read_file +from kernelbench.compile import batch_compile +from kernelbench.dataset import construct_kernelbench_dataset +from kernelbench.eval import ( + build_compile_cache, + eval_kernel_against_ref, + KernelExecResult, + check_metadata_serializable_all_types, +) +from kernelbench.utils import set_gpu_arch, read_file """ Batch Evaluation from Existing Generations @@ -39,9 +44,9 @@ class EvalConfig(Config): def __init__(self): - self.run_name = REQUIRED # name of the run to evaluate + self.run_name = REQUIRED # name of the run to evaluate - self.dataset_src = REQUIRED # either huggingface or local + self.dataset_src = REQUIRED # either huggingface or local # name of dataset name on Hugging Face self.dataset_name = "ScalingIntelligence/KernelBench" @@ -50,7 +55,7 @@ def __init__(self): self.level = REQUIRED # subset of problems to evaluate - self.subset = (None, None) # (start_id, end_id), these are the logical index + self.subset = (None, None) # (start_id, end_id), these are the logical index # Evaluation Mode: local (requires GPU), see modal (cloud GPU) in the modal file self.eval_mode = "local" @@ -68,20 +73,21 @@ def __init__(self): # Eval settings self.num_correct_trials = 5 self.num_perf_trials = 100 - self.timeout = 180 # in seconds + self.timeout = 180 # in seconds self.measure_performance = True - + # Eval Flow setting # To speedup evaluation, you can start building the kernel on CPU on disk as cache self.build_cache = False - self.num_cpu_workers = 20 # number of parallel process to to parallelize the build on CPUs - + self.num_cpu_workers = ( + 20 # number of parallel process to to parallelize the build on CPUs + ) + # Directory to build kernels for evaluation self.kernel_eval_build_dir = os.path.join(REPO_TOP_DIR, "cache") # number of GPUs to do batch evaluation self.num_gpu_devices = 1 - def __repr__(self): return f"EvalConfig({self.to_dict()})" @@ -94,43 +100,58 @@ class WorkArgs: device: torch.device -def fetch_ref_arch_from_problem_id(dataset, problem_id: int, dataset_src: str) -> str | None: +def fetch_ref_arch_from_problem_id( + dataset, problem_id: int, dataset_src: str +) -> str | None: """ Fetch reference architecture from problem directory Either from Hugging Face or Local Dataset """ if dataset_src == "huggingface": - curr_problem_row = dataset.filter(lambda x: x["problem_id"] == problem_id, num_proc=1, desc=None) + curr_problem_row = dataset.filter( + lambda x: x["problem_id"] == problem_id, num_proc=1, desc=None + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] - + elif dataset_src == "local": - problem_idx_in_dataset = problem_id - 1 # due to dataset list being 0-indexed locally + problem_idx_in_dataset = ( + problem_id - 1 + ) # due to dataset list being 0-indexed locally ref_arch_path = dataset[problem_idx_in_dataset] problem_name = os.path.basename(ref_arch_path) ref_arch_src = read_file(ref_arch_path) # verify - # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") + # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") problem_number = int(problem_name.split("_")[0]) - assert problem_number == problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({problem_id})" - + assert ( + problem_number == problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({problem_id})" + return ref_arch_src -def fetch_kernel_from_disk(run_dir: str, level: int, problem_id: int, sample_id: int) -> str | None: +def fetch_kernel_from_disk( + run_dir: str, level: int, problem_id: int, sample_id: int +) -> str | None: """ Fetch kernel file from disk (stored in runs/{run_name}) """ - kernel_path = os.path.join(run_dir, f"level_{level}_problem_{problem_id}_sample_{sample_id}_kernel.py") - + kernel_path = os.path.join( + run_dir, f"level_{level}_problem_{problem_id}_sample_{sample_id}_kernel.py" + ) + if os.path.exists(kernel_path): return read_file(kernel_path) else: return None -def evaluate_single_sample(work_args: WorkArgs, configs: EvalConfig, dataset, run_dir: str) -> KernelExecResult | None: + +def evaluate_single_sample( + work_args: WorkArgs, configs: EvalConfig, dataset, run_dir: str +) -> KernelExecResult | None: """ Evaluate a single sample on a single GPU """ @@ -140,22 +161,28 @@ def evaluate_single_sample(work_args: WorkArgs, configs: EvalConfig, dataset, ru work_args.device, ) # fetch reference architecture from problem directory - ref_arch_src = fetch_ref_arch_from_problem_id(dataset, problem_id, configs.dataset_src) + ref_arch_src = fetch_ref_arch_from_problem_id( + dataset, problem_id, configs.dataset_src + ) # fetch kernel from disk # Add database support in the future kernel_src = fetch_kernel_from_disk(run_dir, configs.level, problem_id, sample_id) - assert kernel_src is not None, f"Kernel not found for problem {problem_id} sample {sample_id}" + assert ( + kernel_src is not None + ), f"Kernel not found for problem {problem_id} sample {sample_id}" - build_dir = os.path.join(configs.kernel_eval_build_dir, configs.run_name, f"{problem_id}", f"{sample_id}") + build_dir = os.path.join( + configs.kernel_eval_build_dir, configs.run_name, f"{problem_id}", f"{sample_id}" + ) - try: + try: eval_result = eval_kernel_against_ref( original_model_src=ref_arch_src, custom_model_src=kernel_src, measure_performance=configs.measure_performance, - verbose=configs.verbose, + verbose=configs.verbose, num_correct_trials=configs.num_correct_trials, num_perf_trials=configs.num_perf_trials, build_dir=build_dir, @@ -178,14 +205,17 @@ def evaluate_single_sample(work_args: WorkArgs, configs: EvalConfig, dataset, ru ) return eval_result else: - metadata = {"other_error": f"error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } # for debugging - eval_result = KernelExecResult(compiled=False, correctness=False, - metadata=metadata) + metadata = { + "other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } # for debugging + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) return eval_result - + + def cuda_single_eval_wrapper(curr_work: WorkArgs, configs: dict, dataset, run_dir: str): """ Wrapper to handle timeout and keyboard interrupt @@ -198,16 +228,18 @@ def cuda_single_eval_wrapper(curr_work: WorkArgs, configs: dict, dataset, run_di args=(curr_work, configs, dataset, run_dir), ).get(timeout=configs.timeout) except KeyboardInterrupt: - print( - "\n [Terminate] Caught KeyboardInterrupt, terminating workers..." - ) + print("\n [Terminate] Caught KeyboardInterrupt, terminating workers...") pool.terminate() pool.join() raise except mp.TimeoutError as e: - print(f"[WARNING] Evaluation TIMED OUT for Problem ID: {curr_work.problem_id}, Sample ID: {curr_work.sample_id}") + print( + f"[WARNING] Evaluation TIMED OUT for Problem ID: {curr_work.problem_id}, Sample ID: {curr_work.sample_id}" + ) - print(f"[Eval Result] Problem ID: {curr_work.problem_id}, Sample ID: {curr_work.sample_id}: {result}") + print( + f"[Eval Result] Problem ID: {curr_work.problem_id}, Sample ID: {curr_work.sample_id}: {result}" + ) return result @@ -216,15 +248,20 @@ def remove_cache_dir(cache_dir: str, run_name: str, problem_id, sample_id): Remove the cached folder for sample compilation so it can start a clean build next time useful for time out, failed build, etc. """ - problem_cache_dir = os.path.join(cache_dir, run_name, f"{problem_id}", f"{sample_id}") + problem_cache_dir = os.path.join( + cache_dir, run_name, f"{problem_id}", f"{sample_id}" + ) print(f"cache_dir to remove: {problem_cache_dir}") if os.path.exists(cache_dir): try: shutil.rmtree(cache_dir, ignore_errors=True) - print(f"\n[INFO] Removed cached folder for Problem ID: {problem_id}, Sample ID: {sample_id}") + print( + f"\n[INFO] Removed cached folder for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) except Exception as e: print(f"\n[WARNING] Failed to remove cache directory {cache_dir}: {str(e)}") + def batch_eval( total_work: list[tuple[int, int]], config: EvalConfig, @@ -248,7 +285,9 @@ def batch_eval( print( f"[Curr Batch] {len(curr_work_batch)} tasks over {config.num_gpu_devices} GPUs; [Total Work left] {len(total_work)}" ) - assert len(curr_work_batch) <= batch_size, f"Current batch size {len(curr_work_batch)} is greater than the number of GPUs {batch_size}" + assert ( + len(curr_work_batch) <= batch_size + ), f"Current batch size {len(curr_work_batch)} is greater than the number of GPUs {batch_size}" with mp.Pool(batch_size) as pool: @@ -273,7 +312,7 @@ def batch_eval( async_results.append( pool.apply_async(evaluate_single_sample, work_arg) ) - + # Collect results with a batch timeout results = [] batch_timeout = config.timeout @@ -285,20 +324,30 @@ def batch_eval( remaining_time = max(0, batch_timeout - elapsed_time) result = async_result.get(timeout=remaining_time) results.append((problem_id, sample_id, result)) - + except mp.TimeoutError: print( f"[WARNING] Evaluation TIMED OUT for Problem ID: {problem_id}, Sample ID: {sample_id}" ) results.append((problem_id, sample_id, None)) - - remove_cache_dir(config.kernel_eval_build_dir, config.run_name, problem_id, sample_id) + + remove_cache_dir( + config.kernel_eval_build_dir, + config.run_name, + problem_id, + sample_id, + ) except Exception as e: print( f"[ERROR] Evaluation FAILED for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}" ) results.append((problem_id, sample_id, None)) - remove_cache_dir(config.kernel_eval_build_dir, config.run_name, problem_id, sample_id) + remove_cache_dir( + config.kernel_eval_build_dir, + config.run_name, + problem_id, + sample_id, + ) end_time = time.time() @@ -313,8 +362,12 @@ def batch_eval( # add all the batch results here to avoid file race condition # add to eval result if valid result if result is not None: - print(f"Adding Eval Result to file for problem {problem_id} sample {sample_id}") - add_to_eval_results_file(problem_id, sample_id, result, eval_file_path) + print( + f"Adding Eval Result to file for problem {problem_id} sample {sample_id}" + ) + add_to_eval_results_file( + problem_id, sample_id, result, eval_file_path + ) print("-" * 128) print( @@ -323,51 +376,62 @@ def batch_eval( pbar.update(len(curr_work_batch)) -def check_if_eval_exists_local(problem_id: int, sample_id: int, eval_file_path: str) -> bool: + +def check_if_eval_exists_local( + problem_id: int, sample_id: int, eval_file_path: str +) -> bool: """ Check if evaluation result already exists in eval results file """ if os.path.exists(eval_file_path): - with open(eval_file_path, 'r') as f: + with open(eval_file_path, "r") as f: eval_results = json.load(f) return str(problem_id) in eval_results return False -def add_to_eval_results_file(problem_id: int, sample_id: int, eval_result: KernelExecResult, eval_file_path: str): + +def add_to_eval_results_file( + problem_id: int, sample_id: int, eval_result: KernelExecResult, eval_file_path: str +): """ Add evaluation result to eval results file TODO: migrate database support """ # Load existing results if file exists if os.path.exists(eval_file_path): - with open(eval_file_path, 'r') as f: + with open(eval_file_path, "r") as f: eval_results = json.load(f) else: eval_results = {} - + # Add new result eval_results[str(problem_id)] = { # assume 1 sample for now, will think about how to do this better for more samples - 'sample_id': sample_id, - 'compiled': eval_result.compiled, - 'correctness': eval_result.correctness, - 'metadata': check_metadata_serializable_all_types(eval_result.metadata), - 'runtime': eval_result.runtime, - 'runtime_stats': eval_result.runtime_stats, + "sample_id": sample_id, + "compiled": eval_result.compiled, + "correctness": eval_result.correctness, + "metadata": check_metadata_serializable_all_types(eval_result.metadata), + "runtime": eval_result.runtime, + "runtime_stats": eval_result.runtime_stats, } - + # Write updated results back to file if not os.path.exists(eval_file_path): os.makedirs(os.path.dirname(eval_file_path), exist_ok=True) - + with open(eval_file_path, "w") as f: json.dump(eval_results, f) -def single_eval_example(config: EvalConfig, curr_level_dataset: list[str], run_dir: str, eval_file_path ): + +def single_eval_example( + config: EvalConfig, curr_level_dataset: list[str], run_dir: str, eval_file_path +): device = torch.device("cuda:0") example_work = WorkArgs(problem_id=1, sample_id=0, device=device) # example_eval_result = evaluate_single_sample(example_work, config, curr_level_dataset, run_dir) - example_eval_result = cuda_single_eval_wrapper(example_work, config, curr_level_dataset, run_dir) + example_eval_result = cuda_single_eval_wrapper( + example_work, config, curr_level_dataset, run_dir + ) print(example_eval_result) if not check_if_eval_exists_local(1, 0, eval_file_path): add_to_eval_results_file(1, 0, example_eval_result, eval_file_path) @@ -380,7 +444,7 @@ def main(config: EvalConfig): Store Eval Results in specified eval results file """ print(f"Starting Batch Eval with config: {config}") - + # Check if CUDA is available if not torch.cuda.is_available(): raise RuntimeError("CUDA device not available. Evaluation requires GPU.") @@ -394,38 +458,47 @@ def main(config: EvalConfig): curr_level_dataset = dataset[f"level_{config.level}"] elif config.dataset_src == "local": curr_level_dataset = construct_kernelbench_dataset(config.level) - + num_problems_in_level = len(curr_level_dataset) if config.subset == (None, None): problem_id_range = range(1, num_problems_in_level) else: - assert config.subset[0] >= 1 and config.subset[1] <= num_problems_in_level, f"Subset range {config.subset} out of range for Level {config.level}" + assert ( + config.subset[0] >= 1 and config.subset[1] <= num_problems_in_level + ), f"Subset range {config.subset} out of range for Level {config.level}" problem_id_range = range(config.subset[0], config.subset[1]) - print(f"Evaluating 1 sample each for level {config.level} problems: {problem_id_range}") + print( + f"Evaluating 1 sample each for level {config.level} problems: {problem_id_range}" + ) run_dir = os.path.join(config.runs_dir, config.run_name) eval_file_path = os.path.join(run_dir, f"eval_results.json") - # set GPU arch to configure what target to build for set_gpu_arch(config.gpu_arch) - assert config.num_gpu_devices <= torch.cuda.device_count(), f"Number of GPUs requested ({config.num_gpu_devices}) is greater than the number of available GPUs ({torch.cuda.device_count()})" + assert ( + config.num_gpu_devices <= torch.cuda.device_count() + ), f"Number of GPUs requested ({config.num_gpu_devices}) is greater than the number of available GPUs ({torch.cuda.device_count()})" # To Debug # single_eval_example(config, curr_level_dataset, run_dir, eval_file_path) total_work = [] - for problem_id in range(problem_id_range.start, problem_id_range.stop + 1): # end index is inclusive - sample_id = 0 # only evaluate 1 sample for now + for problem_id in range( + problem_id_range.start, problem_id_range.stop + 1 + ): # end index is inclusive + sample_id = 0 # only evaluate 1 sample for now if not check_if_eval_exists_local(problem_id, sample_id, eval_file_path): total_work.append((problem_id, sample_id)) - print(f"Start evaluation on {len(total_work)} unevaluated samples in range: {problem_id_range}") + print( + f"Start evaluation on {len(total_work)} unevaluated samples in range: {problem_id_range}" + ) # Build Cache on CPU as that is faster if config.build_cache: - compile.batch_compile(total_work, config.to_dict()) + batch_compile(total_work, config.to_dict()) # Batch Eval on multiple GPUs in parallel batch_eval(total_work, config, curr_level_dataset, run_dir, eval_file_path) @@ -433,4 +506,3 @@ def main(config: EvalConfig): if __name__ == "__main__": main() - \ No newline at end of file diff --git a/scripts/generate_and_eval_single_sample.py b/scripts/generate_and_eval_single_sample.py index 3fdb14b5..d6da57f4 100644 --- a/scripts/generate_and_eval_single_sample.py +++ b/scripts/generate_and_eval_single_sample.py @@ -6,10 +6,13 @@ from datasets import load_dataset -from src.dataset import construct_kernelbench_dataset -from src.eval import eval_kernel_against_ref -from src.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template -from src.utils import extract_first_code, query_server, set_gpu_arch, read_file, create_inference_server_from_presets +from kernelbench.dataset import construct_kernelbench_dataset +from kernelbench.eval import eval_kernel_against_ref +from kernelbench.prompt_constructor import ( + prompt_generate_custom_cuda_from_prompt_template, +) +from kernelbench.utils import extract_first_code, set_gpu_arch, read_file +from kernelbench.frameworks import create_inference_server_from_presets """ Generate and evaluate a single sample @@ -20,15 +23,15 @@ torch.set_printoptions(precision=4, threshold=10) + class EvalConfig(Config): def __init__(self): - - self.dataset_src = REQUIRED # either huggingface or local + + self.dataset_src = REQUIRED # either huggingface or local # name of dataset name on Hugging Face self.dataset_name = "ScalingIntelligence/KernelBench" - # Problem Specification self.level = REQUIRED # NOTE: this is the logical index (problem id the problem_name)\ @@ -86,24 +89,31 @@ def main(config: EvalConfig): if config.log: os.makedirs(config.logdir, exist_ok=True) - + # Problem Checks num_problems = len(curr_level_dataset) print(f"Number of problems in Level {config.level}: {num_problems}") - print(f"Start Generation + Evaluation for Level {config.level} Problem {config.problem_id}") - - assert config.problem_id <= num_problems, f"Problem ID {config.problem_id} out of range for Level {config.level}" + print( + f"Start Generation + Evaluation for Level {config.level} Problem {config.problem_id}" + ) + assert ( + config.problem_id <= num_problems + ), f"Problem ID {config.problem_id} out of range for Level {config.level}" # 1. Fetch Problem if config.dataset_src == "huggingface": - curr_problem_row = curr_level_dataset.filter(lambda x: x["problem_id"] == config.problem_id) + curr_problem_row = curr_level_dataset.filter( + lambda x: x["problem_id"] == config.problem_id + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] elif config.dataset_src == "local": - problem_idx_in_dataset = config.problem_id - 1 # due to dataset list being 0-indexed locally + problem_idx_in_dataset = ( + config.problem_id - 1 + ) # due to dataset list being 0-indexed locally ref_arch_path = curr_level_dataset[problem_idx_in_dataset] problem_name = os.path.basename(ref_arch_path) @@ -112,24 +122,31 @@ def main(config: EvalConfig): # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") problem_number = int(problem_name.split("_")[0]) - assert problem_number == config.problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - - + assert ( + problem_number == config.problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" + # 2. Generate Sample # Create inference function with config parameters # We provide some presets in utils but you can also pass in your own, see query_server for more details - inference_server = create_inference_server_from_presets(server_type=config.server_type, - model_name=config.model_name, - temperature=config.temperature, - max_tokens=config.max_tokens, - verbose=config.verbose, - time_generation=True) - - + inference_server = create_inference_server_from_presets( + server_type=config.server_type, + model_name=config.model_name, + temperature=config.temperature, + max_tokens=config.max_tokens, + verbose=config.verbose, + time_generation=True, + ) custom_cuda_prompt = prompt_generate_custom_cuda_from_prompt_template(ref_arch_src) if config.log_prompt: - with open(os.path.join(config.logdir, f"prompt_level_{config.level}_problem_{config.problem_id}.txt"), "w") as f: + with open( + os.path.join( + config.logdir, + f"prompt_level_{config.level}_problem_{config.problem_id}.txt", + ), + "w", + ) as f: f.write(custom_cuda_prompt) # Query server with constructed prompt @@ -137,27 +154,45 @@ def main(config: EvalConfig): custom_cuda = extract_first_code(custom_cuda, ["python", "cpp"]) # check LLM is able to generate custom CUDA code assert custom_cuda is not None, "Custom CUDA code generation failed" - + # this should be optional if config.log: - with open(os.path.join(config.logdir, f"generated_kernel_level_{config.level}_problem_{config.problem_id}.py"), "w") as f: + with open( + os.path.join( + config.logdir, + f"generated_kernel_level_{config.level}_problem_{config.problem_id}.py", + ), + "w", + ) as f: f.write(custom_cuda) # 3. Evaluate Kernel # NOTE: no need to wrap around process here as only a single sample # see batch eval for examples of process isolation kernel_exec_result = eval_kernel_against_ref( - ref_arch_src, custom_cuda, verbose=config.verbose, measure_performance=True, num_correct_trials=5, num_perf_trials=100 + ref_arch_src, + custom_cuda, + verbose=config.verbose, + measure_performance=True, + num_correct_trials=5, + num_perf_trials=100, + ) + + print( + f"Evaluation result for level {config.level} problem {config.problem_id}:\n{kernel_exec_result}" ) - - print(f"Evaluation result for level {config.level} problem {config.problem_id}:\n{kernel_exec_result}") if config.log: - with open(os.path.join(config.logdir, f"eval_result_level_{config.level}_problem_{config.problem_id}.txt"), "a") as f: + with open( + os.path.join( + config.logdir, + f"eval_result_level_{config.level}_problem_{config.problem_id}.txt", + ), + "a", + ) as f: f.write(f"Problem Name: {problem_name}\n") f.write(str(kernel_exec_result)) if __name__ == "__main__": main() - diff --git a/scripts/generate_and_eval_single_sample_modal.py b/scripts/generate_and_eval_single_sample_modal.py index e4a31233..03dafc5f 100644 --- a/scripts/generate_and_eval_single_sample_modal.py +++ b/scripts/generate_and_eval_single_sample_modal.py @@ -7,10 +7,16 @@ from datasets import load_dataset -#from src.dataset import construct_kernelbench_dataset +# from src.dataset import construct_kernelbench_dataset from src.eval import eval_kernel_against_ref from src.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template -from src.utils import extract_first_code, query_server, set_gpu_arch, read_file, create_inference_server_from_presets +from src.utils import ( + extract_first_code, + query_server, + set_gpu_arch, + read_file, + create_inference_server_from_presets, +) app = modal.App("eval_single_sample") @@ -23,17 +29,25 @@ torch.set_printoptions(precision=4, threshold=10) -gpu_arch_mapping = {"L40S": ["Ada"], "H100": ["Hopper"], "A100": ["Ampere"], "L4": ["Ada"], "T4": ["Turing"], "A10G": ["Ampere"]} +gpu_arch_mapping = { + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"], +} + class EvalConfig(Config): def __init__(self): - - self.dataset_src = REQUIRED # either huggingface or local + self.weave_project = "generate_and_eval_single_sample_modal" + + self.dataset_src = REQUIRED # either huggingface or local # name of dataset name on Hugging Face self.dataset_name = "ScalingIntelligence/KernelBench" - # Problem Specification self.level = REQUIRED # NOTE: this is the logical index (problem id the problem_name)\ @@ -45,15 +59,14 @@ def __init__(self): # Construct this from mapping from architecture name to torch cuda arch list in the future # you can either specify SM version or just use the name self.gpu = "L40S" - self.gpu_arch = ['Ada'] - + self.gpu_arch = ["Ada"] # Inference config - self.server_type = "deepseek" - self.model_name = "deepseek-coder" + self.server_type = "anthropic" + self.model_name = "claude-3-5-sonnet-20241022" self.max_tokens = 4096 self.temperature = 0.0 - + # Logging self.logdir = os.path.join(REPO_TOP_DIR, "results/eval_logs") self.verbose = False @@ -72,6 +85,7 @@ def verbose_logging(self): def __repr__(self): return f"EvalConfig({self.to_dict()})" + cuda_version = "12.4.0" # should be no greater than host CUDA version flavor = "devel" # includes full CUDA toolkit operating_sys = "ubuntu22.04" @@ -79,11 +93,7 @@ def __repr__(self): image = ( modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") - .apt_install("git", - "gcc-10", - "g++-10", - "clang" # note i skip a step - ) + .apt_install("git", "gcc-10", "g++-10", "clang") # note i skip a step .pip_install( # required to build flash-attn "anthropic", "numpy", @@ -102,6 +112,7 @@ def __repr__(self): ) ) + @app.cls(image=image) class EvalFunc: @@ -112,45 +123,62 @@ def eval_single_sample_modal(self, ref_arch_src, custom_cuda, verbose, gpu_arch) # see batch eval for examples of process isolation from src.eval import eval_kernel_against_ref from src.utils import set_gpu_arch + set_gpu_arch(gpu_arch) return eval_kernel_against_ref( - ref_arch_src, custom_cuda, verbose=verbose, measure_performance=True, num_correct_trials=5, num_perf_trials=100 + ref_arch_src, + custom_cuda, + verbose=verbose, + measure_performance=True, + num_correct_trials=5, + num_perf_trials=100, ) + @pydra.main(base=EvalConfig) def main(config: EvalConfig): - """ Keep it simple: Generate and evaluate a single sample """ + + import weave + + weave.init(config.weave_project) print(f"Starting Eval with config: {config}") # Configurations - + if config.dataset_src == "huggingface": dataset = load_dataset(config.dataset_name) curr_level_dataset = dataset[f"level_{config.level}"] if config.log: os.makedirs(config.logdir, exist_ok=True) - + # Problem Checks num_problems = len(curr_level_dataset) print(f"Number of problems in Level {config.level}: {num_problems}") - print(f"Start Generation + Evaluation for Level {config.level} Problem {config.problem_id}") - - assert config.problem_id <= num_problems, f"Problem ID {config.problem_id} out of range for Level {config.level}" + print( + f"Start Generation + Evaluation for Level {config.level} Problem {config.problem_id}" + ) + assert ( + config.problem_id <= num_problems + ), f"Problem ID {config.problem_id} out of range for Level {config.level}" # 1. Fetch Problem if config.dataset_src == "huggingface": - curr_problem_row = curr_level_dataset.filter(lambda x: x["problem_id"] == config.problem_id) + curr_problem_row = curr_level_dataset.filter( + lambda x: x["problem_id"] == config.problem_id + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] elif config.dataset_src == "local": - problem_idx_in_dataset = config.problem_id - 1 # due to dataset list being 0-indexed locally + problem_idx_in_dataset = ( + config.problem_id - 1 + ) # due to dataset list being 0-indexed locally ref_arch_path = curr_level_dataset[problem_idx_in_dataset] problem_name = os.path.basename(ref_arch_path) @@ -159,24 +187,31 @@ def main(config: EvalConfig): # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") problem_number = int(problem_name.split("_")[0]) - assert problem_number == config.problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - - + assert ( + problem_number == config.problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" + # 2. Generate Sample # Create inference function with config parameters # We provide some presets in utils but you can also pass in your own, see query_server for more details - inference_server = create_inference_server_from_presets(server_type=config.server_type, - model_name=config.model_name, - temperature=config.temperature, - max_tokens=config.max_tokens, - verbose=config.verbose, - time_generation=True) - - + inference_server = create_inference_server_from_presets( + server_type=config.server_type, + model_name=config.model_name, + temperature=config.temperature, + max_tokens=config.max_tokens, + verbose=config.verbose, + time_generation=True, + ) custom_cuda_prompt = prompt_generate_custom_cuda_from_prompt_template(ref_arch_src) if config.log_prompt: - with open(os.path.join(config.logdir, f"prompt_level_{config.level}_problem_{config.problem_id}.txt"), "w") as f: + with open( + os.path.join( + config.logdir, + f"prompt_level_{config.level}_problem_{config.problem_id}.txt", + ), + "w", + ) as f: f.write(custom_cuda_prompt) # Query server with constructed prompt @@ -184,21 +219,40 @@ def main(config: EvalConfig): custom_cuda = extract_first_code(custom_cuda, ["python", "cpp"]) # check LLM is able to generate custom CUDA code assert custom_cuda is not None, "Custom CUDA code generation failed" - + # this should be optional if config.log: - with open(os.path.join(config.logdir, f"generated_kernel_level_{config.level}_problem_{config.problem_id}.py"), "w") as f: + with open( + os.path.join( + config.logdir, + f"generated_kernel_level_{config.level}_problem_{config.problem_id}.py", + ), + "w", + ) as f: f.write(custom_cuda) with app.run(): - kernel_exec_result = EvalFunc.with_options(gpu=config.gpu)().eval_single_sample_modal.remote(ref_arch_src, custom_cuda, config.verbose, gpu_arch_mapping[config.gpu]) - - print(f"Evaluation result for level {config.level} problem {config.problem_id}:\n{kernel_exec_result}") - + kernel_exec_result = EvalFunc.with_options( + gpu=config.gpu + )().eval_single_sample_modal.remote( + ref_arch_src, custom_cuda, config.verbose, gpu_arch_mapping[config.gpu] + ) + + print( + f"Evaluation result for level {config.level} problem {config.problem_id}:\n{kernel_exec_result}" + ) + if config.log: - with open(os.path.join(config.logdir, f"eval_result_level_{config.level}_problem_{config.problem_id}.txt"), "a") as f: + with open( + os.path.join( + config.logdir, + f"eval_result_level_{config.level}_problem_{config.problem_id}.txt", + ), + "a", + ) as f: f.write(f"Problem Name: {problem_name}\n") f.write(str(kernel_exec_result)) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/generate_baseline_time.py b/scripts/generate_baseline_time.py index 8846ab56..de7c8477 100644 --- a/scripts/generate_baseline_time.py +++ b/scripts/generate_baseline_time.py @@ -1,14 +1,13 @@ import torch import numpy as np -from src.eval import ( +from kernelbench.eval import ( load_original_model_and_inputs, time_execution_with_cuda_event, get_timing_stats, set_seed, - fetch_ref_arch_from_problem_id, ) -from src.dataset import construct_problem_dataset_from_problem_dir -from src.utils import read_file +from kernelbench.dataset import construct_problem_dataset_from_problem_dir +from kernelbench.utils import read_file import os import json from tqdm import tqdm @@ -26,7 +25,7 @@ Torch Compile with various modes https://pytorch.org/docs/main/generated/torch.compile.html - torch.compile: backend="inductor", mode="default" (this is usually what happens when you do torch.compile(model)) -- torch.compile: backend="inductor", mode="reduce-overhead" +- torch.compile: backend="inductor", mode="reduce-overhead" - torch.compile: backend="inductor", mode="max-autotune" - torch.compile: backend="inductor", mode="max-autotune-no-cudagraphs" @@ -46,8 +45,9 @@ TIMING_DIR = os.path.join(REPO_TOP_PATH, "results", "timing") -def fetch_ref_arch_from_dataset(dataset: list[str], - problem_id: int) -> tuple[str, str, str]: +def fetch_ref_arch_from_dataset( + dataset: list[str], problem_id: int +) -> tuple[str, str, str]: """ Fetch the reference architecture from the problem directory problem_id should be logical index (1-indexed), matching the problem_id in the problem_name @@ -58,14 +58,14 @@ def fetch_ref_arch_from_dataset(dataset: list[str], ref_arch_src: str, the source code of the reference architecture """ ref_arch_path = None - + for file in dataset: if file.split("/")[-1].split("_")[0] == str(problem_id): ref_arch_path = file break if ref_arch_path is None: raise ValueError(f"No reference architecture found for problem_id {problem_id}") - + ref_arch_src = read_file(ref_arch_path) ref_arch_name = ref_arch_path.split("/")[-1] @@ -73,14 +73,14 @@ def fetch_ref_arch_from_dataset(dataset: list[str], def measure_program_time( - ref_arch_name: str, - ref_arch_src: str, - num_trials: int = 100, - use_torch_compile: bool = False, - torch_compile_backend: str="inductor", - torch_compile_options: str="default", - device: torch.device="cuda:0", - verbose: bool = False, + ref_arch_name: str, + ref_arch_src: str, + num_trials: int = 100, + use_torch_compile: bool = False, + torch_compile_backend: str = "inductor", + torch_compile_options: str = "default", + device: torch.device = "cuda:0", + verbose: bool = False, ) -> dict: """ Measure the time of a KernelBench reference architecture @@ -107,13 +107,17 @@ def measure_program_time( # Initialize PyTorch model, use this for eager mode execution model = Model(*init_inputs) - + if use_torch_compile: - print(f"Using torch.compile to compile model {ref_arch_name} with {torch_compile_backend} backend and {torch_compile_options} mode") - model = torch.compile(model, backend=torch_compile_backend, mode=torch_compile_options) + print( + f"Using torch.compile to compile model {ref_arch_name} with {torch_compile_backend} backend and {torch_compile_options} mode" + ) + model = torch.compile( + model, backend=torch_compile_backend, mode=torch_compile_options + ) else: print(f"Using PyTorch Eager Execution on {ref_arch_name}") - + model = model.cuda(device=device) torch.cuda.synchronize(device=device) elapsed_times = time_execution_with_cuda_event( @@ -123,25 +127,26 @@ def measure_program_time( if verbose: print(f"{ref_arch_name} {runtime_stats}") - + return runtime_stats except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") - -def record_baseline_times(use_torch_compile: bool = False, - torch_compile_backend: str="inductor", - torch_compile_options: str="default", - file_name: str="baseline_time.json"): +def record_baseline_times( + use_torch_compile: bool = False, + torch_compile_backend: str = "inductor", + torch_compile_options: str = "default", + file_name: str = "baseline_time.json", +): """ - Generate baseline time for KernelBench, + Generate baseline time for KernelBench, configure profiler options for PyTorch save to specified file """ device = torch.device("cuda:0") json_results = {} - + for level in [1, 2, 3]: PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) @@ -149,7 +154,9 @@ def record_baseline_times(use_torch_compile: bool = False, num_problems = len(dataset) for problem_id in tqdm(range(1, num_problems + 1)): - ref_arch_path, ref_arch_name, ref_arch_src = fetch_ref_arch_from_dataset(dataset, problem_id) + ref_arch_path, ref_arch_name, ref_arch_src = fetch_ref_arch_from_dataset( + dataset, problem_id + ) runtime_stats = measure_program_time( ref_arch_name=ref_arch_name, ref_arch_src=ref_arch_src, @@ -157,7 +164,7 @@ def record_baseline_times(use_torch_compile: bool = False, torch_compile_backend=torch_compile_backend, torch_compile_options=torch_compile_options, device=device, - verbose=False # do not print + verbose=False, # do not print ) json_results[f"level{level}"][ref_arch_name] = runtime_stats @@ -168,6 +175,7 @@ def record_baseline_times(use_torch_compile: bool = False, json.dump(json_results, f) return json_results + def test_measure_particular_program(level_num: int, problem_id: int): """ Test measure_program_time on a particular program @@ -177,7 +185,9 @@ def test_measure_particular_program(level_num: int, problem_id: int): PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level_num)) dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) - ref_arch_path, ref_arch_name, ref_arch_src = fetch_ref_arch_from_dataset(dataset, problem_id) + ref_arch_path, ref_arch_name, ref_arch_src = fetch_ref_arch_from_dataset( + dataset, problem_id + ) exec_stats = measure_program_time( ref_arch_name=ref_arch_name, @@ -186,7 +196,7 @@ def test_measure_particular_program(level_num: int, problem_id: int): torch_compile_backend="inductor", torch_compile_options="default", device=device, - verbose=False + verbose=False, ) print(f"Execution time for {ref_arch_name}: {exec_stats}") @@ -195,37 +205,49 @@ def test_measure_particular_program(level_num: int, problem_id: int): if __name__ == "__main__": # DEBUG and simple testing # test_measure_particular_program(2, 28) - - # Replace this with whatever hardware you are running on + + # Replace this with whatever hardware you are running on hardware_name = "L40S_matx3" - input(f"You are about to start recording baseline time for {hardware_name}, press Enter to continue...") + input( + f"You are about to start recording baseline time for {hardware_name}, press Enter to continue..." + ) # Systematic recording of baseline time if os.path.exists(os.path.join(TIMING_DIR, hardware_name)): - input(f"Directory {hardware_name} already exists, Are you sure you want to overwrite? Enter to continue...") + input( + f"Directory {hardware_name} already exists, Are you sure you want to overwrite? Enter to continue..." + ) # 1. Record Torch Eager - record_baseline_times(use_torch_compile=False, - torch_compile_backend=None, - torch_compile_options=None, - file_name=f"{hardware_name}/baseline_time_torch.json") - - # 2. Record Torch Compile using Inductor - for torch_compile_mode in ["default", "reduce-overhead", "max-autotune", "max-autotune-no-cudagraphs"]: - record_baseline_times(use_torch_compile=True, - torch_compile_backend="inductor", - torch_compile_options=torch_compile_mode, - file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_{torch_compile_mode}.json") - - # 3. Record Torch Compile using cudagraphs - record_baseline_times(use_torch_compile=True, - torch_compile_backend="cudagraphs", - torch_compile_options=None, - file_name=f"{hardware_name}/baseline_time_torch_compile_cudagraphs.json") - + record_baseline_times( + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + file_name=f"{hardware_name}/baseline_time_torch.json", + ) + # 2. Record Torch Compile using Inductor + for torch_compile_mode in [ + "default", + "reduce-overhead", + "max-autotune", + "max-autotune-no-cudagraphs", + ]: + record_baseline_times( + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options=torch_compile_mode, + file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_{torch_compile_mode}.json", + ) + # 3. Record Torch Compile using cudagraphs + record_baseline_times( + use_torch_compile=True, + torch_compile_backend="cudagraphs", + torch_compile_options=None, + file_name=f"{hardware_name}/baseline_time_torch_compile_cudagraphs.json", + ) # Random debuging # get_torch_compile_triton(2, 12) @@ -236,8 +258,6 @@ def test_measure_particular_program(level_num: int, problem_id: int): # get_time(2, 43, torch_compile=True) - - ################################################################################ # Deprecated ################################################################################ @@ -269,7 +289,7 @@ def get_time_old(level_num, problem_id, num_trials=100, torch_compile=False): for x in init_inputs ] model = Model(*init_inputs) - + if torch_compile: model = torch.compile(model) print("Compiled model Done") @@ -284,5 +304,3 @@ def get_time_old(level_num, problem_id, num_trials=100, torch_compile=False): return (ref_arch_name, runtime_stats) except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") - - diff --git a/scripts/generate_baseline_time_modal.py b/scripts/generate_baseline_time_modal.py index 240d6c7b..37a83d5e 100644 --- a/scripts/generate_baseline_time_modal.py +++ b/scripts/generate_baseline_time_modal.py @@ -1,20 +1,18 @@ import torch import numpy as np -from src.eval import ( +from kernelbench.eval import ( load_original_model_and_inputs, time_execution_with_cuda_event, get_timing_stats, set_seed, - fetch_ref_arch_from_problem_id, ) -from src.dataset import construct_problem_dataset_from_problem_dir -from src.utils import read_file +from kernelbench.dataset import construct_problem_dataset_from_problem_dir +from kernelbench.utils import read_file import os import json from tqdm import tqdm import multiprocessing as mp import time -import einops """ Generate baseline time for KernelBench @@ -29,7 +27,7 @@ Torch Compile with various modes https://pytorch.org/docs/main/generated/torch.compile.html - torch.compile: backend="inductor", mode="default" (this is usually what happens when you do torch.compile(model)) -- torch.compile: backend="inductor", mode="reduce-overhead" +- torch.compile: backend="inductor", mode="reduce-overhead" - torch.compile: backend="inductor", mode="max-autotune" - torch.compile: backend="inductor", mode="max-autotune-no-cudagraphs" @@ -50,8 +48,17 @@ # Modal Infra import modal + app = modal.App("generate_baseline_modal") -gpu_arch_mapping = {"L40S": ["Ada"], "H100": ["Hopper"], "A100": ["Ampere"], "A100-80GB": ["Ampere"], "L4": ["Ada"], "T4": ["Turing"], "A10G": ["Ampere"]} +gpu_arch_mapping = { + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "A100-80GB": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"], +} batch_size = 10 gpu = "L40S" timeout = 1800 @@ -62,11 +69,7 @@ image = ( modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") - .apt_install("git", - "gcc-10", - "g++-10", - "clang" # note i skip a step - ) + .apt_install("git", "gcc-10", "g++-10", "clang") # note i skip a step .pip_install( # required to build flash-attn "anthropic", "numpy", @@ -84,12 +87,10 @@ "utils", "einops", ) - .add_local_dir( - KERNEL_BENCH_PATH, - remote_path="/root/KernelBench" - ) + .add_local_dir(KERNEL_BENCH_PATH, remote_path="/root/KernelBench") ) + def write_batch_to_json(entries_to_write: list, f_path: str): """ Write batch of data to JSON file (append or overwrite, do not completely overwrite) @@ -97,11 +98,11 @@ def write_batch_to_json(entries_to_write: list, f_path: str): # Read existing data if file exists existing_data = {} if os.path.exists(f_path): - with open(f_path, 'r') as f_r: + with open(f_path, "r") as f_r: existing_data = json.load(f_r) - + # Add new entries - for (level, problem, entry) in entries_to_write: + for level, problem, entry in entries_to_write: # Initialize nested structure if it doesn't exist if str(level) not in existing_data: existing_data[level] = {} @@ -114,11 +115,13 @@ def write_batch_to_json(entries_to_write: list, f_path: str): # Write back combined data with open(f_path, "w") as f_w: json.dump(existing_data, f_w, indent=4) - + print(f"[INFO] Wrote {len(entries_to_write)} entries to {f_path}") -def fetch_ref_arch_from_dataset(dataset: list[str], - problem_id: int) -> tuple[str, str, str]: + +def fetch_ref_arch_from_dataset( + dataset: list[str], problem_id: int +) -> tuple[str, str, str]: """ Fetch the reference architecture from the problem directory problem_id should be logical index (1-indexed), matching the problem_id in the problem_name @@ -129,33 +132,36 @@ def fetch_ref_arch_from_dataset(dataset: list[str], ref_arch_src: str, the source code of the reference architecture """ ref_arch_path = None - + for file in dataset: if file.split("/")[-1].split("_")[0] == str(problem_id): ref_arch_path = file break if ref_arch_path is None: raise ValueError(f"No reference architecture found for problem_id {problem_id}") - + ref_arch_src = read_file(ref_arch_path) ref_arch_name = ref_arch_path.split("/")[-1] return (ref_arch_path, ref_arch_name, ref_arch_src) + @app.cls(image=image, container_idle_timeout=5) class EvalFunc: @modal.method() def measure_program_time( - self, - ref_arch_name: str, - ref_arch_src: str, - num_trials: int = 100, - use_torch_compile: bool = False, - torch_compile_backend: str="inductor", - torch_compile_options: str="default", - device: torch.device = torch.cuda.current_device() if torch.cuda.is_available() else None, - verbose: bool = False, + self, + ref_arch_name: str, + ref_arch_src: str, + num_trials: int = 100, + use_torch_compile: bool = False, + torch_compile_backend: str = "inductor", + torch_compile_options: str = "default", + device: torch.device = ( + torch.cuda.current_device() if torch.cuda.is_available() else None + ), + verbose: bool = False, ): """ Measure the time of a KernelBench reference architecture @@ -182,37 +188,51 @@ def measure_program_time( # Initialize PyTorch model, use this for eager mode execution model = Model(*init_inputs) - + if use_torch_compile: - print(f"Using torch.compile to compile model {ref_arch_name} with {torch_compile_backend} backend and {torch_compile_options} mode") - model = torch.compile(model, backend=torch_compile_backend, mode=torch_compile_options) + print( + f"Using torch.compile to compile model {ref_arch_name} with {torch_compile_backend} backend and {torch_compile_options} mode" + ) + model = torch.compile( + model, backend=torch_compile_backend, mode=torch_compile_options + ) else: print(f"Using PyTorch Eager Execution on {ref_arch_name}") - + model = model.cuda(device=device) torch.cuda.synchronize(device=device) elapsed_times = time_execution_with_cuda_event( - model, *inputs, num_trials=num_trials, verbose=verbose, device=device + model, + *inputs, + num_trials=num_trials, + verbose=verbose, + device=device, ) runtime_stats = get_timing_stats(elapsed_times, device=device) if verbose: print(f"{ref_arch_name} {runtime_stats}") - + return runtime_stats except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") + def measure_program_time_wrapper(*args, **kwargs): with app.run(): - return EvalFunc.with_options(gpu=gpu)().measure_program_time.remote(*args, **kwargs) + return EvalFunc.with_options(gpu=gpu)().measure_program_time.remote( + *args, **kwargs + ) -def record_baseline_times(use_torch_compile: bool = False, - torch_compile_backend: str="inductor", - torch_compile_options: str="default", - file_name: str="baseline_time.json"): + +def record_baseline_times( + use_torch_compile: bool = False, + torch_compile_backend: str = "inductor", + torch_compile_options: str = "default", + file_name: str = "baseline_time.json", +): """ - Generate baseline time for KernelBench, + Generate baseline time for KernelBench, configure profiler options for PyTorch save to specified file """ @@ -222,12 +242,17 @@ def record_baseline_times(use_torch_compile: bool = False, PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) num_problems = len(dataset) - total_work = [(i, *fetch_ref_arch_from_dataset(dataset, i)) for i in list(range(1, num_problems + 1))] + total_work = [ + (i, *fetch_ref_arch_from_dataset(dataset, i)) + for i in list(range(1, num_problems + 1)) + ] with tqdm(total=len(total_work), desc="Processing batches") as pbar: while len(total_work) > 0: curr_work_batch = total_work[:batch_size] - total_work = total_work[batch_size:] # pop the first batch_size elements + total_work = total_work[ + batch_size: + ] # pop the first batch_size elements with mp.Pool() as pool: @@ -240,9 +265,14 @@ def record_baseline_times(use_torch_compile: bool = False, torch_compile_backend, torch_compile_options, torch.device(f"cuda:0"), - False # do not print + False, # do not print ) - for i, (p_id, ref_arch_path, ref_arch_name, ref_arch_src) in enumerate(curr_work_batch) + for i, ( + p_id, + ref_arch_path, + ref_arch_name, + ref_arch_src, + ) in enumerate(curr_work_batch) ] start_time = time.time() @@ -261,14 +291,16 @@ def record_baseline_times(use_torch_compile: bool = False, elapsed_time = time.time() - start_time remaining_time = max(0, batch_timeout - elapsed_time) result = async_result.get(timeout=remaining_time) - json_results.append((f"level{level}", ref_arch_name, result)) - + json_results.append( + (f"level{level}", ref_arch_name, result) + ) + except mp.TimeoutError: print( f"[WARNING] Evaluation TIMED OUT for Problem ID: {problem_id}" ) json_results.append((f"level{level}", ref_arch_name, None)) - + except Exception as e: print( f"[ERROR] Evaluation FAILED for Problem ID: {problem_id}: {str(e)}" @@ -286,7 +318,7 @@ def record_baseline_times(use_torch_compile: bool = False, # DEBUG and simple testing # test_measure_particular_program(2, 28) gpu = "A10G" - # Replace this with whatever hardware you are running on + # Replace this with whatever hardware you are running on hardware_name = f"{gpu}_modal" print(f"Generating baseline time for {hardware_name}") # input(f"You are about to start recording baseline time for {hardware_name}, press Enter to continue...") @@ -296,31 +328,32 @@ def record_baseline_times(use_torch_compile: bool = False, # input(f"Directory {hardware_name} already exists, Are you sure you want to overwrite? Enter to continue...") # 1. Record Torch Eager - record_baseline_times(use_torch_compile=False, - torch_compile_backend=None, - torch_compile_options=None, - file_name=f"{hardware_name}/baseline_time_torch.json") - - record_baseline_times(use_torch_compile=True, - torch_compile_backend="inductor", - torch_compile_options="default", - file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_default.json") - + record_baseline_times( + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + file_name=f"{hardware_name}/baseline_time_torch.json", + ) + + record_baseline_times( + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_default.json", + ) + # 2. Record Torch Compile using Inductor # for torch_compile_mode in ["default", "reduce-overhead", "max-autotune", "max-autotune-no-cudagraphs"]: - # record_baseline_times(use_torch_compile=True, + # record_baseline_times(use_torch_compile=True, # torch_compile_backend="inductor", - # torch_compile_options=torch_compile_mode, + # torch_compile_options=torch_compile_mode, # file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_{torch_compile_mode}.json") - + # 3. Record Torch Compile using cudagraphs - # record_baseline_times(use_torch_compile=True, + # record_baseline_times(use_torch_compile=True, # torch_compile_backend="cudagraphs", - # torch_compile_options=None, + # torch_compile_options=None, # file_name=f"{hardware_name}/baseline_time_torch_compile_cudagraphs.json") - - - # Random debuging # get_torch_compile_triton(2, 12) @@ -331,8 +364,6 @@ def record_baseline_times(use_torch_compile: bool = False, # get_time(2, 43, torch_compile=True) - - ################################################################################ # Deprecated ################################################################################ @@ -364,7 +395,7 @@ def get_time_old(level_num, problem_id, num_trials=100, torch_compile=False): for x in init_inputs ] model = Model(*init_inputs) - + if torch_compile: model = torch.compile(model) print("Compiled model Done") @@ -379,5 +410,3 @@ def get_time_old(level_num, problem_id, num_trials=100, torch_compile=False): return (ref_arch_name, runtime_stats) except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") - - diff --git a/scripts/generate_samples.py b/scripts/generate_samples.py index 0d552b8b..a079911e 100644 --- a/scripts/generate_samples.py +++ b/scripts/generate_samples.py @@ -1,17 +1,17 @@ +import os +from dataclasses import dataclass + import pydra from pydra import REQUIRED, Config -import os, sys import torch -import json -from dataclasses import dataclass - - from datasets import load_dataset -from src.dataset import construct_kernelbench_dataset -from src.eval import eval_kernel_against_ref -from src.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template -from src.utils import extract_first_code, set_gpu_arch, read_file, create_inference_server_from_presets, maybe_multithread +from kernelbench.dataset import construct_kernelbench_dataset +from kernelbench.prompt_constructor import ( + prompt_generate_custom_cuda_from_prompt_template, +) +from kernelbench.utils import read_file, maybe_multithread +from kernelbench.llm_utils import create_inference_server_from_presets """ Batch Generate Samples for Particular Level @@ -23,21 +23,25 @@ torch.set_printoptions(precision=4, threshold=10) + class GenerationConfig(Config): def __init__(self): - - self.dataset_src = REQUIRED # either huggingface or local + + self.dataset_src = REQUIRED # either huggingface or local # name of dataset name on Hugging Face self.dataset_name = "ScalingIntelligence/KernelBench" # Problem Specification self.level = REQUIRED - + # subset of problems to generate, otherwise generate on all problems in the level - self.subset = (None, None) # (problem_id, problem_name), these are the logical index + self.subset = ( + None, + None, + ) # (problem_id, problem_name), these are the logical index - self.run_name = REQUIRED # name of the run + self.run_name = REQUIRED # name of the run # num of thread pool to call inference server in parallel self.num_workers = 1 @@ -48,13 +52,13 @@ def __init__(self): self.model_name = "deepseek-coder" self.max_tokens = 4096 self.temperature = 0.0 - + # Logging # Top Directory to Store Runs self.runs_dir = os.path.join(REPO_TOP_DIR, "runs") - + self.verbose = False - self.store_type = "local" # TODO: add Database Integration + self.store_type = "local" # TODO: add Database Integration # Future support # Migrate Monkeys code base to KernelBench @@ -68,23 +72,34 @@ def greedy(self): def __repr__(self): return f"EvalConfig({self.to_dict()})" - + @dataclass class WorkArgs: - problem_id: int # logically indexed + problem_id: int # logically indexed sample_id: int -def generate_sample_single(work: WorkArgs, config: GenerationConfig, dataset, inference_server: callable, run_dir: str) -> bool: + +def generate_sample_single( + work: WorkArgs, + config: GenerationConfig, + dataset, + inference_server: callable, + run_dir: str, +) -> bool: # 1. Fetch Problem if config.dataset_src == "huggingface": - curr_problem_row = dataset.filter(lambda x: x["problem_id"] == work.problem_id, desc=None) + curr_problem_row = dataset.filter( + lambda x: x["problem_id"] == work.problem_id, desc=None + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] elif config.dataset_src == "local": - problem_idx_in_dataset = work.problem_id - 1 # due to dataset list being 0-indexed locally + problem_idx_in_dataset = ( + work.problem_id - 1 + ) # due to dataset list being 0-indexed locally ref_arch_path = dataset[problem_idx_in_dataset] problem_name = os.path.basename(ref_arch_path) @@ -92,14 +107,17 @@ def generate_sample_single(work: WorkArgs, config: GenerationConfig, dataset, in # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") problem_number = int(problem_name.split("_")[0]) - assert problem_number == work.problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - - + assert ( + problem_number == work.problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - # Construct Prompt + # Construct Prompt custom_cuda_prompt = prompt_generate_custom_cuda_from_prompt_template(ref_arch_src) if config.log_prompt: - prompt_path = os.path.join(run_dir, f"level_{config.level}_problem_{work.problem_id}_sample_{work.sample_id}_prompt.txt") + prompt_path = os.path.join( + run_dir, + f"level_{config.level}_problem_{work.problem_id}_sample_{work.sample_id}_prompt.txt", + ) with open(prompt_path, "w") as f: f.write(custom_cuda_prompt) @@ -110,17 +128,28 @@ def generate_sample_single(work: WorkArgs, config: GenerationConfig, dataset, in assert custom_cuda is not None, "Custom CUDA code generation failed" if config.verbose: - print(f"Generated sample {work.sample_id} for problem {problem_number}: {problem_name}") + print( + f"Generated sample {work.sample_id} for problem {problem_number}: {problem_name}" + ) # Store to local file - kernel_path = os.path.join(run_dir, f"level_{config.level}_problem_{work.problem_id}_sample_{work.sample_id}_kernel.py") + kernel_path = os.path.join( + run_dir, + f"level_{config.level}_problem_{work.problem_id}_sample_{work.sample_id}_kernel.py", + ) with open(kernel_path, "w") as f: f.write(custom_cuda) - + return True - -def generate_sample_launcher(work: WorkArgs, config: GenerationConfig, dataset, inference_server: callable, run_dir: str): + +def generate_sample_launcher( + work: WorkArgs, + config: GenerationConfig, + dataset, + inference_server: callable, + run_dir: str, +): try: return generate_sample_single(work, config, dataset, inference_server, run_dir) except Exception as e: @@ -128,13 +157,17 @@ def generate_sample_launcher(work: WorkArgs, config: GenerationConfig, dataset, return None -def check_kernel_exists(run_dir: str, level: int, problem_id: int, sample_id: int) -> bool: +def check_kernel_exists( + run_dir: str, level: int, problem_id: int, sample_id: int +) -> bool: """ Check if a kernel for a given problem and sample ID already exists in the run directory """ - kernel_path = os.path.join(run_dir, f"level_{level}_problem_{problem_id}_sample_{sample_id}_kernel.py") + kernel_path = os.path.join( + run_dir, f"level_{level}_problem_{problem_id}_sample_{sample_id}_kernel.py" + ) return os.path.exists(kernel_path) - + @pydra.main(base=GenerationConfig) def main(config: GenerationConfig): @@ -151,63 +184,69 @@ def main(config: GenerationConfig): elif config.dataset_src == "local": curr_level_dataset = construct_kernelbench_dataset(config.level) - num_problems_in_level = len(curr_level_dataset) if config.subset == (None, None): problem_id_range = range(1, num_problems_in_level) else: - assert config.subset[0] >= 1 and config.subset[1] <= num_problems_in_level, f"Subset range {config.subset} out of range for Level {config.level}" + assert ( + config.subset[0] >= 1 and config.subset[1] <= num_problems_in_level + ), f"Subset range {config.subset} out of range for Level {config.level}" problem_id_range = range(config.subset[0], config.subset[1]) - print(f"Generating on 1 sample each for level {config.level} problems: {problem_id_range}") + print( + f"Generating on 1 sample each for level {config.level} problems: {problem_id_range}" + ) # set up run directory run_dir = os.path.join(config.runs_dir, config.run_name) os.makedirs(run_dir, exist_ok=True) pydra.save_yaml(config.to_dict(), os.path.join(run_dir, "generation_config.yaml")) - - assert config.store_type == "local", "supporting local file-system based storage for now" # database integreation coming soon, need to migrate from CUDA Monkeys code + assert ( + config.store_type == "local" + ), "supporting local file-system based storage for now" # database integreation coming soon, need to migrate from CUDA Monkeys code problems_to_run = [] - for problem_id in range(problem_id_range.start, problem_id_range.stop + 1): # end index is inclusive + for problem_id in range( + problem_id_range.start, problem_id_range.stop + 1 + ): # end index is inclusive # assume sample id is 0 for now if not check_kernel_exists(run_dir, config.level, problem_id, sample_id=0): problems_to_run.append( - WorkArgs( - problem_id=int(problem_id), - sample_id=0 # fix to 0 for now - ) - ) - + WorkArgs(problem_id=int(problem_id), sample_id=0) # fix to 0 for now + ) # Create inference function with config parameters # We provide some presets in utils but you can also pass in your own, see query_server for more details - inference_server = create_inference_server_from_presets(server_type=config.server_type, - model_name=config.model_name, - temperature=config.temperature, - max_tokens=config.max_tokens, - verbose=config.verbose) + inference_server = create_inference_server_from_presets( + server_type=config.server_type, + model_name=config.model_name, + temperature=config.temperature, + max_tokens=config.max_tokens, + verbose=config.verbose, + ) # Launch workers - generation_results = maybe_multithread(generate_sample_launcher, - problems_to_run, - config.num_workers, - time_interval=config.api_query_interval, - # extra args - config=config, - dataset=curr_level_dataset, - inference_server=inference_server, - run_dir=run_dir - ) - + generation_results = maybe_multithread( + generate_sample_launcher, + problems_to_run, + config.num_workers, + time_interval=config.api_query_interval, + # extra args + config=config, + dataset=curr_level_dataset, + inference_server=inference_server, + run_dir=run_dir, + ) + num_generated_samples = len(generation_results) total_problems = len(problems_to_run) num_failed_problems = total_problems - num_generated_samples - print(f"Generated {num_generated_samples} samples for total {total_problems} problems, Please retry for the {num_failed_problems} failed problems.") + print( + f"Generated {num_generated_samples} samples for total {total_problems} problems, Please retry for the {num_failed_problems} failed problems." + ) if __name__ == "__main__": main() - diff --git a/scripts/inspect_baseline.py b/scripts/inspect_baseline.py index e7811f64..8c0ee428 100644 --- a/scripts/inspect_baseline.py +++ b/scripts/inspect_baseline.py @@ -1,19 +1,15 @@ -import torch -import logging import os -import sys +import logging + import numpy as np -from src.eval import ( +import torch + +from kernelbench.eval import ( load_original_model_and_inputs, - time_execution_with_cuda_event, - get_timing_stats, set_seed, fetch_ref_arch_from_problem_id, ) -from src.dataset import construct_problem_dataset_from_problem_dir -import os, sys -import logging -import json +from kernelbench.dataset import construct_problem_dataset_from_problem_dir device = torch.device("cuda:0") @@ -26,27 +22,35 @@ KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") -assert torch.cuda.get_device_capability() >= (7, 0), "torch.compile is not supported on this device." +assert torch.cuda.get_device_capability() >= ( + 7, + 0, +), "torch.compile is not supported on this device." + def get_toy_torch_compile_fn_and_inputs(): @torch.compile() def fn(x, y): z = x + y return z + 2 + inputs = (torch.ones(2, 2, device="cuda"), torch.zeros(2, 2, device="cuda")) return fn, inputs -def inspect_torch_compile(fn, inputs, output_dir="results/triton_code", filename="optimized_kernel"): +def inspect_torch_compile( + fn, inputs, output_dir="results/triton_code", filename="optimized_kernel" +): """ Benchmark a torch.compile'd function by viewing dynamo tracing, traced graph, fusion decisions and generated code. - + Args: fn: The compiled function to benchmark inputs: Tuple of input tensors to the function output_dir: Directory to save generated code """ + def separator(name): print(f"==================={name}=========================") torch._dynamo.reset() @@ -56,7 +60,7 @@ def separator(name): torch._logging.set_logs(dynamo=logging.DEBUG) fn(*inputs) - separator("Traced Graph") + separator("Traced Graph") # View traced graph torch._logging.set_logs(graph=True) fn(*inputs) @@ -69,34 +73,36 @@ def separator(name): separator("Output Code") # View output code generated by inductor os.makedirs(output_dir, exist_ok=True) - + # Create a custom logging handler to capture the output class OutputCodeHandler(logging.Handler): def __init__(self, file): super().__init__() self.file = file - + def emit(self, record): - self.file.write(self.format(record) + '\n') + self.file.write(self.format(record) + "\n") with open(f"{output_dir}/{filename}.py", "w") as f: # Set up logging handler handler = OutputCodeHandler(f) logging.getLogger("torch._inductor.codecache").addHandler(handler) - + torch._logging.set_logs(output_code=True) fn(*inputs) # Run the function - + # Clean up handler logging.getLogger("torch._inductor.codecache").removeHandler(handler) separator("") - + + def fetch_ref_arch_from_level_problem_id(level_num, problem_id, with_name=False): PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level_num)) dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) return fetch_ref_arch_from_problem_id(problem_id, dataset, with_name) + def inspect_torch_compile_triton(level_num, problem_id): ref_arch_name, ref_arch_src = fetch_ref_arch_from_level_problem_id( level_num, problem_id, with_name=True @@ -138,10 +144,15 @@ def inspect_baseline_torch_compile(level_num, problem_id): for x in init_inputs ] model = Model(*init_inputs) - + model = torch.compile(model) model = model.cuda(device=device) - inspect_torch_compile(model, inputs, output_dir="results/triton_code", filename=f"level{level_num}_problem{problem_id}_triton") + inspect_torch_compile( + model, + inputs, + output_dir="results/triton_code", + filename=f"level{level_num}_problem{problem_id}_triton", + ) except Exception as e: print(f"[Eval] Error in Inspecting Torch Compile: {e}") @@ -150,5 +161,3 @@ def inspect_baseline_torch_compile(level_num, problem_id): # fn, inputs = get_toy_torch_compile_fn_and_inputs() inspect_baseline_torch_compile(2, 43) - - diff --git a/scripts/inspect_kernel_pytorch_profiler.py b/scripts/inspect_kernel_pytorch_profiler.py index 3e93637c..4ba796c5 100644 --- a/scripts/inspect_kernel_pytorch_profiler.py +++ b/scripts/inspect_kernel_pytorch_profiler.py @@ -1,36 +1,38 @@ -import torch -from torch.profiler import profile, record_function, ProfilerActivity -import logging -import os -import io - - """ For analysis Inspect the operator and kernel breakdown of model-generated kernel to a particular problem Using PyTorch Profiler """ -REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -device = "cuda:0" +import os +import io +import logging +import torch +from torch.profiler import profile, ProfilerActivity -from src.utils import read_file -from src.eval import ( +from kernelbench.utils import read_file +from kernelbench.eval import ( load_custom_model, load_original_model_and_inputs, set_seed, ) -def get_torch_profiler_info(ref_arch_src: str, - kernel_src: str, - build_dir: str, - device: torch.device, - num_trials: int = 100, - table_row_limit: int = 10, - seed_num: int = 42)->str: +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +device = "cuda:0" + + +def get_torch_profiler_info( + ref_arch_src: str, + kernel_src: str, + build_dir: str, + device: torch.device, + num_trials: int = 100, + table_row_limit: int = 10, + seed_num: int = 42, +) -> str: """ Get the profiler info for a particular kernel Given a KernelBench solution to a problem, we want to profile the kernel @@ -45,9 +47,9 @@ def get_torch_profiler_info(ref_arch_src: str, Notes about profiling: - - We do not set p.toggle_collection_dynamic explicitly, + - We do not set p.toggle_collection_dynamic explicitly, - We only collect CUDA activity (ProfilerActivity.CUDA), as we are only interested in the kernel - + """ assert torch.cuda.is_available(), "CUDA is not available, cannot run Torch Profiler" @@ -61,14 +63,12 @@ def get_torch_profiler_info(ref_arch_src: str, inputs = get_inputs() init_inputs = get_init_inputs() inputs = [ - x.cuda(device=device) if isinstance(x, torch.Tensor) else x - for x in inputs + x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in inputs ] init_inputs = [ - x.cuda(device=device) if isinstance(x, torch.Tensor) else x - for x in init_inputs + x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in init_inputs ] - + ModelNew = load_custom_model(kernel_src, context, build_dir) # construct the new model with init inputs model = ModelNew(*init_inputs) @@ -77,7 +77,6 @@ def get_torch_profiler_info(ref_arch_src: str, model = model.cuda(device=device) - with torch.no_grad(): profiling_scheduler = torch.profiler.schedule( skip_first=2, @@ -91,20 +90,26 @@ def get_torch_profiler_info(ref_arch_src: str, schedule=profiling_scheduler, ) as prof: for _ in range(num_trials): - + output = model(*inputs) prof.step() - profiler_output = prof.key_averages().table(sort_by='cuda_time_total', - row_limit=table_row_limit) - + profiler_output = prof.key_averages().table( + sort_by="cuda_time_total", row_limit=table_row_limit + ) + return profiler_output - + + def __main__(): # run_profile(dataset, problem_id, num_trials=10) - ref_arch_src_path = os.path.join(REPO_ROOT, "src/prompts/few_shot/model_ex_mnist2.py") - kernel_src_path = os.path.join(REPO_ROOT, "src/prompts/few_shot/model_new_ex_mnist2.py") + ref_arch_src_path = os.path.join( + REPO_ROOT, "src/prompts/few_shot/model_ex_mnist2.py" + ) + kernel_src_path = os.path.join( + REPO_ROOT, "src/prompts/few_shot/model_new_ex_mnist2.py" + ) ref_arch_src = read_file(ref_arch_src_path) kernel_src = read_file(kernel_src_path) @@ -116,11 +121,14 @@ def __main__(): device="cuda:0", num_trials=20, seed_num=42, - table_row_limit=10 + table_row_limit=10, ) - + print(profile_result) - print(f"Profiler result could be parsed as a string of length {len(profile_result)}") + print( + f"Profiler result could be parsed as a string of length {len(profile_result)}" + ) + if __name__ == "__main__": - __main__() \ No newline at end of file + __main__() diff --git a/scripts/inspect_triton.py b/scripts/inspect_triton.py index 4f13c8af..7d1baf93 100644 --- a/scripts/inspect_triton.py +++ b/scripts/inspect_triton.py @@ -1,10 +1,3 @@ -import torch -from torch.profiler import profile, record_function, ProfilerActivity -import logging -import os -import io - - """ [WIP] For debugging and analysis Inspect torch compile generated triton code @@ -12,22 +5,29 @@ using PyTorch Profiler """ -REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -device = "cuda:0" +import os +import io +import logging +import torch +from torch.profiler import profile, record_function, ProfilerActivity -from src.utils import read_file -from src.eval import ( - load_custom_model, +from kernelbench.utils import read_file +from kernelbench.eval import ( load_original_model_and_inputs, time_execution_with_cuda_event, get_timing_stats, set_seed, ) -def fetch_ref_arch_from_dataset(dataset: list[str], - problem_id: int) -> tuple[str, str, str]: +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +device = "cuda:0" + + +def fetch_ref_arch_from_dataset( + dataset: list[str], problem_id: int +) -> tuple[str, str, str]: """ Fetch the reference architecture from the problem directory problem_id should be logical index (1-indexed), matching the problem_id in the problem_name @@ -38,14 +38,14 @@ def fetch_ref_arch_from_dataset(dataset: list[str], ref_arch_src: str, the source code of the reference architecture """ ref_arch_path = None - + for file in dataset: if file.split("/")[-1].split("_")[0] == str(problem_id): ref_arch_path = file break if ref_arch_path is None: raise ValueError(f"No reference architecture found for problem_id {problem_id}") - + ref_arch_src = read_file(ref_arch_path) ref_arch_name = ref_arch_path.split("/")[-1] @@ -77,22 +77,21 @@ def run_profile_and_save_trace(dataset: list[str], problem_id: int, num_trials=1 set_seed(42) init_inputs = get_init_inputs() inputs = [ - x.cuda(device=device) if isinstance(x, torch.Tensor) else x - for x in inputs + x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in inputs ] init_inputs = [ x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in init_inputs ] - + # Create base model model = Model(*init_inputs) model = model.cuda(device=device) - + # Profile non-compiled model with profile( activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA], - schedule=profiling_scheduler + schedule=profiling_scheduler, ) as prof: with record_function("non_compiled_forward"): for _ in range(num_trials): @@ -100,7 +99,7 @@ def run_profile_and_save_trace(dataset: list[str], problem_id: int, num_trials=1 prof.step() print(f"\nProfiling results for non-compiled model:") print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10)) - + # Profile compiled model model_compiled = torch.compile(model) with profile( @@ -111,14 +110,16 @@ def run_profile_and_save_trace(dataset: list[str], problem_id: int, num_trials=1 model_compiled(*inputs) prof_compiled.step() print(f"\nProfiling results for compiled model:") - print(prof_compiled.key_averages().table(sort_by="cuda_time_total", row_limit=10)) - + print( + prof_compiled.key_averages().table(sort_by="cuda_time_total", row_limit=10) + ) prof.export_chrome_trace("trace_non_compiled.json") prof_compiled.export_chrome_trace("trace_compiled.json") # except Exception as e: - # print(f"[Eval] Error in Measuring Performance: {e}") + # print(f"[Eval] Error in Measuring Performance: {e}") + def get_torch_compile_triton(level_num, problem_id): """ @@ -150,21 +151,22 @@ def get_torch_compile_triton(level_num, problem_id): model = Model(*init_inputs) # output triton code - log_file = f"results/triton_code/level{level_num}_problem_{problem_id}_triton.log" + log_file = ( + f"results/triton_code/level{level_num}_problem_{problem_id}_triton.log" + ) os.makedirs(os.path.dirname(log_file), exist_ok=True) logging.basicConfig(filename=log_file, level=logging.DEBUG) - # TODO: Figure out a way to save to a file + # TODO: Figure out a way to save to a file torch._logging.set_logs(output_code=True) # Call torch compile - model =torch.compile(model, backend="inductor") + model = torch.compile(model, backend="inductor") - # reduce overhead -> + # reduce overhead -> # model = torch.compile(model, mode="") - + model = model.cuda(device=device) - torch.cuda.synchronize(device=device) elapsed_times = time_execution_with_cuda_event( @@ -173,6 +175,6 @@ def get_torch_compile_triton(level_num, problem_id): runtime_stats = get_timing_stats(elapsed_times, device=device) # json_results[f"level{level_num}"][ref_arch_name] = runtime_stats print(f"{ref_arch_name} {runtime_stats}") - return (ref_arch_name) + return ref_arch_name except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") diff --git a/scripts/llm_generation.py b/scripts/llm_generation.py new file mode 100644 index 00000000..5e592820 --- /dev/null +++ b/scripts/llm_generation.py @@ -0,0 +1,186 @@ +import asyncio +import weave +from weave.trace.context import call_context +import requests +from pathlib import Path +from dataclasses import dataclass +from rich.console import Console +import litellm +from litellm import acompletion +from pydantic import BaseModel, Field +import simple_parsing as sp +from kernelbench.utils import read_file, extract_python_code +from kernelbench.prompt_constructor import ( + prompt_generate_prompt_with_hardware_info_from_template, +) +from io import BytesIO + + +litellm.drop_params = True # drop params that are not used by the model + +BENCHMARK_SERVER_PARAMS: dict = { + "num_correct_trials": "5", + "num_perf_trials": "100", + "verbose": "true", +} + + +@dataclass +class ScriptArgs: + gpu_name: str = "H100" + # MODEL = "claude-3-5-sonnet-20240620" + model: str = "gpt-4o" + dataset_folder: str = "KernelBench/level1" + benchmark_server_url: str = ( + "https://tcapelle--kernel-benchmark-server-benchmarkservice-fastapi-app.modal.run/benchmark" + ) + debug: bool = False + N: int = 5 + + +args = sp.parse(ScriptArgs) + +console = Console() + +console.rule("Loading Dataset...") +ds = [ + {"ref_fname": str(sf), "ref_code": read_file(str(sf)), "gpu_name": args.gpu_name} + for sf in Path(args.dataset_folder).glob("*.py") +] + + +ds = ds[: args.N] + +console.print(f"Loaded {len(ds)} kernels") + + +class LLMResponse(BaseModel): + generated_code: str = Field(description="The optimized generated code") + + +class LLMCuda(weave.Model): + model: str = args.model + temperature: float = 0.5 + max_tokens: int = 2000 + + @weave.op + def prepare_prompt(self, filename: str, gpu_name: str): + ref_arch_src = read_file(filename) + prompt = prompt_generate_prompt_with_hardware_info_from_template( + ref_arch_src, gpu_name + ) + return prompt + + @weave.op + async def generate_with_llm(self, prompt) -> LLMResponse: + response = await acompletion( + model=self.model, + messages=[ + { + "role": "system", + "content": "You are a helpful assistant that generates optimized CUDA code for a given PyTorch function. You Reply in JSON format,", + }, + {"role": "user", "content": prompt}, + ], + max_tokens=self.max_tokens, + temperature=self.temperature, + response_format=LLMResponse, + ) + response = LLMResponse.model_validate_json(response.choices[0].message.content) + return response + + @weave.op + async def predict(self, ref_fname: str, gpu_name: str) -> LLMResponse: + prompt = self.prepare_prompt(ref_fname, gpu_name) + response = await self.generate_with_llm(prompt) + return response + + +weave.init("claude_cuda") + +claude = LLMCuda() + + +@weave.op +def call_benchmark_server( + ref_pytorch_code, + optimized_code, + benchmark_server_url=args.benchmark_server_url, + benchmark_server_params=BENCHMARK_SERVER_PARAMS, +): + # Create in-memory file objects + ref_file = BytesIO(ref_pytorch_code.encode("utf-8")) + kernel_file = BytesIO(optimized_code.encode("utf-8")) + + # Prepare the files for the request + files = { + "ref_file": ("ref_file.py", ref_file), + "kernel_file": ("kernel_file.py", kernel_file), + } + + # Make the request with both files and data + response = requests.post( + benchmark_server_url, files=files, data=benchmark_server_params + ) + + # Add debugging info + if args.debug: + console.print(f"Status code: {response.status_code}") + console.print( + f"Response content: {response.content[:500]}" + ) # Showing first 500 chars + + # Check for successful response before parsing JSON + if response.status_code != 200: + return { + "error": f"Server error: {response.status_code}", + "content": str(response.content), + } + + # Try to parse JSON with better error handling + try: + return response.json() + except requests.exceptions.JSONDecodeError: + return {"error": "Invalid JSON response", "content": str(response.content)} + + +@weave.op +def score_kernel(output: LLMResponse, ref_code: str) -> dict: + extracted_code = extract_python_code(output.generated_code) + benchmark_result = call_benchmark_server(ref_code, extracted_code) + error = benchmark_result.get("error", None) + if error is not None: + return { + "compiled": False, + "correctness": False, + "speedup_vs_compile": 0, + "speedup_vs_eager": 0, + "error": benchmark_result.get("content", str(error)), + } + + # Handle missing keys safely with .get() and provide defaults + kernel_result = benchmark_result.get("kernel_result", {}) + return { + "compiled": kernel_result.get("compiled", False), + "correctness": kernel_result.get("correctness", False), + "speedup_vs_compile": benchmark_result.get("speedup_vs_compile", 0), + "speedup_vs_eager": benchmark_result.get("speedup_vs_eager", 0), + "error": benchmark_result.get("error", None), + } + + +if args.debug: + console.rule("Running one sample...") + one_sample = ds[0] + console.print(f"One sample: {one_sample}") + response = asyncio.run( + claude.predict(one_sample["ref_fname"], one_sample["gpu_name"]) + ) + console.print(f"Response: {response}") + score = score_kernel(response, one_sample["ref_code"]) + console.print(f"Score: {score}") + +else: + console.rule("Running Evaluation...") + evaluation = weave.Evaluation(dataset=ds, scorers=[score_kernel]) + asyncio.run(evaluation.evaluate(claude)) diff --git a/scripts/run_and_check.py b/scripts/run_and_check.py index 79c00a7e..3ac35bb6 100644 --- a/scripts/run_and_check.py +++ b/scripts/run_and_check.py @@ -1,16 +1,17 @@ +import os import shutil +import importlib.util +import sys +import tempfile + import torch +import numpy as np import pydra from pydra import REQUIRED, Config -import os from datasets import load_dataset - -from src import eval as kernel_eval -from src import utils as kernel_utils -from scripts.generate_baseline_time import measure_program_time - -from src.utils import read_file +from kernelbench.eval import eval_kernel_against_ref, KernelExecResult +from kernelbench.utils import read_file, set_gpu_arch """ Run a pair of KernelBench format (problem, solution) to check if solution is correct and compute speedup @@ -37,12 +38,13 @@ torch.set_printoptions(precision=4, threshold=10) + class ScriptConfig(Config): def __init__(self): # Problem and Solution definition # Input src origin definition - self.ref_origin = REQUIRED # either local or kernelbench + self.ref_origin = REQUIRED # either local or kernelbench # ref_origin is local, specify local file path self.ref_arch_src_path = "" # ref_origin is kernelbench, specify level and problem id @@ -52,7 +54,6 @@ def __init__(self): # Solution src definition self.kernel_src_path = "" - # KernelBench Eval specific # number of trials to run for correctness self.num_correct_trials = 5 @@ -63,64 +64,194 @@ def __init__(self): # verbose logging self.verbose = False self.measure_performance = True - self.build_dir_prefix = "" # if you want to specify a custom build directory - self.clear_cache = False # TODO + self.build_dir_prefix = "" # if you want to specify a custom build directory + self.clear_cache = False # TODO # Replace with your NVIDIA GPU architecture, e.g. ["Hopper"] - self.gpu_arch = ["Ada"] + self.gpu_arch = ["Ada"] def __repr__(self): return f"ScriptConfig({self.to_dict()})" -def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> kernel_eval.KernelExecResult: + +def evaluate_single_sample_src( + ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device +) -> KernelExecResult: """ Evaluate a single sample source code against a reference source code """ kernel_hash = str(hash(kernel_src)) build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) - - if configs["clear_cache"]: # fresh kernel build + + if configs["clear_cache"]: # fresh kernel build print(f"[INFO] Clearing cache for build directory: {build_dir}") shutil.rmtree(build_dir, ignore_errors=True) - + num_correct_trials = configs["num_correct_trials"] - num_perf_trials = configs["num_perf_trials"] + num_perf_trials = configs["num_perf_trials"] verbose = configs["verbose"] measure_performance = configs["measure_performance"] try: - eval_result = kernel_eval.eval_kernel_against_ref( - original_model_src=ref_arch_src, + eval_result = eval_kernel_against_ref( + original_model_src=ref_arch_src, custom_model_src=kernel_src, measure_performance=measure_performance, verbose=verbose, num_correct_trials=num_correct_trials, num_perf_trials=num_perf_trials, build_dir=build_dir, - device=device + device=device, ) return eval_result except Exception as e: print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") - if "CUDA error" in str(e): + if "CUDA error" in str(e): # NOTE: count this as compilation failure as it is not runnable code - metadata = {"cuda_error": f"CUDA Error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } - eval_result = kernel_eval.KernelExecResult(compiled=False, correctness=False, - metadata=metadata) + metadata = { + "cuda_error": f"CUDA Error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) return eval_result else: - metadata = {"other_error": f"error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } - eval_result = kernel_eval.KernelExecResult(compiled=False, correctness=False, - metadata=metadata) + metadata = { + "other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) return eval_result +def measure_program_time( + ref_arch_name: str, # Added for consistency, although not used in this version + ref_arch_src: str, + num_trials: int, + device: torch.device, + use_torch_compile: bool = False, + torch_compile_backend: str | None = None, + torch_compile_options: str | None = None, +) -> dict: + """Measure the execution time of a reference program""" + + # Create temporary module + temp_dir = tempfile.mkdtemp() + ref_module_path = os.path.join(temp_dir, "ref_module.py") + + with open(ref_module_path, "w") as f: + f.write(ref_arch_src) + + # Load reference module + spec = importlib.util.spec_from_file_location("ref_module", ref_module_path) + ref_module = importlib.util.module_from_spec(spec) + sys.modules["ref_module"] = ref_module + spec.loader.exec_module(ref_module) + + # Create model instance + if hasattr(ref_module, "get_init_inputs"): + init_inputs = ref_module.get_init_inputs() + init_inputs = [ + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) + for x in init_inputs + ] + ref_model = ref_module.Model(*init_inputs).to(device) + else: + ref_model = ref_module.Model().to(device) + + # Apply torch.compile if needed + if use_torch_compile: + if torch_compile_backend is not None: + if torch_compile_options is not None and torch_compile_options != "default": + compile_options = ( + {"mode": torch_compile_options} + if torch_compile_options in ["max-autotune", "reduce-overhead"] + else {} + ) + ref_model = torch.compile( + ref_model, + backend=torch_compile_backend, + options=compile_options, + ) + else: + ref_model = torch.compile(ref_model, backend=torch_compile_backend) + else: + ref_model = torch.compile(ref_model) + + # Generate inputs + if hasattr(ref_module, "get_inputs"): + inputs = ref_module.get_inputs() + inputs = [ + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) + for x in inputs + ] + elif hasattr(ref_module, "INPUT_SHAPE"): + input_shape = ref_module.INPUT_SHAPE + if isinstance(input_shape, tuple): + inputs = (torch.randn(input_shape, device=device),) + elif isinstance(input_shape, list): + inputs = tuple(torch.randn(shape, device=device) for shape in input_shape) + else: + raise ValueError(f"Invalid INPUT_SHAPE: {input_shape}") + else: + # Infer inputs from model + if hasattr(ref_model, "forward"): + argcount = ref_model.forward.__code__.co_argcount + inputs = tuple( + torch.randn(1, 128, device=device) for _ in range(argcount - 1) + ) + else: + raise ValueError("Could not determine appropriate inputs for the model") + + # Warmup + for _ in range(10): + ref_model(*inputs) + + # Timing + torch.cuda.synchronize(device=device) + times = [] + for _ in range(num_trials): + start = torch.cuda.Event(enable_timing=True) + end = torch.cuda.Event(enable_timing=True) + + start.record() + ref_model(*inputs) + end.record() + + torch.cuda.synchronize(device=device) + times.append(start.elapsed_time(end)) + + # Clean up + try: + os.remove(ref_module_path) + os.rmdir(temp_dir) + except OSError: + shutil.rmtree(temp_dir, ignore_errors=True) + + # Calculate statistics + times = np.array(times) + return { + "mean": float(np.mean(times)), + "std": float(np.std(times)), + "min": float(np.min(times)), + "max": float(np.max(times)), + "median": float(np.median(times)), + } + + @pydra.main(base=ScriptConfig) def main(config: ScriptConfig): @@ -128,9 +259,11 @@ def main(config: ScriptConfig): # Fetch reference and kernel code - assert config.ref_origin == "local" or config.ref_origin == "kernelbench", "ref_origin must be either local or kernelbench" - assert config.kernel_src_path != "", "kernel_src_path is required" - + assert ( + config.ref_origin == "local" or config.ref_origin == "kernelbench" + ), "ref_origin must be either local or kernelbench" + assert config.kernel_src_path != "", "kernel_src_path is required" + if config.ref_origin == "local": assert config.ref_arch_src_path != "", "ref_arch_src_path is required" ref_arch_src = read_file(config.ref_arch_src_path) @@ -143,24 +276,29 @@ def main(config: ScriptConfig): dataset = load_dataset(config.dataset_name) curr_level_dataset = dataset[f"level_{config.level}"] - curr_problem_row = curr_level_dataset.filter(lambda x: x["problem_id"] == config.problem_id) + curr_problem_row = curr_level_dataset.filter( + lambda x: x["problem_id"] == config.problem_id + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] problem_number = int(problem_name.split("_")[0]) - assert problem_number == config.problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - - print(f"Fetched problem {config.problem_id} from KernelBench level {config.level}: {problem_name}") + assert ( + problem_number == config.problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" + print( + f"Fetched problem {config.problem_id} from KernelBench level {config.level}: {problem_name}" + ) else: raise ValueError("Invalid ref_origin") - + kernel_src = read_file(config.kernel_src_path) # Start Evaluation - device = torch.device("cuda:0") # default device - kernel_utils.set_gpu_arch(config.gpu_arch) + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + set_gpu_arch(config.gpu_arch) print("[INFO] Evaluating kernel against reference code") # Evaluate kernel against reference code @@ -168,46 +306,54 @@ def main(config: ScriptConfig): ref_arch_src=ref_arch_src, kernel_src=kernel_src, configs=config.to_dict(), - device=device + device=device, ) kernel_exec_time = kernel_eval_result.runtime # Measure baseline time print("[INFO] Measuring reference program time") # Default using PyTorch Eager here - ref_time_eager_result = measure_program_time(ref_arch_name="Reference Program", - ref_arch_src=ref_arch_src, - num_trials=config.num_perf_trials, - use_torch_compile=False, - device=device) + ref_time_eager_result = measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=config.num_perf_trials, + use_torch_compile=False, + device=device, + ) ref_exec_eager_time = ref_time_eager_result.get("mean", None) # Measure Torch Compile time - ref_time_compile_result = measure_program_time(ref_arch_name="Reference Program", - ref_arch_src=ref_arch_src, - num_trials=config.num_perf_trials, - use_torch_compile=True, - torch_compile_backend="inductor", - torch_compile_options="default", - device=device) + ref_time_compile_result = measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=config.num_perf_trials, + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + device=device, + ) ref_exec_compile_time = ref_time_compile_result.get("mean", None) - print("="*40) + print("=" * 40) print(f"[Eval] Kernel eval result: {kernel_eval_result}") - print("-"*40) + print("-" * 40) print(f"[Timing] PyTorch Reference Eager exec time: {ref_exec_eager_time} ms") print(f"[Timing] PyTorch Reference torch.compile time: {ref_exec_compile_time} ms") print(f"[Timing] Custom Kernel exec time: {kernel_exec_time} ms") - print("-"*40) - + print("-" * 40) + if kernel_eval_result.correctness: - print(f"[Speedup] Speedup over eager: {ref_exec_eager_time / kernel_exec_time:.2f}x") - print(f"[Speedup] Speedup over torch.compile: {ref_exec_compile_time / kernel_exec_time:.2f}x") + print( + f"[Speedup] Speedup over eager: {ref_exec_eager_time / kernel_exec_time:.2f}x" + ) + print( + f"[Speedup] Speedup over torch.compile: {ref_exec_compile_time / kernel_exec_time:.2f}x" + ) else: print("[Speedup] Speedup Not Available as Kernel did not pass correctness") - print("="*40) + print("=" * 40) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/run_and_check_modal.py b/scripts/run_and_check_modal.py new file mode 100644 index 00000000..58763c4d --- /dev/null +++ b/scripts/run_and_check_modal.py @@ -0,0 +1,306 @@ +import os +import shutil +import importlib.util +import sys +import os +import tempfile + +import modal +import pydra +import torch +import numpy as np + +from pydra import REQUIRED, Config + + +from kernelbench.eval import evaluate_single_sample_src +from kernelbench.utils import read_file, set_gpu_arch + + +""" +Run a pair of (reference, solution) to check if solution is correct and compute speedup using Modal + +Usage: +python3 scripts/run_and_check_modal.py ref_arch_src_path=src/prompts/model_ex_add.py kernel_src_path=src/prompts/model_new_ex_add.py +""" + +torch.set_printoptions(precision=4, threshold=10) +app = modal.App("run_and_check") +gpu_arch_mapping = { + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"], +} + + +class ScriptConfig(Config): + def __init__(self): + # Required file paths + self.ref_arch_src_path = REQUIRED # Reference implementation + self.kernel_src_path = REQUIRED # Custom kernel implementation + self.gpu = "L40S" # GPU type for modal + self.num_correct_trials = 5 # Number of trials for correctness + self.num_perf_trials = 100 # Number of trials for performance + self.timeout = 300 # Timeout for each trial + self.verbose = False # Verbose logging + self.measure_performance = True # Whether to measure performance + self.build_dir_prefix = "" # Custom build directory prefix + self.clear_cache = False # Whether to clear build cache + self.gpu_arch = ["Ada"] # Default GPU architecture + + def __repr__(self): + return f"ScriptConfig({self.to_dict()})" + + +# Configure Modal image +cuda_version = "12.8.0" +flavor = "devel" +operating_sys = "ubuntu22.04" +tag = f"{cuda_version}-{flavor}-{operating_sys}" + +image = ( + modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") + .apt_install("git", "gcc-10", "g++-10", "clang") + .pip_install_from_requirements("scripts/server_requirements.txt") + .add_local_python_source("kernelbench") + .add_local_dir("KernelBench", "/KernelBench") +) + + +@app.cls(image=image) +class EvalFunc: + @modal.method() + def evaluate_single_sample_src_modal( + self, ref_arch_src, kernel_src, configs, gpu_arch + ): + """Evaluate a single sample source code against a reference source code""" + + set_gpu_arch(gpu_arch) + device = torch.device("cuda:0") + + eval_result = evaluate_single_sample_src( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + configs=configs, + device=device, + ) + + return eval_result + + @modal.method() + def measure_program_time( + self, + ref_arch_src, + num_trials, + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + gpu_arch=None, + ): + """Measure the execution time of a reference program""" + + # Setup + if gpu_arch: + set_gpu_arch(gpu_arch) + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + + # Create temporary module + temp_dir = tempfile.mkdtemp() + ref_module_path = os.path.join(temp_dir, "ref_module.py") + + with open(ref_module_path, "w") as f: + f.write(ref_arch_src) + + # Load reference module + spec = importlib.util.spec_from_file_location("ref_module", ref_module_path) + ref_module = importlib.util.module_from_spec(spec) + sys.modules["ref_module"] = ref_module + spec.loader.exec_module(ref_module) + + # Create model instance + if hasattr(ref_module, "get_init_inputs"): + init_inputs = ref_module.get_init_inputs() + init_inputs = [ + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) + for x in init_inputs + ] + ref_model = ref_module.Model(*init_inputs).to(device) + else: + ref_model = ref_module.Model().to(device) + + # Apply torch.compile if needed + if use_torch_compile: + if torch_compile_backend is not None: + if ( + torch_compile_options is not None + and torch_compile_options != "default" + ): + compile_options = ( + {"mode": torch_compile_options} + if torch_compile_options in ["max-autotune", "reduce-overhead"] + else {} + ) + ref_model = torch.compile( + ref_model, + backend=torch_compile_backend, + options=compile_options, + ) + else: + ref_model = torch.compile(ref_model, backend=torch_compile_backend) + else: + ref_model = torch.compile(ref_model) + + # Generate inputs + if hasattr(ref_module, "get_inputs"): + inputs = ref_module.get_inputs() + inputs = [ + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) + for x in inputs + ] + elif hasattr(ref_module, "INPUT_SHAPE"): + input_shape = ref_module.INPUT_SHAPE + if isinstance(input_shape, tuple): + inputs = (torch.randn(input_shape, device=device),) + elif isinstance(input_shape, list): + inputs = tuple( + torch.randn(shape, device=device) for shape in input_shape + ) + else: + raise ValueError(f"Invalid INPUT_SHAPE: {input_shape}") + else: + # Infer inputs from model + if hasattr(ref_model, "forward"): + argcount = ref_model.forward.__code__.co_argcount + inputs = tuple( + torch.randn(1, 128, device=device) for _ in range(argcount - 1) + ) + else: + raise ValueError("Could not determine appropriate inputs for the model") + + # Warmup + for _ in range(10): + ref_model(*inputs) + + # Timing + torch.cuda.synchronize() + times = [] + for _ in range(num_trials): + start = torch.cuda.Event(enable_timing=True) + end = torch.cuda.Event(enable_timing=True) + + start.record() + ref_model(*inputs) + end.record() + + torch.cuda.synchronize() + times.append(start.elapsed_time(end)) + + # Clean up + try: + os.remove(ref_module_path) + os.rmdir(temp_dir) + except OSError: + shutil.rmtree(temp_dir, ignore_errors=True) + + # Calculate statistics + times = np.array(times) + return { + "mean": float(np.mean(times)), + "std": float(np.std(times)), + "min": float(np.min(times)), + "max": float(np.max(times)), + "median": float(np.median(times)), + } + + +@pydra.main(base=ScriptConfig) +def main(config: ScriptConfig): + print("Running with config", config) + + # Read source files + ref_arch_src = read_file(config.ref_arch_src_path) + kernel_src = read_file(config.kernel_src_path) + + # Prepare GPU architecture settings + gpu_arch = gpu_arch_mapping.get(config.gpu, config.gpu_arch) + print(f"[INFO] Using GPU architecture: {gpu_arch}") + + # Start Evaluation + with app.run(): + # Evaluate kernel against reference code + print("[INFO] Evaluating kernel against reference code") + kernel_eval_result = EvalFunc.with_options( + gpu=config.gpu + )().evaluate_single_sample_src_modal.remote( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + configs=config.to_dict(), + gpu_arch=gpu_arch, + ) + print(f"Raw result: {kernel_eval_result}, {type(kernel_eval_result)}") + + kernel_exec_time = kernel_eval_result.runtime + + # Measure baseline time for PyTorch Eager + print("[INFO] Measuring reference program time (eager mode)") + ref_time_eager_result = EvalFunc.with_options( + gpu=config.gpu + )().measure_program_time.remote( + ref_arch_src=ref_arch_src, + num_trials=config.num_perf_trials, + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + gpu_arch=gpu_arch, + ) + ref_exec_eager_time = ref_time_eager_result.get("mean", None) + + # Measure Torch Compile time + print("[INFO] Measuring reference program time (torch.compile)") + ref_time_compile_result = EvalFunc.with_options( + gpu=config.gpu + )().measure_program_time.remote( + ref_arch_src=ref_arch_src, + num_trials=config.num_perf_trials, + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + gpu_arch=gpu_arch, + ) + ref_exec_compile_time = ref_time_compile_result.get("mean", None) + + # Print results + print("=" * 40) + print(f"[Eval] Kernel eval result: {kernel_eval_result}") + print("-" * 40) + print(f"[Timing] PyTorch Reference Eager exec time: {ref_exec_eager_time} ms") + print(f"[Timing] PyTorch Reference torch.compile time: {ref_exec_compile_time} ms") + print(f"[Timing] Custom Kernel exec time: {kernel_exec_time} ms") + print("-" * 40) + + if kernel_eval_result.correctness: + print( + f"[Speedup] Speedup over eager: {ref_exec_eager_time / kernel_exec_time:.2f}x" + ) + print( + f"[Speedup] Speedup over torch.compile: {ref_exec_compile_time / kernel_exec_time:.2f}x" + ) + else: + print("[Speedup] Speedup Not Available as Kernel did not pass correctness") + + print("=" * 40) + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/scripts/server_requirements.txt similarity index 78% rename from requirements.txt rename to scripts/server_requirements.txt index 04381881..743cd38c 100644 --- a/requirements.txt +++ b/scripts/server_requirements.txt @@ -1,10 +1,8 @@ -anthropic modal numpy -openai -packaging pydra_config torch==2.5.0 +triton tqdm datasets transformers @@ -13,4 +11,4 @@ together pytest ninja archon-ai -einops \ No newline at end of file +einops diff --git a/scripts/server_run_and_check.py b/scripts/server_run_and_check.py new file mode 100644 index 00000000..eea7ce42 --- /dev/null +++ b/scripts/server_run_and_check.py @@ -0,0 +1,179 @@ +import os +import shutil +import tempfile +from typing import Optional, Dict, Any, List + +import fastapi +import uvicorn +import torch +from fastapi import UploadFile, File, HTTPException, status +from pydantic import BaseModel + +# Import the relevant modules directly +from scripts.run_and_check import evaluate_single_sample_src +from scripts.generate_baseline_time import measure_program_time +from kernelbench.utils import read_file, set_gpu_arch + + +# Define the response model +class BenchmarkResult(BaseModel): + compiled: bool + correctness: bool + ref_exec_eager_time_ms: Optional[float] = None + ref_exec_compile_time_ms: Optional[float] = None + kernel_exec_time_ms: Optional[float] = None + speedup_vs_eager: Optional[float] = None + speedup_vs_compile: Optional[float] = None + metadata: Dict[str, Any] + error: Optional[str] = None + + +app = fastapi.FastAPI() + + +@app.post("/benchmark", response_model=BenchmarkResult) +async def run_benchmark( + ref_file: UploadFile = File(...), + kernel_file: UploadFile = File(...), + gpu_arch: List[str] = ["Ada"], + num_correct_trials: int = 5, + num_perf_trials: int = 100, + verbose: bool = False, +): + # Create temporary files for the uploaded code + with ( + tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode="wb") as ref_tmp, + tempfile.NamedTemporaryFile( + delete=False, suffix=".py", mode="wb" + ) as kernel_tmp, + ): + try: + # Save uploaded file contents to temporary files + shutil.copyfileobj(ref_file.file, ref_tmp) + shutil.copyfileobj(kernel_file.file, kernel_tmp) + + # Ensure files are flushed and closed before script access + ref_path = ref_tmp.name + kernel_path = kernel_tmp.name + finally: + ref_file.file.close() + kernel_file.file.close() + + try: + # Read the contents of the files + ref_arch_src = read_file(ref_path) + kernel_src = read_file(kernel_path) + + # Set up GPU architecture + set_gpu_arch(gpu_arch) + + # Default device + device = torch.device("cuda:0") + + # Prepare configs + configs = { + "num_correct_trials": num_correct_trials, + "num_perf_trials": num_perf_trials, + "verbose": verbose, + "measure_performance": True, + "build_dir_prefix": "server_builds", + "clear_cache": False, + } + + # Evaluate kernel against reference + kernel_eval_result = evaluate_single_sample_src( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + configs=configs, + device=device, + ) + + # Measure reference times + ref_time_eager_result = measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=num_perf_trials, + use_torch_compile=False, + device=device, + ) + + ref_time_compile_result = measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=num_perf_trials, + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + device=device, + ) + + # Extract values + kernel_exec_time = kernel_eval_result.runtime + ref_exec_eager_time = ref_time_eager_result.get("mean", None) + ref_exec_compile_time = ref_time_compile_result.get("mean", None) + + # Calculate speedups + speedup_vs_eager = None + speedup_vs_compile = None + + if kernel_eval_result.correctness and kernel_exec_time and ref_exec_eager_time: + speedup_vs_eager = ref_exec_eager_time / kernel_exec_time + + if ( + kernel_eval_result.correctness + and kernel_exec_time + and ref_exec_compile_time + ): + speedup_vs_compile = ref_exec_compile_time / kernel_exec_time + + # Prepare output summary + raw_output = f""" +============================== +[Eval] Kernel eval result: {kernel_eval_result} +------------------------------ +[Timing] PyTorch Reference Eager exec time: {ref_exec_eager_time} ms +[Timing] PyTorch Reference torch.compile time: {ref_exec_compile_time} ms +[Timing] Custom Kernel exec time: {kernel_exec_time} ms +------------------------------ +""" + if kernel_eval_result.correctness: + raw_output += f""" +[Speedup] Speedup over eager: {speedup_vs_eager:.2f}x +[Speedup] Speedup over torch.compile: {speedup_vs_compile:.2f}x +""" + else: + raw_output += ( + "[Speedup] Speedup Not Available as Kernel did not pass correctness" + ) + + raw_output += "==============================" + + # Prepare the response + response = BenchmarkResult( + compiled=kernel_eval_result.compiled, + correctness=kernel_eval_result.correctness, + ref_exec_eager_time_ms=ref_exec_eager_time, + ref_exec_compile_time_ms=ref_exec_compile_time, + kernel_exec_time_ms=kernel_exec_time, + speedup_vs_eager=speedup_vs_eager, + speedup_vs_compile=speedup_vs_compile, + metadata=kernel_eval_result.metadata or {}, + ) + print(raw_output) + return response + + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"An error occurred during benchmarking: {str(e)}", + ) + finally: + # Clean up temporary files + if "ref_path" in locals() and os.path.exists(ref_path): + os.remove(ref_path) + if "kernel_path" in locals() and os.path.exists(kernel_path): + os.remove(kernel_path) + + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/scripts/server_run_and_check_modal.py b/scripts/server_run_and_check_modal.py new file mode 100644 index 00000000..2d0f32ca --- /dev/null +++ b/scripts/server_run_and_check_modal.py @@ -0,0 +1,543 @@ +import os +import shutil +import tempfile +from typing import Dict, Optional, Any +import sys +import traceback +import importlib.util +import time + +import torch +import modal +import numpy as np +from fastapi import FastAPI, File, Form, UploadFile, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel +from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse + + +from kernelbench.eval import evaluate_single_sample_src, KernelExecResult +from kernelbench.utils import set_gpu_arch + + +# GPU architecture mapping +gpu_arch_mapping = { + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"], +} + +GPU = "H100" +SCALEDOWN_WINDOW = 300 + +# Configure Modal image +cuda_version = "12.4.0" +flavor = "devel" +operating_sys = "ubuntu22.04" +tag = f"{cuda_version}-{flavor}-{operating_sys}" + +image = ( + modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") + .apt_install("git", "gcc-10", "g++-10", "clang") + .pip_install( + "fastapi", + "uvicorn", + "python-multipart", + "pydantic", + "aiofiles", # For serving static files + ) + .pip_install_from_requirements("scripts/server_requirements.txt") + .add_local_python_source("kernelbench") + .add_local_dir("KernelBench", "/KernelBench") + # .add_local_dir("static", "/root/static") +) + +# Create Modal app +app = modal.App("kernel-benchmark-server", image=image) + + +class BenchmarkResult(BaseModel): + kernel_result: KernelExecResult + ref_exec_eager_time_ms: Optional[float] = None + ref_exec_compile_time_ms: Optional[float] = None + kernel_exec_time_ms: Optional[float] = None + speedup_vs_eager: Optional[float] = None + speedup_vs_compile: Optional[float] = None + compile_time_ms: Optional[float] = None + total_benchmark_time_ms: Optional[float] = None + error: Optional[str] = None + + +@app.cls( + gpu=GPU, + scaledown_window=SCALEDOWN_WINDOW, + secrets=[modal.Secret.from_name("wandb-api-key")], +) +class BenchmarkService: + def measure_program_time( + self, + ref_arch_src, + num_trials, + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + gpu_arch=None, + ): + """Measure the execution time of a reference program""" + # Setup + if gpu_arch: + set_gpu_arch(gpu_arch) + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + + # Create temporary module + temp_dir = tempfile.mkdtemp() + ref_module_path = os.path.join(temp_dir, "ref_module.py") + + with open(ref_module_path, "w") as f: + f.write(ref_arch_src) + + # Load reference module + spec = importlib.util.spec_from_file_location("ref_module", ref_module_path) + ref_module = importlib.util.module_from_spec(spec) + sys.modules["ref_module"] = ref_module + spec.loader.exec_module(ref_module) + + # Create model instance + if hasattr(ref_module, "get_init_inputs"): + init_inputs = ref_module.get_init_inputs() + init_inputs = [ + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) + for x in init_inputs + ] + ref_model = ref_module.Model(*init_inputs).to(device) + else: + ref_model = ref_module.Model().to(device) + + # Apply torch.compile if needed + if use_torch_compile: + if torch_compile_backend is not None: + if ( + torch_compile_options is not None + and torch_compile_options != "default" + ): + compile_options = ( + {"mode": torch_compile_options} + if torch_compile_options in ["max-autotune", "reduce-overhead"] + else {} + ) + ref_model = torch.compile( + ref_model, + backend=torch_compile_backend, + options=compile_options, + ) + else: + ref_model = torch.compile(ref_model, backend=torch_compile_backend) + else: + ref_model = torch.compile(ref_model) + + # Generate inputs + if hasattr(ref_module, "get_inputs"): + inputs = ref_module.get_inputs() + inputs = [ + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) + for x in inputs + ] + elif hasattr(ref_module, "INPUT_SHAPE"): + input_shape = ref_module.INPUT_SHAPE + if isinstance(input_shape, tuple): + inputs = (torch.randn(input_shape, device=device),) + elif isinstance(input_shape, list): + inputs = tuple( + torch.randn(shape, device=device) for shape in input_shape + ) + else: + raise ValueError(f"Invalid INPUT_SHAPE: {input_shape}") + else: + # Infer inputs from model + if hasattr(ref_model, "forward"): + argcount = ref_model.forward.__code__.co_argcount + inputs = tuple( + torch.randn(1, 128, device=device) for _ in range(argcount - 1) + ) + else: + raise ValueError("Could not determine appropriate inputs for the model") + + # Warmup + for _ in range(10): + ref_model(*inputs) + + # Timing + torch.cuda.synchronize() + times = [] + for _ in range(num_trials): + start = torch.cuda.Event(enable_timing=True) + end = torch.cuda.Event(enable_timing=True) + + start.record() + ref_model(*inputs) + end.record() + + torch.cuda.synchronize() + times.append(start.elapsed_time(end)) + + # Clean up + try: + os.remove(ref_module_path) + os.rmdir(temp_dir) + except OSError: + shutil.rmtree(temp_dir, ignore_errors=True) + + # Calculate statistics + times = np.array(times) + return { + "mean": float(np.mean(times)), + "std": float(np.std(times)), + "min": float(np.min(times)), + "max": float(np.max(times)), + "median": float(np.median(times)), + } + + @modal.method() + def run_benchmark( + self, + ref_arch_src: str, + kernel_src: str, + num_correct_trials: int = 5, + num_perf_trials: int = 100, + verbose: bool = False, + ): + """Run a complete benchmark of kernel vs reference implementation""" + print(f"[DEBUG] Starting benchmark on GPU: {GPU}") + + start_time = time.time() + + try: + # Get GPU architecture + gpu_arch = gpu_arch_mapping.get(GPU, ["Ada"]) + print(f"[DEBUG] Using GPU architecture: {gpu_arch}") + + # Set GPU architecture + set_gpu_arch(gpu_arch) + + # Default device + device = torch.device("cuda:0") + print(f"[DEBUG] Using device: {device}") + + # Check CUDA availability + if torch.cuda.is_available(): + print( + f"[DEBUG] CUDA is available. Device count: {torch.cuda.device_count()}" + ) + print(f"[DEBUG] Current device: {torch.cuda.current_device()}") + print(f"[DEBUG] Device name: {torch.cuda.get_device_name(device)}") + else: + print(f"[WARNING] CUDA is not available. Using CPU.") + + # Config dictionary + configs = { + "num_correct_trials": num_correct_trials, + "num_perf_trials": num_perf_trials, + "verbose": verbose, + "measure_performance": True, + "build_dir_prefix": "api_builds", + "clear_cache": False, + } + print(f"[DEBUG] Using configs: {configs}") + + try: + # Time the compilation specifically + compile_start_time = time.time() + kernel_result = evaluate_single_sample_src( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + configs=configs, + device=device, + ) + compile_time = ( + time.time() - compile_start_time + ) * 1000 # Convert to ms + + # Evaluate kernel + print(f"[DEBUG] Evaluating kernel against reference...") + kernel_exec_time = kernel_result.runtime + print(f"[DEBUG] Kernel execution time: {kernel_exec_time} ms") + + # Measure baseline time for PyTorch Eager + print(f"[DEBUG] Measuring PyTorch Eager execution time...") + ref_time_eager_result = self.measure_program_time( + ref_arch_src=ref_arch_src, + num_trials=num_perf_trials, + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + gpu_arch=gpu_arch, + ) + ref_exec_eager_time = ref_time_eager_result.get("mean", None) + print(f"[DEBUG] PyTorch Eager execution time: {ref_exec_eager_time} ms") + + # Measure Torch Compile time + print(f"[DEBUG] Measuring PyTorch Compiled execution time...") + ref_time_compile_result = self.measure_program_time( + ref_arch_src=ref_arch_src, + num_trials=num_perf_trials, + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + gpu_arch=gpu_arch, + ) + ref_exec_compile_time = ref_time_compile_result.get("mean", None) + print( + f"[DEBUG] PyTorch Compiled execution time: {ref_exec_compile_time} ms" + ) + + # Calculate speedups + speedup_vs_eager = None + speedup_vs_compile = None + + if ( + kernel_result.correctness + and kernel_exec_time + and ref_exec_eager_time + ): + speedup_vs_eager = ref_exec_eager_time / kernel_exec_time + print(f"[DEBUG] Speedup vs Eager: {speedup_vs_eager}x") + + if ( + kernel_result.correctness + and kernel_exec_time + and ref_exec_compile_time + ): + speedup_vs_compile = ref_exec_compile_time / kernel_exec_time + print(f"[DEBUG] Speedup vs Compiled: {speedup_vs_compile}x") + + # Round all float values to 2 decimal places + if ref_exec_eager_time: + ref_exec_eager_time = round(ref_exec_eager_time, 2) + if ref_exec_compile_time: + ref_exec_compile_time = round(ref_exec_compile_time, 2) + if kernel_exec_time: + kernel_exec_time = round(kernel_exec_time, 2) + if speedup_vs_eager: + speedup_vs_eager = round(speedup_vs_eager, 2) + if speedup_vs_compile: + speedup_vs_compile = round(speedup_vs_compile, 2) + + # Calculate total benchmark time + total_time = round( + (time.time() - start_time) * 1000, 2 + ) # Convert to ms and round + compile_time = round(compile_time, 2) + + # Build response + print(f"[DEBUG] Building response...") + return BenchmarkResult( + kernel_result=kernel_result, + ref_exec_eager_time_ms=ref_exec_eager_time, + ref_exec_compile_time_ms=ref_exec_compile_time, + kernel_exec_time_ms=kernel_exec_time, + speedup_vs_eager=speedup_vs_eager, + speedup_vs_compile=speedup_vs_compile, + compile_time_ms=compile_time, + total_benchmark_time_ms=total_time, + ) + except Exception as e: + print(f"[ERROR] Error during benchmark execution: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + return BenchmarkResult( + kernel_result=KernelExecResult(compiled=False, correctness=False), + error=f"Benchmark execution error: {str(e)}", + ) + except Exception as e: + print(f"[ERROR] Fatal error in run_benchmark: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + return BenchmarkResult( + kernel_result=KernelExecResult(compiled=False, correctness=False), + error=str(e), + ) + + @modal.asgi_app() + def fastapi_app(self): + web_app = FastAPI(title="KernelBench Benchmarking API") + + # Add CORS middleware + web_app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + # Determine if we're running locally or in Modal + static_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static" + ) + modal_static_dir = "/root/static" + + # Check both possible locations for static files + if os.path.exists(static_dir): + # Mount static files directory (local development) + web_app.mount("/static", StaticFiles(directory=static_dir), name="static") + + @web_app.get("/") + async def root(): + return FileResponse(os.path.join(static_dir, "index.html")) + + elif os.path.exists(modal_static_dir): + # Mount static files directory (Modal environment) + web_app.mount( + "/static", StaticFiles(directory=modal_static_dir), name="static" + ) + + @web_app.get("/") + async def root(): + return FileResponse(os.path.join(modal_static_dir, "index.html")) + + else: + # Fallback for when static directory isn't available + @web_app.get("/") + async def root(): + return { + "name": "KernelBench Benchmarking API", + "version": "1.0.0", + "description": "API for benchmarking CUDA kernels against PyTorch reference implementations", + "endpoints": { + "/benchmark": "POST endpoint for benchmarking kernels", + "/status": "GET endpoint for checking server status", + }, + } + + @web_app.post("/benchmark", response_model=BenchmarkResult) + async def benchmark_endpoint( + ref_file: UploadFile = File(...), + kernel_file: UploadFile = File(...), + num_correct_trials: int = Form(5), + num_perf_trials: int = Form(100), + verbose: bool = Form(False), + ): + try: + print( + f"[DEBUG] Received benchmark request for GPU: {GPU}, trials: {num_correct_trials}/{num_perf_trials}" + ) + + # Read file contents + try: + ref_content = await ref_file.read() + print(f"[DEBUG] Read reference file: {len(ref_content)} bytes") + kernel_content = await kernel_file.read() + print(f"[DEBUG] Read kernel file: {len(kernel_content)} bytes") + + ref_arch_src = ref_content.decode("utf-8") + kernel_src = kernel_content.decode("utf-8") + except Exception as e: + print(f"[ERROR] Failed to read uploaded files: {str(e)}") + raise HTTPException( + status_code=400, + detail=f"Failed to read uploaded files: {str(e)}", + ) + + # Run the benchmark + try: + print(f"[DEBUG] Calling run_benchmark method") + result = self.run_benchmark.remote( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + num_correct_trials=num_correct_trials, + num_perf_trials=num_perf_trials, + verbose=verbose, + ) + print(f"[DEBUG] Benchmark completed successfully") + return result + except Exception as e: + print(f"[ERROR] Benchmark execution failed: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + raise HTTPException( + status_code=500, detail=f"Benchmark execution failed: {str(e)}" + ) + except Exception as e: + print(f"[ERROR] Unexpected error in benchmark endpoint: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + raise HTTPException( + status_code=500, detail=f"Benchmark failed: {str(e)}" + ) + + @web_app.get("/status") + async def status(): + return {"status": "online", "gpu_type": GPU} + + @web_app.get("/test_imports") + async def test_imports(): + """Test endpoint to check if we can import the necessary modules""" + result = { + "python_version": sys.version, + "sys_path": sys.path, + "env_vars": dict(os.environ), + "imports": {}, + } + + # Check modules that should have been imported at the top + try: + # Verify torch import + if "torch" in sys.modules: + result["imports"]["torch"] = { + "version": torch.__version__, + "cuda_available": torch.cuda.is_available(), + "cuda_version": ( + torch.version.cuda + if hasattr(torch.version, "cuda") + else None + ), + } + else: + result["imports"]["torch"] = {"error": "torch module not loaded"} + except Exception as e: + result["imports"]["torch"] = { + "error": f"Error checking torch: {str(e)}" + } + + result["imports"]["src.eval"] = {"success": True} + result["imports"]["src.utils"] = {"success": True} + + # Check for file existence + result["files"] = { + "static_local": os.path.exists( + os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "static", + ) + ), + "static_modal": os.path.exists("/root/static"), + "requirements_txt": os.path.exists("requirements.txt"), + } + + return result + + return web_app + + +def main(): + # For local development, you can use: + # modal serve scripts.server_run_and_check_modal + print("Starting KernelBench API server...") + print( + "Use 'modal serve scripts.server_run_and_check_modal' to start the development server" + ) + print( + "Use 'modal deploy scripts.server_run_and_check_modal' to deploy to production" + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/verify_bench.py b/scripts/verify_bench.py index 5fdc6862..2ab73629 100644 --- a/scripts/verify_bench.py +++ b/scripts/verify_bench.py @@ -1,22 +1,21 @@ """ -This script tests the correctness of models in KernelBench by generating random inputs +This script tests the correctness of models in KernelBench by generating random inputs and random initialization. It compares the output of the original model against itself. It ensures that the test is well-formed and there are no sources of non-determinism in the test. Usage: python test_bench.py """ +import os import importlib -import torch -import torch.nn as nn -import torch.nn.functional as F import random -import numpy as np -import os import importlib.util +import torch +import numpy as np + """ -Test all the reference architectures compiles +Test all the reference architectures compiles and reproduce the same results when run against itself """ diff --git a/scripts/verify_generation.py b/scripts/verify_generation.py index c284d3b5..f0dcc9af 100644 --- a/scripts/verify_generation.py +++ b/scripts/verify_generation.py @@ -1,14 +1,20 @@ import sys, os -import src.utils as utils import time -from src.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template + +import kernelbench.utils as utils +from kernelbench.prompt_constructor import ( + prompt_generate_custom_cuda_from_prompt_template, +) """ -For testing infernece and quickly iterate on prompts +For testing infernece and quickly iterate on prompts Uses functions in prompt_constructor """ -def inference_with_prompt(arch_path, inference_server: callable = None, log_to_local: bool = False) -> str: + +def inference_with_prompt( + arch_path, inference_server: callable = None, log_to_local: bool = False +) -> str: """ Returns the generated custom CUDA code (kernel to evaluate) @@ -27,7 +33,7 @@ def inference_with_prompt(arch_path, inference_server: callable = None, log_to_l custom_cuda_prompt = prompt_generate_custom_cuda_from_prompt_template(arch) - if log_to_local: + if log_to_local: with open(f"./scratch/prompt.py", "w") as f: f.write(custom_cuda_prompt) @@ -56,18 +62,19 @@ def sanity_check_inference(inference_server: callable): lm_response = inference_server("What does CUDA stand for?") end_time = time.time() print(f"[Timing] Inference took {end_time - start_time:.2f} seconds") - print(lm_response) + print(lm_response) return lm_response if __name__ == "__main__": - inference_server = utils.create_inference_server_from_presets(server_type="together", - model_name="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", - greedy_sample=True, - verbose=True, - time_generation=True) - + inference_server = utils.create_inference_server_from_presets( + server_type="together", + model_name="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", + greedy_sample=True, + verbose=True, + time_generation=True, + ) # sanity_check_inference(inference_server) @@ -78,6 +85,6 @@ def sanity_check_inference(inference_server: callable): # most basic problem arch_path = "./KernelBench/level1/1_Square_matrix_multiplication_.py" # representative of long problem, might require longer max tokens to not get cut of - # arch_path = "./KernelBench/level3/45_UNetSoftmax.py" - + # arch_path = "./KernelBench/level3/45_UNetSoftmax.py" + inference_with_prompt(arch_path, inference_server, log_to_local=True) diff --git a/setup.py b/setup.py deleted file mode 100644 index 220b79d1..00000000 --- a/setup.py +++ /dev/null @@ -1,8 +0,0 @@ -from setuptools import setup - -if __name__ == "__main__": - setup( - name="src", - version="0.0.1", - packages=["src"], - ) diff --git a/src/prompts/model_ex_0.py b/src/kernelbench/__init__.py similarity index 100% rename from src/prompts/model_ex_0.py rename to src/kernelbench/__init__.py diff --git a/src/analysis.py b/src/kernelbench/analysis.py similarity index 95% rename from src/analysis.py rename to src/kernelbench/analysis.py index 5c621c54..f6f1ec34 100644 --- a/src/analysis.py +++ b/src/kernelbench/analysis.py @@ -1,12 +1,9 @@ ################################################################################ # Helpers for Analysis ################################################################################ +import re import numpy as np - -from functools import cache from transformers import AutoTokenizer -import utils -import re def pass_at_k(n, c, k): @@ -23,14 +20,16 @@ def pass_at_k(n, c, k): def get_token_count(text: str, tokenizer: AutoTokenizer) -> int: - assert isinstance(text, str), "can only tokenize strings but got {}".format(type(text)) + assert isinstance(text, str), "can only tokenize strings but got {}".format( + type(text) + ) return len(tokenizer.encode(text)) def extract_all_cuda_sources(file_content: str) -> list[str]: """ Extract all CUDA sources wrapped in triple quotes. - + Returns: list[str]: List of all extracted CUDA source code blocks """ @@ -46,4 +45,3 @@ def get_cuda_tokens(kernel_src: str, tokenizer: AutoTokenizer) -> int: all_cuda_code = extract_all_cuda_sources(kernel_src) num_cuda_tokens = sum(get_token_count(code, tokenizer) for code in all_cuda_code) return num_cuda_tokens - diff --git a/src/compile.py b/src/kernelbench/compile.py similarity index 50% rename from src/compile.py rename to src/kernelbench/compile.py index 41fd8c3b..4290bb15 100644 --- a/src/compile.py +++ b/src/kernelbench/compile.py @@ -1,15 +1,14 @@ -from dataclasses import dataclass -import random +import multiprocessing as mp +import os +import shutil import time +from dataclasses import dataclass +import torch from tqdm import tqdm -import shutil -from src.eval import build_compile_cache -from src import utils as utils -import torch -import os -import multiprocessing as mp +from kernelbench.utils import set_gpu_arch +from kernelbench.eval import build_compile_cache """ Compile and Cache @@ -18,7 +17,8 @@ on CPU in parallel so you can speedup the evaluation process The cache build directory must match the ones you use during evaluation phase -""" +""" + @dataclass class WorkArgs: @@ -26,59 +26,83 @@ class WorkArgs: sample_id: int device: torch.device + def compile_single_sample(work_args: WorkArgs, config: dict) -> tuple[bool, str]: - problem_id = work_args.problem_id + problem_id = work_args.problem_id sample_id = work_args.sample_id verbose = config["verbose"] - - utils.set_gpu_arch(config["gpu_arch"]) - build_dir = os.path.join(config["kernel_eval_build_dir"], config["run_name"], str(problem_id), str(sample_id)) + set_gpu_arch(config["gpu_arch"]) + + build_dir = os.path.join( + config["kernel_eval_build_dir"], + config["run_name"], + str(problem_id), + str(sample_id), + ) run_dir = os.path.join(config["runs_dir"], config["run_name"]) - kernel_src_path = os.path.join(run_dir, f"level_{config['level']}_problem_{problem_id}_sample_{sample_id}_kernel.py") + kernel_src_path = os.path.join( + run_dir, + f"level_{config['level']}_problem_{problem_id}_sample_{sample_id}_kernel.py", + ) if not os.path.exists(kernel_src_path): - print(f"[ERROR] Kernel source file not found for Problem ID: {problem_id}, Sample ID: {sample_id}") + print( + f"[ERROR] Kernel source file not found for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) return False, "Kernel source file not found" with open(kernel_src_path, "r") as f: kernel_src = f.read() try: - compiled_and_cached, stdout_content, error_msg = build_compile_cache(custom_model_src=kernel_src, - verbose=verbose, - build_dir=build_dir) + compiled_and_cached, stdout_content, error_msg = build_compile_cache( + custom_model_src=kernel_src, verbose=verbose, build_dir=build_dir + ) return compiled_and_cached, stdout_content, error_msg except Exception as e: - print(f"[WARNING] Last level catch on {sample_id}: Some issue while compiling and attempting to cache for kernel: {e} ") + print( + f"[WARNING] Last level catch on {sample_id}: Some issue while compiling and attempting to cache for kernel: {e} " + ) return None, str(e), str(e) - + + def remove_cache_dir(config, problem_id, sample_id): """ Remove the cached folder for sample compilation so it can start a clean build next time useful for time out, failed build, etc. """ - cache_dir = os.path.join(config['kernel_eval_build_dir'], config["run_name"], f"{problem_id}", f"{sample_id}") + cache_dir = os.path.join( + config["kernel_eval_build_dir"], + config["run_name"], + f"{problem_id}", + f"{sample_id}", + ) print(f"cache_dir to remove: {cache_dir}") if os.path.exists(cache_dir): try: # Add error handling and retry with force shutil.rmtree(cache_dir, ignore_errors=True) - print(f"\n[INFO] Removed cached folder for Problem ID: {problem_id}, Sample ID: {sample_id}") + print( + f"\n[INFO] Removed cached folder for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) except Exception as e: print(f"\n[WARNING] Failed to remove cache directory {cache_dir}: {str(e)}") + def batch_compile(total_work: list[tuple[int, int]], config: dict): """ Batch compile cache across CPUs, assume config has num_cpu_workers """ if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') + mp.set_start_method("spawn") - assert "num_cpu_workers" in config, "num_cpu_workers must be specified in config for batch compile" + assert ( + "num_cpu_workers" in config + ), "num_cpu_workers must be specified in config for batch compile" try: with mp.Pool(config["num_cpu_workers"]) as pool: # Create work args for each task @@ -87,7 +111,6 @@ def batch_compile(total_work: list[tuple[int, int]], config: dict): for p_id, s_idx in total_work ] - # Launch all tasks in parallel and track start times async_results = [] start_times = {} @@ -96,68 +119,89 @@ def batch_compile(total_work: list[tuple[int, int]], config: dict): async_result = pool.apply_async(compile_single_sample, args=work_arg) async_results.append(async_result) start_times[id(async_result)] = time.time() - + results = [] pending_tasks = list(enumerate(async_results)) - + with tqdm(total=len(work_args), desc="Compile & Cache Progress") as pbar: while pending_tasks: remaining_tasks = [] for i, async_result in pending_tasks: try: - problem_id, sample_id = total_work[i] # curr code of interest + problem_id, sample_id = total_work[ + i + ] # curr code of interest if async_result.ready(): try: - compiled, stdout_content, error_msg = async_result.get(timeout=1) # Short timeout for completed tasks - - print(f"[Status] Compilation {compiled} for problem {problem_id} sample {sample_id}") + compiled, stdout_content, error_msg = ( + async_result.get(timeout=1) + ) # Short timeout for completed tasks + + print( + f"[Status] Compilation {compiled} for problem {problem_id} sample {sample_id}" + ) results.append((i, compiled)) if not compiled: # Remove the cached folder for this timed out sample so it can start a clean build next time problem_id, sample_id = total_work[i] remove_cache_dir(config, problem_id, sample_id) - + pbar.update(1) except Exception as e: problem_id, sample_id = total_work[i] with open("error_log.txt", "a") as f: - f.write(f"\n[ERROR] Task failed for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}") - print(f"\n[ERROR] Task failed for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}") + f.write( + f"\n[ERROR] Task failed for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}" + ) + print( + f"\n[ERROR] Task failed for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}" + ) remove_cache_dir(config, problem_id, sample_id) results.append((i, None)) pbar.update(1) else: # Check if the task has exceeded timeout - if time.time() - start_times[id(async_result)] > config["timeout"]: + if ( + time.time() - start_times[id(async_result)] + > config["timeout"] + ): problem_id, sample_id = total_work[i] - print(f"\n[TIME OUT] Task timed out for Problem ID: {problem_id}, Sample ID: {sample_id}") - + print( + f"\n[TIME OUT] Task timed out for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) + problem_id, sample_id = total_work[i] remove_cache_dir(config, problem_id, sample_id) # if we were to retry! # Start a new task for the same work - print(f"Retrying for Problem ID: {problem_id}, Sample ID: {sample_id}") - new_async_result = pool.apply_async(compile_single_sample, args=work_args[i]) + print( + f"Retrying for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) + new_async_result = pool.apply_async( + compile_single_sample, args=work_args[i] + ) start_times[id(new_async_result)] = time.time() remaining_tasks.append((i, new_async_result)) else: - # keep going + # keep going remaining_tasks.append((i, async_result)) except Exception as e: problem_id, sample_id = total_work[i] - print(f"\n[ERROR] Unexpected error for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}") - + print( + f"\n[ERROR] Unexpected error for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}" + ) + remove_cache_dir(config, problem_id, sample_id) - + results.append((i, None)) - + pbar.update(1) - + pending_tasks = remaining_tasks time.sleep(0.1) # Prevent busy waiting - + # Sort results back to original order sorted_results = [r for _, r in sorted(results, key=lambda x: x[0])] return sorted_results @@ -167,5 +211,80 @@ def batch_compile(total_work: list[tuple[int, int]], config: dict): pool.terminate() raise finally: - if 'pool' in locals(): + if "pool" in locals(): pool.close() + + +def compile_and_benchmark_kernel(kernel_code, **kwargs): + """ + Compile a CUDA kernel and benchmark its performance + + Args: + kernel_code (str): The CUDA kernel code to compile + **kwargs: Additional arguments for compilation and benchmarking + + Returns: + dict: Results containing compilation status, time, and benchmark metrics + """ + # This is a placeholder implementation + # In a real implementation, this would compile and run the kernel code + + try: + # Simulate compilation and benchmarking + compile_time = 0.2 # seconds + + benchmark_results = { + "mean": 1.0, # ms + "std": 0.1, + "min": 0.8, + "max": 1.2, + } + + return { + "status": "success", + "compile_time": compile_time, + "benchmark_results": benchmark_results, + } + except Exception as e: + return {"status": "error", "error": str(e)} + + +def compile_code(code, skip_on_error=False, **kwargs): + """ + Compile and benchmark a kernel code + + Args: + code (str): The CUDA kernel code to compile + skip_on_error (bool): If True, skip compilation on error + **kwargs: Additional arguments for compilation + + Returns: + dict: Compilation and benchmark results + """ + try: + # Attempt to compile and benchmark + return compile_and_benchmark_kernel(code, **kwargs) + except Exception as e: + if skip_on_error: + return {"status": "skipped", "error": str(e)} + raise + + +def get_data_type_mappings(): + """ + Get mappings between Python/NumPy data types and CUDA data types + + Returns: + dict: Mapping of data type names to their CUDA equivalents + """ + return { + "float32": "float", + "float64": "double", + "int32": "int", + "int64": "long long", + "uint32": "unsigned int", + "uint64": "unsigned long long", + "complex64": "cuFloatComplex", + "complex128": "cuDoubleComplex", + "bool": "bool", + } diff --git a/src/dataset.py b/src/kernelbench/dataset.py similarity index 67% rename from src/dataset.py rename to src/kernelbench/dataset.py index cb429dc1..2123410e 100644 --- a/src/dataset.py +++ b/src/kernelbench/dataset.py @@ -6,16 +6,82 @@ import random import re import hashlib +import requests +from kernelbench.utils import read_file + +# Replace hardcoded path with more robust resolution REPO_TOP_PATH = os.path.abspath( os.path.join( os.path.dirname(__file__), - "..", + "../..", ) ) +# Use pathlib for more robust path handling KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") +def fetch_kernel_from_database( + run_name: str, problem_id: int, sample_id: int, server_url: str +): + """ + Intenral to us with our django database + Return a dict with kernel hash, kernel code, problem_id + """ + response = requests.get( + f"{server_url}/get_kernel_by_run_problem_sample/{run_name}/{problem_id}/{sample_id}", + json={"run_name": run_name, "problem_id": problem_id, "sample_id": sample_id}, + ) + assert response.status_code == 200 + response_json = response.json() + assert str(response_json["problem_id"]) == str(problem_id) + return response_json + + +def fetch_ref_arch_from_problem_id(problem_id, problems, with_name=False) -> str: + """ + Fetches the reference architecture in string for a given problem_id + """ + if isinstance(problem_id, str): + problem_id = int(problem_id) + + problem_path = problems[problem_id] + + # problem_path = os.path.join(REPO_ROOT_PATH, problem) + if not os.path.exists(problem_path): + raise FileNotFoundError(f"Problem file at {problem_path} does not exist.") + + ref_arch = read_file(problem_path) + if not with_name: + return ref_arch + else: + return (problem_path, ref_arch) + + +def fetch_ref_arch_from_level_problem_id(level, problem_id, with_name=False): + PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) + dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) + return fetch_ref_arch_from_problem_id(problem_id, dataset, with_name) + + +# Alternative approach - make the path configurable +def get_kernel_bench_path(): + """Get the path to the KernelBench dataset directory + + Tries to use environment variable KERNEL_BENCH_PATH if set, + otherwise falls back to the default location relative to this file. + """ + env_path = os.environ.get("KERNEL_BENCH_PATH") + if env_path: + return env_path + + return os.path.join(REPO_TOP_PATH, "KernelBench") + + +# Update to use the function instead of the constant +KERNEL_BENCH_PATH = get_kernel_bench_path() + + def assign_problem_hash(problem_path: str) -> list[int]: """ Assign a unique hash to a problem in the dataset @@ -116,7 +182,26 @@ def get_kernelbench_subset( "87_conv_pointwise_2D.py", ] -level1_representative_subset_problem_ids = [1, 3, 6, 18, 23, 26, 33, 36, 40, 42, 48, 54, 57, 65, 77, 82, 86, 87] +level1_representative_subset_problem_ids = [ + 1, + 3, + 6, + 18, + 23, + 26, + 33, + 36, + 40, + 42, + 48, + 54, + 57, + 65, + 77, + 82, + 86, + 87, +] level2_representative_subset = [ "1_Conv2D_ReLU_BiasAdd.py", @@ -143,4 +228,4 @@ def get_kernelbench_subset( "43_MinGPTCausalAttention.py", ] -level3_representative_subset_problem_ids = [1, 5, 8, 11, 20, 33, 38, 43] \ No newline at end of file +level3_representative_subset_problem_ids = [1, 5, 8, 11, 20, 33, 38, 43] diff --git a/src/eval.py b/src/kernelbench/eval.py similarity index 89% rename from src/eval.py rename to src/kernelbench/eval.py index 4532154e..2018c712 100644 --- a/src/eval.py +++ b/src/kernelbench/eval.py @@ -2,70 +2,16 @@ Helpers for Evaluations """ -import requests +from contextlib import redirect_stdout, redirect_stderr +from io import StringIO +import json +import numpy as np +import os +import shutil +import subprocess import torch import torch.nn as nn -import os, subprocess from pydantic import BaseModel -import numpy as np -import random -import json -from contextlib import redirect_stdout, redirect_stderr -from io import StringIO -import sys - -from . import utils - -REPO_TOP_PATH = os.path.abspath( - os.path.join( - os.path.dirname(__file__), - "..", - ) -) -KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") - - -def fetch_kernel_from_database( - run_name: str, problem_id: int, sample_id: int, server_url: str -): - """ - Intenral to us with our django database - Return a dict with kernel hash, kernel code, problem_id - """ - response = requests.get( - f"{server_url}/get_kernel_by_run_problem_sample/{run_name}/{problem_id}/{sample_id}", - json={"run_name": run_name, "problem_id": problem_id, "sample_id": sample_id}, - ) - assert response.status_code == 200 - response_json = response.json() - assert str(response_json["problem_id"]) == str(problem_id) - return response_json - - -def fetch_ref_arch_from_problem_id(problem_id, problems, with_name=False) -> str: - """ - Fetches the reference architecture in string for a given problem_id - """ - if isinstance(problem_id, str): - problem_id = int(problem_id) - - problem_path = problems[problem_id] - - # problem_path = os.path.join(REPO_ROOT_PATH, problem) - if not os.path.exists(problem_path): - raise FileNotFoundError(f"Problem file at {problem_path} does not exist.") - - ref_arch = utils.read_file(problem_path) - if not with_name: - return ref_arch - else: - return (problem_path, ref_arch) - - -def fetch_ref_arch_from_level_problem_id(level, problem_id, with_name=False): - PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) - dataset = utils.construct_problem_dataset_from_problem_dir(PROBLEM_DIR) - return fetch_ref_arch_from_problem_id(problem_id, dataset, with_name) def set_seed(seed: int): @@ -169,6 +115,7 @@ def graceful_eval_cleanup(curr_context: dict, device: torch.device): # _cleanup_cuda_extensions() # SIMON NOTE: is this necessary? + def build_compile_cache_legacy( custom_model_src: str, verbose: bool = False, @@ -202,11 +149,12 @@ def build_compile_cache_legacy( if verbose: print(f"[Compilation] Compilation Successful, saved cache at: {build_dir}") except Exception as e: - print(f"[Compilation] Failed to compile custom CUDA kernel. Unable to cache, \nError: {e}") + print( + f"[Compilation] Failed to compile custom CUDA kernel. Unable to cache, \nError: {e}" + ) return False, stdout_buffer.getvalue(), str(e) - - return True, stdout_buffer.getvalue(), None + return True, stdout_buffer.getvalue(), None def build_compile_cache( @@ -242,16 +190,16 @@ def build_compile_cache( if verbose: print(f"[Compilation] Compilation Successful, saved cache at: {build_dir}") except Exception as e: - print(f"[Compilation] Failed to compile custom CUDA kernel. Unable to cache, \nError: {e}") + print( + f"[Compilation] Failed to compile custom CUDA kernel. Unable to cache, \nError: {e}" + ) return False, stdout_buffer.getvalue(), str(e) return True, stdout_buffer.getvalue(), None def build_compile_cache_with_capturing( - custom_model_src: str, - verbose: bool = False, - build_dir: os.PathLike = None + custom_model_src: str, verbose: bool = False, build_dir: os.PathLike = None ) -> tuple[int, str, str]: """ Write a temporary python file to compile the custom model on CPU @@ -273,22 +221,21 @@ def build_compile_cache_with_capturing( f.write(custom_model_src) # Execute the temporary Python file and capture output - process = subprocess.Popen(['python', tmp], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen( + ["python", tmp], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) stdout, stderr = process.communicate() returncode = process.returncode # Clean up temporary file os.remove(tmp) - if verbose: print("[CPU Precompile] return code: ", returncode) - print("[CPU Precompile] stdout: \n", stdout.decode('utf-8')) - print("[CPU Precompile] stderr: \n", stderr.decode('utf-8')) - - return returncode, stdout.decode('utf-8'), stderr.decode('utf-8') - + print("[CPU Precompile] stdout: \n", stdout.decode("utf-8")) + print("[CPU Precompile] stderr: \n", stderr.decode("utf-8")) + return returncode, stdout.decode("utf-8"), stderr.decode("utf-8") def eval_kernel_against_ref( @@ -300,7 +247,9 @@ def eval_kernel_against_ref( verbose: bool = False, measure_performance: bool = False, build_dir: os.PathLike = None, - device: torch.device = torch.cuda.current_device() if torch.cuda.is_available() else None, # have to run on GPU + device: torch.device = ( + torch.cuda.current_device() if torch.cuda.is_available() else None + ), # have to run on GPU ) -> KernelExecResult: """ Evaluate the custom kernel against the original model @@ -458,6 +407,61 @@ def eval_kernel_against_ref( return kernel_exec_result +def evaluate_single_sample_src( + ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device +) -> KernelExecResult: + """ + Evaluate a single sample source code against a reference source code + """ + + kernel_hash = str(hash(kernel_src)) + build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) + + if configs["clear_cache"]: # fresh kernel build + print(f"[INFO] Clearing cache for build directory: {build_dir}") + shutil.rmtree(build_dir, ignore_errors=True) + + num_correct_trials = configs["num_correct_trials"] + num_perf_trials = configs["num_perf_trials"] + verbose = configs["verbose"] + measure_performance = configs["measure_performance"] + try: + eval_result = eval_kernel_against_ref( + original_model_src=ref_arch_src, + custom_model_src=kernel_src, + measure_performance=measure_performance, + verbose=verbose, + num_correct_trials=num_correct_trials, + num_perf_trials=num_perf_trials, + build_dir=build_dir, + device=device, + ) + return eval_result + except Exception as e: + print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") + if "CUDA error" in str(e): + # NOTE: count this as compilation failure as it is not runnable code + metadata = { + "cuda_error": f"CUDA Error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) + return eval_result + else: + metadata = { + "other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) + return eval_result + + def register_and_format_exception( exception_type: str, exception_msg: Exception | str, @@ -678,11 +682,13 @@ def check_metadata_serializable(metadata: dict): return metadata + def check_metadata_serializable_all_types(metadata: dict): """ Ensure metadata is JSON serializable, if not, convert non-serializable values to strings recursively """ + def convert_to_serializable(obj): if isinstance(obj, dict): return {k: convert_to_serializable(v) for k, v in obj.items()} diff --git a/src/frameworks.py b/src/kernelbench/frameworks.py similarity index 78% rename from src/frameworks.py rename to src/kernelbench/frameworks.py index cb3795f9..944d1eea 100644 --- a/src/frameworks.py +++ b/src/kernelbench/frameworks.py @@ -5,37 +5,19 @@ # See how we added Archon support as an example ######################## -import multiprocessing -import subprocess -import re -import random -import tempfile -from pathlib import Path -import re -import math import os import json -from tqdm import tqdm +import time # API clients from archon.completions import Archon # python-dotenv reads key-value pairs from a .env file and sets them as environment variables from dotenv import load_dotenv + load_dotenv() # from datasets import load_dataset -import numpy as np -from contextlib import contextmanager -from collections import defaultdict -import time -import shutil -import concurrent -from functools import cache -from transformers import AutoTokenizer -import hashlib - -from concurrent.futures import ProcessPoolExecutor, as_completed # Define API key access TOGETHER_KEY = os.environ.get("TOGETHER_API_KEY") @@ -47,17 +29,17 @@ SAMBANOVA_API_KEY = os.environ.get("SAMBANOVA_API_KEY") - ######################################################## # Inference Time Frameworks ######################################################## + def query_framework_server( prompt: str | list[dict], # string if normal prompt, list of dicts if chat prompt, system_prompt: str = "You are a helpful assistant", # only used for chat prompts temperature: float = 0.0, - top_p: float = 1.0, # nucleus sampling - top_k: int = 50, + top_p: float = 1.0, # nucleus sampling + top_k: int = 50, max_tokens: int = 128, # max output tokens to generate num_completions: int = 1, server_port: int = 30000, # only for local server hosted on SGLang @@ -76,7 +58,9 @@ def query_framework_server( case "archon": archon_config_path = framework_config_path assert archon_config_path is not None, "Archon config path is required" - assert os.path.exists(archon_config_path), f"Archon config path {archon_config_path} does not exist" + assert os.path.exists( + archon_config_path + ), f"Archon config path {archon_config_path} does not exist" client = Archon(json.load(open(archon_config_path))) model = model_name print(f"Querying Archon model {model} with config {archon_config_path}") @@ -94,6 +78,7 @@ def query_framework_server( case _: raise NotImplementedError + # a list of presets for API server configs SERVER_PRESETS = { "archon": { @@ -102,15 +87,18 @@ def query_framework_server( }, } -def create_inference_framework_server_from_presets(framework_type: str = None, - greedy_sample: bool = False, - verbose: bool = False, - time_generation: bool = False, - **kwargs, - ) -> callable: + +def create_inference_framework_server_from_presets( + framework_type: str = None, + greedy_sample: bool = False, + verbose: bool = False, + time_generation: bool = False, + **kwargs, +) -> callable: """ Return a callable function that queries LLM with given settings """ + def _query_llm(prompt: str | list[dict]): server_args = SERVER_PRESETS[framework_type].copy() @@ -122,7 +110,7 @@ def _query_llm(prompt: str | list[dict]): server_args["top_k"] = 1 if verbose: print(f"Querying server {framework_type} with args: {server_args}") - + if time_generation: start_time = time.time() response = query_framework_server( @@ -135,5 +123,5 @@ def _query_llm(prompt: str | list[dict]): return query_framework_server( prompt, framework_type=framework_type, **server_args ) - - return _query_llm \ No newline at end of file + + return _query_llm diff --git a/src/utils.py b/src/kernelbench/llm_utils.py similarity index 56% rename from src/utils.py rename to src/kernelbench/llm_utils.py index bc7366fb..3b1b147c 100644 --- a/src/utils.py +++ b/src/kernelbench/llm_utils.py @@ -1,18 +1,8 @@ ######################## -# Utils Functions +# API LLM Utils Functions ######################## -import multiprocessing -import subprocess -import re -import random -import tempfile -from pathlib import Path -import re -import math import os -import json -from tqdm import tqdm # API clients from together import Together @@ -21,17 +11,10 @@ import anthropic # from datasets import load_dataset -import numpy as np -from contextlib import contextmanager -from collections import defaultdict import time -import shutil -import concurrent from functools import cache from transformers import AutoTokenizer -import hashlib -from concurrent.futures import ProcessPoolExecutor, as_completed # Define API key access TOGETHER_KEY = os.environ.get("TOGETHER_API_KEY") @@ -48,11 +31,15 @@ # Inference Helpers ######################################################## + @cache def load_deepseek_tokenizer(): # TODO: Should we update this for new deepseek? Same tokenizer? # return AutoTokenizer.from_pretrained("deepseek-ai/DeepSeek-Coder-V2-Instruct-0724") - return AutoTokenizer.from_pretrained("deepseek-ai/DeepSeek-V2", trust_remote_code=True) + return AutoTokenizer.from_pretrained( + "deepseek-ai/DeepSeek-V2", trust_remote_code=True + ) + # Buffer because deepseek totally blocks us if we send stuff that's too long :( TOO_LONG_FOR_DEEPSEEK = 115_000 @@ -62,42 +49,31 @@ def is_safe_to_send_to_deepseek(prompt): tokenizer = load_deepseek_tokenizer() # print(f"Prompt: {len(prompt)}") # print(f"Prompt length: {len(tokenizer(prompt, verbose=False)['input_ids'])}") - - if type(prompt) == str: + + if isinstance(prompt, str): return ( len(tokenizer(prompt, verbose=False)["input_ids"]) < TOO_LONG_FOR_DEEPSEEK ) else: return len(tokenizer.apply_chat_template(prompt)) < TOO_LONG_FOR_DEEPSEEK -def set_gpu_arch(arch_list: list[str]): - """ - Set env variable for torch cuda arch list to build kernels for specified architectures - """ - valid_archs = ["Maxwell", "Pascal", "Volta", "Turing", "Ampere", "Hopper", "Ada"] - for arch in arch_list: - if arch not in valid_archs: - raise ValueError(f"Invalid architecture: {arch}. Must be one of {valid_archs}") - - os.environ["TORCH_CUDA_ARCH_LIST"] = ";".join(arch_list) def query_server( prompt: str | list[dict], # string if normal prompt, list of dicts if chat prompt, system_prompt: str = "You are a helpful assistant", # only used for chat prompts temperature: float = 0.0, - top_p: float = 1.0, # nucleus sampling - top_k: int = 50, + top_p: float = 1.0, # nucleus sampling + top_k: int = 50, max_tokens: int = 128, # max output tokens to generate num_completions: int = 1, server_port: int = 30000, # only for local server hosted on SGLang server_address: str = "localhost", server_type: str = "sglang", model_name: str = "default", # specify model type - # for reasoning models - is_reasoning_model: bool = False, # indiactor of using reasoning models - budget_tokens: int = 0, # for claude thinking - reasoning_effort: str = None, # only for o1 and o3 / more reasoning models in the future + is_reasoning_model: bool = False, # indiactor of using reasoning models + budget_tokens: int = 0, # for claude thinking + reasoning_effort: str = None, # only for o1 and o3 / more reasoning models in the future ): """ Query various sort of LLM inference API providers @@ -127,7 +103,11 @@ def query_server( max_retries=3, ) model = model_name - assert model in ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"], "Only support deepseek-chat or deepseek-coder for now" + assert model in [ + "deepseek-chat", + "deepseek-coder", + "deepseek-reasoner", + ], "Only support deepseek-chat or deepseek-coder for now" if not is_safe_to_send_to_deepseek(prompt): raise RuntimeError("Prompt is too long for DeepSeek") case "fireworks": @@ -151,9 +131,11 @@ def query_server( client = Together(api_key=TOGETHER_KEY) model = model_name case "sambanova": - client = OpenAI(api_key=SAMBANOVA_API_KEY, base_url="https://api.sambanova.ai/v1") + client = OpenAI( + api_key=SAMBANOVA_API_KEY, base_url="https://api.sambanova.ai/v1" + ) model = model_name - + case "openai": client = OpenAI(api_key=OPENAI_KEY) model = model_name @@ -168,7 +150,9 @@ def query_server( ) # Logic to query the LLM if server_type == "anthropic": - assert type(prompt) == str + assert isinstance( + prompt, str + ), f"The prompt must be a string for Anthropic, but it was a {type(prompt)}" if is_reasoning_model: # Use beta endpoint with thinking enabled for reasoning models @@ -196,7 +180,11 @@ def query_server( top_k=top_k, max_tokens=max_tokens, ) - outputs = [choice.text for choice in response.content if not hasattr(choice, 'thinking') or not choice.thinking] + outputs = [ + choice.text + for choice in response.content + if not hasattr(choice, "thinking") or not choice.thinking + ] elif server_type == "google": # assert model_name == "gemini-1.5-flash-002", "Only test this for now" @@ -220,12 +208,12 @@ def query_server( return response.text elif server_type == "deepseek": - + if model in ["deepseek-chat", "deepseek-coder"]: - # regular deepseek model + # regular deepseek model response = client.chat.completions.create( - model=model, - messages=[ + model=model, + messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ], @@ -236,12 +224,14 @@ def query_server( top_p=top_p, ) - else: # deepseek reasoner + else: # deepseek reasoner assert is_reasoning_model, "Only support deepseek-reasoner for now" - assert model == "deepseek-reasoner", "Only support deepseek-reasoner for now" + assert ( + model == "deepseek-reasoner" + ), "Only support deepseek-reasoner for now" response = client.chat.completions.create( - model=model, - messages=[ + model=model, + messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ], @@ -254,8 +244,12 @@ def query_server( elif server_type == "openai": if is_reasoning_model: assert "o1" in model or "o3" in model, "Only support o1 and o3 for now" - print(f"Using OpenAI reasoning model: {model} with reasoning effort {reasoning_effort}") - print(f"Using OpenAI reasoning model: {model} with reasoning effort {reasoning_effort}") + print( + f"Using OpenAI reasoning model: {model} with reasoning effort {reasoning_effort}" + ) + print( + f"Using OpenAI reasoning model: {model} with reasoning effort {reasoning_effort}" + ) response = client.chat.completions.create( model=model, messages=[ @@ -325,7 +319,7 @@ def query_server( outputs = [choice.message.content for choice in response.choices] # for all other kinds of servers, use standard API else: - if type(prompt) == str: + if isinstance(prompt, str): response = client.completions.create( model=model, prompt=prompt, @@ -355,24 +349,20 @@ def query_server( # a list of presets for API server configs SERVER_PRESETS = { - "deepseek": { - "temperature": 1.6, - "model_name": "deepseek", - "max_tokens": 4096 - }, + "deepseek": {"temperature": 1.6, "model_name": "deepseek", "max_tokens": 4096}, "google": { "model_name": "gemini-1.5-flash-002", - "temperature": 0.7, # need to experiment with temperature + "temperature": 0.7, # need to experiment with temperature "max_tokens": 8192, }, - "together": { # mostly for Llama 3.1 + "together": { # mostly for Llama 3.1 "model_name": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", # "model_name": "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", "temperature": 0.7, "max_tokens": 4096, }, "sglang": { # this is for running locally, mostly for Llama - "temperature": 0.8, # human eval pass@N temperature + "temperature": 0.8, # human eval pass@N temperature "server_port": 10210, "server_address": "matx2.stanford.edu", "max_tokens": 8192, @@ -396,15 +386,17 @@ def query_server( } -def create_inference_server_from_presets(server_type: str = None, - greedy_sample: bool = False, - verbose: bool = False, - time_generation: bool = False, - **kwargs, - ) -> callable: +def create_inference_server_from_presets( + server_type: str = None, + greedy_sample: bool = False, + verbose: bool = False, + time_generation: bool = False, + **kwargs, +) -> callable: """ Return a callable function that queries LLM with given settings """ + def _query_llm(prompt: str | list[dict]): server_args = SERVER_PRESETS[server_type].copy() @@ -416,206 +408,14 @@ def _query_llm(prompt: str | list[dict]): server_args["top_k"] = 1 if verbose: print(f"Querying server {server_type} with args: {server_args}") - + if time_generation: start_time = time.time() - response = query_server( - prompt, server_type=server_type, **server_args - ) + response = query_server(prompt, server_type=server_type, **server_args) end_time = time.time() print(f"[Timing] Inference took {end_time - start_time:.2f} seconds") return response else: - return query_server( - prompt, server_type=server_type, **server_args - ) - - return _query_llm - -""" -Model output processing -# TODO: add unit tests -""" - - -def read_file(file_path) -> str: - if not os.path.exists(file_path): - print(f"File {file_path} does not exist") - return "" - - try: - with open(file_path, "r") as file: - return file.read() - except Exception as e: - print(f"Error reading file {file_path}: {e}") - return "" - - -def print_messages(messages): - for message in messages: - print(message["role"]) - print(message["content"]) - print("-" * 50) - print("\n\n") - + return query_server(prompt, server_type=server_type, **server_args) -def extract_python_code(text): - """ - Extract python code from model output - """ - pattern = r"```python\n(.*?)```" - matches = re.findall(pattern, text, re.DOTALL) - return "\n".join(matches) if matches else "" - - -def remove_code_block_header(code, code_language_type): - """Assume input is code but just with like python, cpp, etc. at the top""" - if code.startswith(code_language_type): - code = code[len(code_language_type) :].strip() - return code - - -def extract_first_code(output_string: str, code_language_types: list[str]) -> str: - """ - Extract first code block from model output, specified by code_language_type - """ - trimmed = output_string.strip() - - # Extracting the first occurrence of content between backticks - code_match = re.search(r"```(.*?)```", trimmed, re.DOTALL) - - if code_match: - # Strip leading and trailing whitespace from the extracted code - code = code_match.group(1).strip() - - # depends on code_language_type: cpp, python, etc. - # sometimes the block of code is ```cpp ... ``` instead of ``` ... ``` - # in this case strip the cpp out - for code_type in code_language_types: - if code.startswith(code_type): - code = code[len(code_type) :].strip() - - return code - - return None - - -def extract_last_code(output_string: str, code_language_types: list[str]) -> str | None: - """ - Extract last code block from model output, specified by code_language_type - """ - trimmed = output_string.strip() - - # Find all matches of code blocks - code_matches = re.finditer(r"```(.*?)```", trimmed, re.DOTALL) - - # Get the last match by converting to list and taking the last element - matches_list = list(code_matches) - if matches_list: - last_match = matches_list[-1] - code = last_match.group(1).strip() - - # Remove language type headers - for code_type in code_language_types: - if code.startswith(code_type): - code = code[len(code_type):].strip() - - return code - - return None - -def extract_code_blocks(text, code_language_types: list[str]) -> str: - ''' - Extract all code blocks from text, combine them to return as a single string - ''' - pattern = r'```.*?\n(.*?)```' - matches = re.findall(pattern, text, re.DOTALL) - - # Combine all code blocks and remove language type headers - combined_code = [] - for match in matches: - code = match.strip() - # Remove any language type headers - for lang_type in code_language_types: - if code.startswith(lang_type): - code = code[len(lang_type):].strip() - combined_code.append(code) - - return " \n ".join(combined_code) if combined_code else "" - -################################################################################ -# Scale up experiments in parallel -################################################################################ - -def maybe_multithread(func, instances, num_workers, time_interval=0.0, *shared_args, **shared_kwargs): - """ - Multithreaded execution of func, with optional time interval between queries - Ideal for querying LLM APIs, does not provide process isolation - """ - output_data = [] - if num_workers not in [1, None]: - with tqdm(total=len(instances), smoothing=0) as pbar: - with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor: - - # Submit tasks one at a time with delay between them - futures = [] - for instance in instances: - futures.append( - executor.submit( - func, - instance, - *shared_args, - **shared_kwargs - ) - ) - time.sleep(time_interval) # sleep between submitting each task - - - - # Wait for each future to complete - for future in concurrent.futures.as_completed(futures): - pbar.update(1) - try: - result = future.result() - if result is not None: - output_data.append(result) - except Exception as e: - print("Got an error!", e) - continue - else: - for instance in tqdm(instances): - output = func(instance, *shared_args, **shared_kwargs) - if output is not None: output_data.append(output) - - return output_data - - -def maybe_multiprocess_cuda( - func, instances, num_workers, *shared_args, **shared_kwargs -): - """ - From monkeys, but modified to work with CUDA - """ - output_data = [] - multiprocessing.set_start_method( - "spawn", force=True - ) # this is necessary for CUDA to work - - with tqdm(total=len(instances), smoothing=0) as pbar: - with ProcessPoolExecutor(max_workers=num_workers) as executor: - # Create a future for running each instance - futures = { - executor.submit(func, instance, *shared_args, **shared_kwargs): None - for instance in instances - } - # Wait for each future to complete - for future in as_completed(futures): - pbar.update(1) - try: - result = future.result() - if result is not None: - output_data.append(result) - except Exception as e: - print("Got an error!", e) - continue - return output_data + return _query_llm diff --git a/src/make_hf_dataset.py b/src/kernelbench/make_hf_dataset.py similarity index 89% rename from src/make_hf_dataset.py rename to src/kernelbench/make_hf_dataset.py index 3874b846..cb0e0420 100644 --- a/src/make_hf_dataset.py +++ b/src/kernelbench/make_hf_dataset.py @@ -13,7 +13,7 @@ # \"\"\" # def __init__(self): # super(Model, self).__init__() - + # def forward(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: # \"\"\" # Performs the matrix multiplication. @@ -53,7 +53,7 @@ # def __init__(self, in_channels, out_channels, kernel_size, bias_shape): # super(Model, self).__init__() # self.conv = nn.Conv2d(in_channels, out_channels, kernel_size) -# self.bias = nn.Parameter(torch.randn(bias_shape)) +# self.bias = nn.Parameter(torch.randn(bias_shape)) # def forward(self, x): # x = self.conv(x) @@ -92,19 +92,19 @@ # :param output_size: The number of output features # \"\"\" # super(Model, self).__init__() - + # layers = [] # current_input_size = input_size - + # for layer_size in layer_sizes: # layers.append(nn.Linear(current_input_size, layer_size)) # layers.append(nn.ReLU()) # current_input_size = layer_size - + # layers.append(nn.Linear(current_input_size, output_size)) - + # self.network = nn.Sequential(*layers) - + # def forward(self, x): # \"\"\" # :param x: The input tensor, shape (batch_size, input_size) @@ -132,14 +132,17 @@ # dataset_example_1, # dataset_example_2, # dataset_example_3 -#] +# ] dataset_list = [] + def make_dataset_examples(dir_path, level): global dataset_list # list all files in the directory - file_list = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))] + file_list = [ + f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f)) + ] file_list = sorted(file_list) # count = 0 for f in file_list: @@ -150,12 +153,7 @@ def make_dataset_examples(dir_path, level): code = open(file_path, "r").read() name = f.split(".")[0] problem_id = int(name.split("_")[0]) - json_object = { - "code": "", - "level": 0, - "name": "", - "problem_id": 0 - } + json_object = {"code": "", "level": 0, "name": "", "problem_id": 0} json_object["code"] = code json_object["level"] = level json_object["name"] = name @@ -163,6 +161,7 @@ def make_dataset_examples(dir_path, level): dataset_list.append(json_object) # count += 1 + make_dataset_examples("../KernelBench/level1", 1) make_dataset_examples("../KernelBench/level2", 2) make_dataset_examples("../KernelBench/level3", 3) @@ -179,11 +178,13 @@ def make_dataset_examples(dir_path, level): hf_level_3 = Dataset.from_list(level_3) hf_level_4 = Dataset.from_list(level_4) -dataset_dict = DatasetDict({ - "level_1": hf_level_1, - "level_2": hf_level_2, - "level_3": hf_level_3, - "level_4": hf_level_4 -}) +dataset_dict = DatasetDict( + { + "level_1": hf_level_1, + "level_2": hf_level_2, + "level_3": hf_level_3, + "level_4": hf_level_4, + } +) -dataset_dict.push_to_hub("ScalingIntelligence/KernelBench") \ No newline at end of file +dataset_dict.push_to_hub("ScalingIntelligence/KernelBench") diff --git a/src/prompt_constructor.py b/src/kernelbench/prompt_constructor.py similarity index 64% rename from src/prompt_constructor.py rename to src/kernelbench/prompt_constructor.py index 36cde19f..26afa905 100644 --- a/src/prompt_constructor.py +++ b/src/kernelbench/prompt_constructor.py @@ -1,19 +1,20 @@ import os -from .utils import read_file +import weave +from kernelbench.utils import read_file """ Construct Prompt -Design principles: +Design principles: - To evaluate base model performance on KernelBench, we use the simplest prompt possible to guide model output to generated desired output format. -- However, we do not do extensive prompt engineering or few-shot example in the LLM to steer behaviour. +- However, we do not do extensive prompt engineering or few-shot example in the LLM to steer behaviour. """ REPO_TOP_PATH = os.path.abspath( os.path.join( os.path.dirname(__file__), - "..", + "../..", ) ) KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") @@ -43,6 +44,7 @@ def get_arch_definition(arch_src): """ +@weave.op def prompt_generate_custom_cuda( arc_src: str, example_arch_src: str, example_new_arch_src: str ) -> str: @@ -54,7 +56,7 @@ def prompt_generate_custom_cuda( ``` \n {example_arch_src} ``` \n - The example new arch with custom CUDA kernels looks like this: + The example new arch with custom CUDA kernels looks like this: ``` {example_new_arch_src} ``` \n @@ -76,12 +78,16 @@ def prompt_generate_custom_cuda( Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Just output the new model code, no other text, and NO testing code! \n """ -def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: list) -> str: + +@weave.op +def prompt_generate_custom_cuda_fewshot_and_template( + ref_arch_src: str, shots: list +) -> str: """ - Generate a prompt with specified few-shot examples following a template + Generate a prompt with specified few-shot examples following a template shots: list of few-shot examples to include in the prompt - Avaliable few shot options to start with: + Avaliable few shot options to start with: - ex_add: pointwise addition - ex_fuse_gelu: fused gelu - ex_mnist2: fused convolutions and relus (DEPRECATED) @@ -92,65 +98,103 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l # k = 1 example_add = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_add.py") + os.path.join(REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_add.py") ) example_add_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_add.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_add.py" + ) ) example_add_desc = "This given architecture is for a pointwise addition: " # k = 2 example_fuse_gelu = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py" + ) ) example_fuse_gelu_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py" + ) ) example_fuse_gelu_desc = "This given architecture is for a fused gelu: " # k = 3 (DEPRECATED) example_mnist2 = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_mnist2.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_mnist2.py" + ) ) example_mnist2_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_mnist2.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py" + ) + ) + exmaple_mnist2_desc = ( + "This given architecture is for a model with fused convolutions and relus: " ) - exmaple_mnist2_desc = "This given architecture is for a model with fused convolutions and relus: " # k = 4 example_tiled_matmul = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py" + ) ) example_tiled_matmul_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, + "src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py", + ) + ) + example_tiled_matmul_desc = ( + "This given architecture is for a model with tiled matrix multiplication: " ) - example_tiled_matmul_desc = "This given architecture is for a model with tiled matrix multiplication: " # k = 5 example_flash_attn = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_flash_attn.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_flash_attn.py" + ) ) example_flash_attn_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_flash_attn.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py" + ) ) example_flash_attn_desc = "This given architecture is for a model with simple io-aware implementation of attention, also known as flash attention: " examples = [] for s in shots: - if s not in ["ex_add", "ex_fuse_gelu", "ex_mnist2", "ex_tiled_matmul", "ex_flash_attn"]: + if s not in [ + "ex_add", + "ex_fuse_gelu", + "ex_mnist2", + "ex_tiled_matmul", + "ex_flash_attn", + ]: raise ValueError(f"Invalid shot: {s}") elif s == "ex_add": examples.append((example_add, example_add_new, example_add_desc)) elif s == "ex_fuse_gelu": - examples.append((example_fuse_gelu, example_fuse_gelu_new, example_fuse_gelu_desc)) - elif s == "ex_mnist2": # DEPRECATED + examples.append( + (example_fuse_gelu, example_fuse_gelu_new, example_fuse_gelu_desc) + ) + elif s == "ex_mnist2": # DEPRECATED raise ValueError("ex_mnist2 is deprecated") examples.append((example_mnist2, example_mnist2_new, exmaple_mnist2_desc)) elif s == "ex_tiled_matmul": - examples.append((example_tiled_matmul, example_tiled_matmul_new, example_tiled_matmul_desc)) + examples.append( + ( + example_tiled_matmul, + example_tiled_matmul_new, + example_tiled_matmul_desc, + ) + ) elif s == "ex_flash_attn": - examples.append((example_flash_attn, example_flash_attn_new, example_flash_attn_desc)) - + examples.append( + (example_flash_attn, example_flash_attn_new, example_flash_attn_desc) + ) for i, tup in enumerate(examples): base, kernel, desc = tup @@ -168,7 +212,7 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l ```\n\n """ -# should we put task here? + # should we put task here? prompt += f""" Task:\n\n Here is an example architecture:\n\n @@ -179,10 +223,12 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l prompt += PROBLEM_INSTRUCTION_CLEANED return prompt + +@weave.op def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> str: """ - Generate a prompt with a CoT example following a template - Avaliable CoT examples: + Generate a prompt with a CoT example following a template + Avaliable CoT examples: - ex_fuse_gelu: fused gelu - ex_mnist2: fused convolutions and relus - ex_tiled_matmul: tiled matrix multiplication @@ -190,51 +236,72 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> # I updated this to allow CoT. Also explicilty state think step by step. PROBLEM_INSTRUCTION_COT = """ -Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Do not output testing code. +Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Do not output testing code. In the end, make sure the final code block contains code for output architecture ModelNew with cuda code.\n Let's think step by step.\n -""" +""" prompt = PROBLEM_STATEMENT_CLEANED - + assert cot_example in ["ex_fuse_gelu", "ex_mnist2", "ex_tiled_matmul"] # k = 2 example_fuse_gelu = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py" + ) ) example_fuse_gelu_cot = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/cot/model_cot_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/cot/model_cot_fuse_gelu.py" + ) ) example_fuse_gelu_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py" + ) ) example_fuse_gelu_desc = "This given architecture is for a fused gelu: " # k = 3 example_mnist2 = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_mnist2.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_mnist2.py" + ) ) example_mnist2_cot = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/cot/model_cot_mnist2.py") + os.path.join(REPO_TOP_PATH, "src/kernelbench/prompts/cot/model_cot_mnist2.py") ) example_mnist2_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_mnist2.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py" + ) + ) + exmaple_mnist2_desc = ( + "This given architecture is for a model with fused convolutions and relus: " ) - exmaple_mnist2_desc = "This given architecture is for a model with fused convolutions and relus: " # k = 4 example_tiled_matmul = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py" + ) ) example_tiled_matmul_cot = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/cot/model_cot_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/cot/model_cot_tiled_matmul.py" + ) ) example_tiled_matmul_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, + "src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py", + ) + ) + example_tiled_matmul_desc = ( + "This given architecture is for a model with tiled matrix multiplication: " ) - example_tiled_matmul_desc = "This given architecture is for a model with tiled matrix multiplication: " - + match cot_example: case "ex_fuse_gelu": base = example_fuse_gelu @@ -252,9 +319,11 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> kernel = example_tiled_matmul_new desc = example_tiled_matmul_desc case _: - raise ValueError(f"Invalid CoT example: {cot_example} not found in CoT examples") + raise ValueError( + f"Invalid CoT example: {cot_example} not found in CoT examples" + ) - # construct example with + # construct example with # NOTE: we only do one example with CoT for now # 1. ref_src problem -> 2. Instruction -> 3. CoT -> 4. Solution prompt += f""" @@ -269,7 +338,7 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> ```\n\n """ -# show task to solve + # show task to solve prompt += f""" Task:\n\n Here is an example architecture:\n\n @@ -282,7 +351,6 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> return prompt - def prompt_generate_custom_cuda_from_file_one_example(ref_arch_src, example_ind=1): """ Deprecated: use prompt_generate_custom_cuda_from_prompt_template instead @@ -295,10 +363,10 @@ def prompt_generate_custom_cuda_from_file_one_example(ref_arch_src, example_ind= # These are strictly defined for now example_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_ex_{example_ind}.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_ex_{example_ind}.py" ) example_new_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_new_ex_{example_ind}.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_new_ex_{example_ind}.py" ) if not os.path.exists(example_arch_path): @@ -326,10 +394,10 @@ def prompt_generate_custom_cuda_from_prompt_template(ref_arch_src: str) -> str: # path to prompt template, show an example of Model (torch specifications) and ModelNew (torch + custom CUDA kernels) example_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_ex_add.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_ex_add.py" ) example_new_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_new_ex_add.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_new_ex_add.py" ) if not os.path.exists(example_arch_path): @@ -347,9 +415,11 @@ def prompt_generate_custom_cuda_from_prompt_template(ref_arch_src: str) -> str: return prompt_generate_custom_cuda(arch, example_arch, example_new_arch) -def prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src: str, gpu_name: str) -> str: +def prompt_generate_prompt_with_hardware_info_from_template( + ref_arch_src: str, gpu_name: str +) -> str: """ - Similar to prompt_generate_custom_cuda_from_prompt_template, + Similar to prompt_generate_custom_cuda_from_prompt_template, but with hardware information for the given GPU """ @@ -358,33 +428,37 @@ def prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src: str, g # path to prompt template, show an example of Model (torch specifications) and ModelNew (torch + custom CUDA kernels) example_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_ex_add.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_ex_add.py" ) example_new_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_new_ex_add.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_new_ex_add.py" ) - gpu_spec_file_path = os.path.join(REPO_TOP_PATH, f"src/prompts/hardware/gpu_specs.py") + gpu_spec_file_path = os.path.join( + REPO_TOP_PATH, f"src/kernelbench/prompts/hardware/gpu_specs.py" + ) example_arch = read_file(example_arch_path) example_new_arch = read_file(example_new_arch_path) gpu_spec_info = read_file(gpu_spec_file_path) return prompt_generate_prompt_with_hardware_info( - ref_arch_src=arch, - gpu_name=gpu_name, - example_arch_src=example_arch, - example_new_arch_src=example_new_arch, - gpu_spec_info_src=gpu_spec_info - ) - - - -def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, - gpu_name: str, - example_arch_src: str, - example_new_arch_src: str, - gpu_spec_info_src: str) -> str: + ref_arch_src=arch, + gpu_name=gpu_name, + example_arch_src=example_arch, + example_new_arch_src=example_new_arch, + gpu_spec_info_src=gpu_spec_info, + ) + + +@weave.op +def prompt_generate_prompt_with_hardware_info( + ref_arch_src: str, + gpu_name: str, + example_arch_src: str, + example_new_arch_src: str, + gpu_spec_info_src: str, +) -> str: """ Generate a prompt with hardware information for the given GPU gpu_spec_info_src: str of the gpu spec src file @@ -392,74 +466,98 @@ def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, # Create a dictionary to store the local namespace local_dict = {} - + # Execute the GPU spec file in the local namespace exec(gpu_spec_info_src, {}, local_dict) - + # Get the required variables from the local namespace - GPU_SPEC_INFO = local_dict.get('GPU_SPEC_INFO') - GPU_DEFINITIONS = local_dict.get('GPU_DEFINITIONS') - GPU_BEST_PRACTICES = local_dict.get('GPU_BEST_PRACTICES') - + GPU_SPEC_INFO = local_dict.get("GPU_SPEC_INFO") + GPU_DEFINITIONS = local_dict.get("GPU_DEFINITIONS") + GPU_BEST_PRACTICES = local_dict.get("GPU_BEST_PRACTICES") + if not GPU_SPEC_INFO or not GPU_DEFINITIONS or not GPU_BEST_PRACTICES: - raise ValueError("GPU_SPEC_INFO or GPU_DEFINITIONS or GPU_BEST_PRACTICES not found in gpu_spec_info_src") + raise ValueError( + "GPU_SPEC_INFO or GPU_DEFINITIONS or GPU_BEST_PRACTICES not found in gpu_spec_info_src" + ) assert gpu_name in GPU_SPEC_INFO, f"GPU name {gpu_name} not found in GPU_SPEC_INFO" - prompt = PROBLEM_STATEMENT - - if example_arch_src != "" and example_new_arch_src != "": - prompt += f""" - Here's an example to show you the syntax of inline embedding custom CUDA operators in torch: The example given architecture is: \n - ``` \n - {example_arch_src} - ``` \n - The example new arch with custom CUDA kernels looks like this: - ``` - {example_new_arch_src} - ``` \n - """ - + # Get GPU-specific information curr_gpu_spec_info = GPU_SPEC_INFO[gpu_name] - gpu_architecture = curr_gpu_spec_info.get("GPU Architecture") - prompt += f""" - Here is some information about the underlying hardware that you should keep in mind. \n\n -The GPU that will run the kernel is NVIDIA {gpu_name}, {gpu_architecture} architecture.\n\n""" - + + # Create the title and objective section + objective_section = """# CUDA Kernel Optimization Task + +## Objective +Your task is to optimize PyTorch models by replacing standard PyTorch operators with custom CUDA kernels. You should: +- Choose which operators to replace with custom implementations +- Consider operator fusion opportunities (e.g., combining matmul+relu) +- Explore algorithmic optimizations (e.g., online softmax) +- Rename your optimized implementation as "ModelNew" +""" + + # Create hardware specifications section + hardware_section = f"\n## Hardware Specifications (NVIDIA {gpu_name}, {gpu_architecture} architecture)\n" + hardware_specs = [] for key, value in curr_gpu_spec_info.items(): if key == "GPU Architecture": continue - prompt += f"""- We have {value} of {key}.\n""" - - - prompt += f"""\n\n -Here are some concepts about the GPU architecture that could be helpful: \n\n""" + hardware_specs.append(f"- {value} of {key}") + hardware_section += "\n".join(hardware_specs) + + # Create GPU concepts section + concepts_section = "\n\n## Key GPU Programming Concepts" + concepts = [] for key, value in GPU_DEFINITIONS.items(): - prompt += f"""- {key}: {value}\n""" + concepts.append(f"- {key}: {value}") + concepts_section += "\n" + "\n".join(concepts) - prompt += f"""\n\n -Here are some best practices for writing CUDA kernels on GPU: \n\n""" + # Create best practices section + practices_section = "\n\n## Best Practices" + practices = [] for best_practice in GPU_BEST_PRACTICES: - prompt += f"""- {best_practice}\n""" - - - prompt += f""" - You are given the following architecture: \n - ``` - {ref_arch_src} - ``` - """ - + practices.append(f"- {best_practice}") + practices_section += "\n" + "\n".join(practices) + + # Create examples section if provided + examples_section = "" + if example_arch_src and example_new_arch_src: + examples_section = f""" +## Example: Original Model +```python +{example_arch_src} +``` - prompt += PROBLEM_INSTRUCTION - return prompt +## Example: Optimized Model with Custom CUDA +```python +{example_new_arch_src} +``` +""" + # Create task section + task_section = f""" +## Your Task: Optimize This Model +```python +{ref_arch_src} +``` - return Nonoe +Implement an optimized version called "ModelNew" with custom CUDA operators. +""" + # Combine all sections into the final prompt + prompt = ( + objective_section + + hardware_section + + concepts_section + + practices_section + + examples_section + + task_section + ) + return prompt + return Nonoe def prompt_fix_compile(ref_arch_src, custom_cuda, metadata): @@ -477,7 +575,7 @@ def prompt_fix_compile(ref_arch_src, custom_cuda, metadata): ``` {metadata} ``` - + Please fix the compilation error in the new model code. Please output the corrected code in codeblocks. """ return prompt @@ -502,13 +600,16 @@ def prompt_fix_correctness(ref_arch_src, custom_cuda, metadata): """ return prompt + +@weave.op def main(): gpu_name = "L40S" - ref_arch_src = read_file(os.path.join(KERNEL_BENCH_PATH, f"level1/19_ReLU.py")) assert len(ref_arch_src) > 0, "ref_arch_src is empty" - prompt = prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src, gpu_name) + prompt = prompt_generate_prompt_with_hardware_info_from_template( + ref_arch_src, gpu_name + ) print(prompt) # Write prompt to temp file temp_file_path = os.path.join(REPO_TOP_PATH, "scratch", "prompt_draft.txt") @@ -516,5 +617,7 @@ def main(): with open(temp_file_path, "w") as f: f.write(prompt) + if __name__ == "__main__": + weave.init("prompt_constructor") main() diff --git a/src/prompts/README.md b/src/kernelbench/prompts/README.md similarity index 76% rename from src/prompts/README.md rename to src/kernelbench/prompts/README.md index 3aa01b58..6df80cca 100644 --- a/src/prompts/README.md +++ b/src/kernelbench/prompts/README.md @@ -1,5 +1,5 @@ -This folder includes PyTorch modules paired with CUDA kernels, which are used as in-context examples in KernelBench. +This folder includes PyTorch modules paired with CUDA kernels, which are used as in-context examples in KernelBench. @@ -8,4 +8,4 @@ Acknowledgements: - Minimal Flash Attention: [Peter Kim, Minimal Flash Attention](https://github.com/tspeterkim/flash-attention-minimal/tree/main) There are some examples. -[TODO] Table detailing content and speedups of each example \ No newline at end of file +[TODO] Table detailing content and speedups of each example diff --git a/src/prompts/cot/model_cot_fuse_gelu.py b/src/kernelbench/prompts/cot/model_cot_fuse_gelu.py similarity index 98% rename from src/prompts/cot/model_cot_fuse_gelu.py rename to src/kernelbench/prompts/cot/model_cot_fuse_gelu.py index de740c03..cd9c8741 100644 --- a/src/prompts/cot/model_cot_fuse_gelu.py +++ b/src/kernelbench/prompts/cot/model_cot_fuse_gelu.py @@ -2,6 +2,7 @@ Let us think about how to optimize the code step by step. """ + # Step 1. Let us break down the pytorch module into step by step instructions. class Model(nn.Module): def __init__(self) -> None: @@ -18,7 +19,7 @@ def forward(self, x): Returns: torch.Tensor: Output tensor after applying GELU activation """ - + # First, alculate the constant term (2/pi)^0.5 const = (2 / torch.pi) ** 0.5 @@ -39,6 +40,6 @@ def forward(self, x): Third, we can compute: float inner_term = x + 0.044715f * (x*x*x) Fourth, we can compute: float out[i] = 0.5f * x * (1.0f + tanhf(const * inner_term)) -""" +""" # Step 3. Let us put all of the steps together into CUDA kernel code. diff --git a/src/prompts/cot/model_cot_mnist2.py b/src/kernelbench/prompts/cot/model_cot_mnist2.py similarity index 84% rename from src/prompts/cot/model_cot_mnist2.py rename to src/kernelbench/prompts/cot/model_cot_mnist2.py index 0a9d5ef1..c6640187 100644 --- a/src/prompts/cot/model_cot_mnist2.py +++ b/src/kernelbench/prompts/cot/model_cot_mnist2.py @@ -2,14 +2,15 @@ Let us think about how to optimize the code step by step. """ + # Step 1: Let us break down the PyTorch module into step-by-step instructions. class Model(nn.Module): def __init__(self) -> None: super().__init__() self.conv1 = nn.Conv2d(1, 10, kernel_size=5) # First convolutional layer - self.conv2 = nn.Conv2d(10, 20, kernel_size=5) # Second convolutional layer - self.fc1 = nn.Linear(320, 50) # First fully connected layer - self.fc2 = nn.Linear(50, 10) # Second fully connected layer + self.conv2 = nn.Conv2d(10, 20, kernel_size=5) # Second convolutional layer + self.fc1 = nn.Linear(320, 50) # First fully connected layer + self.fc2 = nn.Linear(50, 10) # Second fully connected layer def forward(self, x): """ @@ -31,10 +32,11 @@ def forward(self, x): """ x = F.relu(F.max_pool2d(self.conv1(x), 2)) # Steps 1-3 x = F.relu(F.max_pool2d(self.conv2(x), 2)) # Steps 4-6 - x = x.view(-1, 320) # Step 7 - x = F.relu(self.fc1(x)) # Steps 8-9 - x = self.fc2(x) # Step 10 - return F.log_softmax(x, dim=1) # Step 11 + x = x.view(-1, 320) # Step 7 + x = F.relu(self.fc1(x)) # Steps 8-9 + x = self.fc2(x) # Step 10 + return F.log_softmax(x, dim=1) # Step 11 + # Step 2: Let us describe how each step could be implemented inside of a CUDA kernel. """ @@ -73,4 +75,3 @@ def forward(self, x): """ # Step 3. Let us put all of the steps together into CUDA kernel code. - diff --git a/src/prompts/cot/model_cot_tiled_matmul.py b/src/kernelbench/prompts/cot/model_cot_tiled_matmul.py similarity index 93% rename from src/prompts/cot/model_cot_tiled_matmul.py rename to src/kernelbench/prompts/cot/model_cot_tiled_matmul.py index c57620d9..c331c897 100644 --- a/src/prompts/cot/model_cot_tiled_matmul.py +++ b/src/kernelbench/prompts/cot/model_cot_tiled_matmul.py @@ -2,6 +2,7 @@ Let us think about how to optimize the code step by step. """ + # Step 1: Let us break down the PyTorch module into step-by-step instructions. class Model(nn.Module): def __init__(self) -> None: @@ -13,7 +14,7 @@ def forward(self, a, b): 1. The input tensors `a` and `b` must have compatible shapes for matrix multiplication. 2. Each element of the resulting tensor is computed as the dot product of a row of `a` and a column of `b`. - + Args: a (torch.Tensor): A tensor of shape (m, n). b (torch.Tensor): A tensor of shape (n, p). @@ -23,7 +24,8 @@ def forward(self, a, b): """ return a @ b -#Step 2: Let us describe how each step could be implemented inside of a CUDA kernel. + +# Step 2: Let us describe how each step could be implemented inside of a CUDA kernel. """ 1. Load the input tensor elements into shared memory: - Each thread block loads a tile of `a` and `b` into shared memory to reduce global memory accesses. diff --git a/src/prompts/model_ex_add.py b/src/kernelbench/prompts/few_shot/model_ex_add.py similarity index 100% rename from src/prompts/model_ex_add.py rename to src/kernelbench/prompts/few_shot/model_ex_add.py diff --git a/src/prompts/few_shot/model_ex_flash_attn.py b/src/kernelbench/prompts/few_shot/model_ex_flash_attn.py similarity index 91% rename from src/prompts/few_shot/model_ex_flash_attn.py rename to src/kernelbench/prompts/few_shot/model_ex_flash_attn.py index 64d34e05..d7702ae9 100644 --- a/src/prompts/few_shot/model_ex_flash_attn.py +++ b/src/kernelbench/prompts/few_shot/model_ex_flash_attn.py @@ -8,20 +8,23 @@ class Model(nn.Module): """ Model that performs an attention operation """ + def __init__(self) -> None: super().__init__() def forward(self, Q, K, V): - att = (Q @ K.transpose(-2, -1) * (1.0 / math.sqrt(K.size(-1)))) + att = Q @ K.transpose(-2, -1) * (1.0 / math.sqrt(K.size(-1))) att = F.softmax(att, dim=-1) y = att @ V return y + batch_size = 32 n_head = 12 seq_len = 64 head_embd = 32 + def get_inputs(): # randomly generate input tensors based on the model architecture Q = torch.randn(batch_size, n_head, seq_len, head_embd) diff --git a/src/prompts/few_shot/model_ex_fuse_gelu.py b/src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py similarity index 90% rename from src/prompts/few_shot/model_ex_fuse_gelu.py rename to src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py index 62fab61f..bc6727cb 100644 --- a/src/prompts/few_shot/model_ex_fuse_gelu.py +++ b/src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py @@ -8,7 +8,7 @@ def __init__(self) -> None: super().__init__() def forward(self, x): - return F.gelu(x, approximate='tanh') + return F.gelu(x, approximate="tanh") def get_inputs(): @@ -20,5 +20,3 @@ def get_inputs(): def get_init_inputs(): # randomly generate tensors required for initialization based on the model architecture return [] - - diff --git a/src/prompts/few_shot/model_ex_mnist2.py b/src/kernelbench/prompts/few_shot/model_ex_mnist2.py similarity index 98% rename from src/prompts/few_shot/model_ex_mnist2.py rename to src/kernelbench/prompts/few_shot/model_ex_mnist2.py index b870d395..dce2050a 100644 --- a/src/prompts/few_shot/model_ex_mnist2.py +++ b/src/kernelbench/prompts/few_shot/model_ex_mnist2.py @@ -28,4 +28,4 @@ def get_inputs(): def get_init_inputs(): # randomly generate tensors required for initialization based on the model architecture - return [] \ No newline at end of file + return [] diff --git a/src/prompts/few_shot/model_ex_tiled_matmul.py b/src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py similarity index 95% rename from src/prompts/few_shot/model_ex_tiled_matmul.py rename to src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py index b4fd3bc4..9fed9469 100644 --- a/src/prompts/few_shot/model_ex_tiled_matmul.py +++ b/src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py @@ -8,7 +8,7 @@ def __init__(self) -> None: super().__init__() def forward(self, a, b): - return a@b + return a @ b def get_inputs(): @@ -21,4 +21,3 @@ def get_inputs(): def get_init_inputs(): # randomly generate tensors required for initialization based on the model architecture return [] - diff --git a/src/prompts/few_shot/model_new_ex_add.py b/src/kernelbench/prompts/few_shot/model_new_ex_add.py similarity index 93% rename from src/prompts/few_shot/model_new_ex_add.py rename to src/kernelbench/prompts/few_shot/model_new_ex_add.py index e9805c11..e103a502 100644 --- a/src/prompts/few_shot/model_new_ex_add.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_add.py @@ -28,9 +28,7 @@ } """ -cpp_src = ( - "torch::Tensor elementwise_add_cuda(torch::Tensor a, torch::Tensor b);" -) +cpp_src = "torch::Tensor elementwise_add_cuda(torch::Tensor a, torch::Tensor b);" # Compile the inline CUDA code for element-wise addition elementwise_add = load_inline( diff --git a/src/prompts/few_shot/model_new_ex_flash_attn.py b/src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py similarity index 98% rename from src/prompts/few_shot/model_new_ex_flash_attn.py rename to src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py index b3df395c..8c67cb40 100644 --- a/src/prompts/few_shot/model_new_ex_flash_attn.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py @@ -3,7 +3,7 @@ import torch.nn.functional as F from torch.utils.cpp_extension import load_inline -source = ''' +source = """ #include #include #include @@ -119,17 +119,17 @@ ); return O; } -''' +""" cpp_src = """ torch::Tensor attention(torch::Tensor Q, torch::Tensor K, torch::Tensor V);""" attention = torch.utils.cpp_extension.load_inline( - 'attention', + "attention", cpp_sources=cpp_src, cuda_sources=source, - functions=['attention'], + functions=["attention"], with_cuda=True, - extra_cuda_cflags=['-O2'], + extra_cuda_cflags=["-O2"], ) diff --git a/src/prompts/few_shot/model_new_ex_fuse_gelu.py b/src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py similarity index 97% rename from src/prompts/few_shot/model_new_ex_fuse_gelu.py rename to src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py index dd82c1c6..3e9e1df0 100644 --- a/src/prompts/few_shot/model_new_ex_fuse_gelu.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py @@ -72,7 +72,7 @@ name="fused_gelu", # Name of the extension cpp_sources=cpp_src, # C++ source code cuda_sources=source, # CUDA source code - functions=['my_gelu', 'my_gelu_out'], # Functions to expose + functions=["my_gelu", "my_gelu_out"], # Functions to expose verbose=True, extra_cflags=[""], extra_ldflags=[""], diff --git a/src/prompts/few_shot/model_new_ex_mnist2.py b/src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py similarity index 88% rename from src/prompts/few_shot/model_new_ex_mnist2.py rename to src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py index c65d295b..8dfd221f 100644 --- a/src/prompts/few_shot/model_new_ex_mnist2.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py @@ -46,34 +46,37 @@ dim3 num_blocks((output_width + block_size.x - 1) / block_size.x, (output_height + block_size.y - 1) / block_size.y, channels); max_pool2d_kernel<<>>( - input.data_ptr(), - output.data_ptr(), - channels, - input_height, - input_width, - kernel_size, - kernel_size, - stride, - output_height, + input.data_ptr(), + output.data_ptr(), + channels, + input_height, + input_width, + kernel_size, + kernel_size, + stride, + output_height, output_width ); return output; } """ -cpp_src = "torch::Tensor max_pool2d_cuda(torch::Tensor input, int kernel_size, int stride);" +cpp_src = ( + "torch::Tensor max_pool2d_cuda(torch::Tensor input, int kernel_size, int stride);" +) # Compile the inline CUDA code custom_max_pool = load_inline( - name='custom_max_pool', + name="custom_max_pool", cpp_sources=cpp_src, cuda_sources=source, - functions=['max_pool2d_cuda'], + functions=["max_pool2d_cuda"], verbose=True, extra_cflags=[""], extra_ldflags=[""], ) + # Custom MNIST model using inlined max_pool2d_cuda class ModelNew(nn.Module): def __init__(self) -> None: diff --git a/src/prompts/few_shot/model_new_ex_tiled_matmul.py b/src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py similarity index 95% rename from src/prompts/few_shot/model_new_ex_tiled_matmul.py rename to src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py index dffe8e6c..ebc1cd1f 100644 --- a/src/prompts/few_shot/model_new_ex_tiled_matmul.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py @@ -25,7 +25,7 @@ /** * @brief Tiled matrix multiplication kernel. - * + * * This kernel performs matrix multiplication using shared memory tiles to improve performance. * * @param out Pointer to the output matrix @@ -73,7 +73,7 @@ /** * @brief Wrapper function for tiled matrix multiplication kernel. - * + * * This function checks input tensors, sets up kernel parameters, and launches the CUDA kernel. * * @param m First input matrix @@ -112,11 +112,12 @@ "tiled_matmul", # Name of the extension cpp_sources=cpp_src, # C++ interface cuda_sources=source, # CUDA source code - functions=['tiled_matmul_cuda'], # Exported functions - extra_cuda_cflags=['--ptxas-options=-v'], # Additional CUDA compilation flags - verbose=True # Enable verbose output during compilation + functions=["tiled_matmul_cuda"], # Exported functions + extra_cuda_cflags=["--ptxas-options=-v"], # Additional CUDA compilation flags + verbose=True, # Enable verbose output during compilation ) + class ModelNew(nn.Module): def __init__(self) -> None: super().__init__() diff --git a/src/prompts/hardware/gpu_specs.py b/src/kernelbench/prompts/hardware/gpu_specs.py similarity index 99% rename from src/prompts/hardware/gpu_specs.py rename to src/kernelbench/prompts/hardware/gpu_specs.py index dcf60c7f..756cee0b 100644 --- a/src/prompts/hardware/gpu_specs.py +++ b/src/kernelbench/prompts/hardware/gpu_specs.py @@ -3,7 +3,6 @@ """ - GPU_SPEC_INFO = { "L40S": { "GPU Architecture": "Ada", @@ -89,7 +88,7 @@ "Maximum number of thread blocks per SM": "24", "Shared memory capacity per SM": "100 KB", "Maximum shared memory per thread block": "99 KB", - }, + }, "T4": { "GPU Architecture": "Turing", "GPU Memory": "16 GB GDDR6", @@ -118,7 +117,7 @@ "Maximum number of thread blocks per SM": "32", "Shared memory capacity per SM": "164 KB", "Maximum shared memory per thread block": "163 KB", - } + }, } # Basic GPU concept definitions @@ -135,7 +134,6 @@ } - GPU_BEST_PRACTICES = [ # From https://docs.nvidia.com/cuda/ada-tuning-guide/index.html # CUDA Best Practices Section @@ -147,4 +145,4 @@ "Avoid long sequences of diverged execution by threads within the same warp.", # we added this to reference the specific GPU architecture "Use specialized instructions based on the specific GPU architecture", -] \ No newline at end of file +] diff --git a/src/prompts/model_new_ex_0.py b/src/kernelbench/prompts/model_ex_0.py similarity index 100% rename from src/prompts/model_new_ex_0.py rename to src/kernelbench/prompts/model_ex_0.py diff --git a/src/prompts/model_ex_1.py b/src/kernelbench/prompts/model_ex_1.py similarity index 100% rename from src/prompts/model_ex_1.py rename to src/kernelbench/prompts/model_ex_1.py diff --git a/src/prompts/model_ex_2.py b/src/kernelbench/prompts/model_ex_2.py similarity index 100% rename from src/prompts/model_ex_2.py rename to src/kernelbench/prompts/model_ex_2.py diff --git a/src/prompts/few_shot/model_ex_add.py b/src/kernelbench/prompts/model_ex_add.py similarity index 97% rename from src/prompts/few_shot/model_ex_add.py rename to src/kernelbench/prompts/model_ex_add.py index f52d64b7..9016fb0e 100644 --- a/src/prompts/few_shot/model_ex_add.py +++ b/src/kernelbench/prompts/model_ex_add.py @@ -20,4 +20,4 @@ def get_inputs(): def get_init_inputs(): # randomly generate tensors required for initialization based on the model architecture - return [] \ No newline at end of file + return [] diff --git a/src/scratch/model.py b/src/kernelbench/prompts/model_new_ex_0.py similarity index 100% rename from src/scratch/model.py rename to src/kernelbench/prompts/model_new_ex_0.py diff --git a/src/prompts/model_new_ex_1.py b/src/kernelbench/prompts/model_new_ex_1.py similarity index 100% rename from src/prompts/model_new_ex_1.py rename to src/kernelbench/prompts/model_new_ex_1.py diff --git a/src/prompts/model_new_ex_2.py b/src/kernelbench/prompts/model_new_ex_2.py similarity index 100% rename from src/prompts/model_new_ex_2.py rename to src/kernelbench/prompts/model_new_ex_2.py diff --git a/src/prompts/model_new_ex_add.py b/src/kernelbench/prompts/model_new_ex_add.py similarity index 100% rename from src/prompts/model_new_ex_add.py rename to src/kernelbench/prompts/model_new_ex_add.py diff --git a/src/kernelbench/score.py b/src/kernelbench/score.py new file mode 100644 index 00000000..d44edf30 --- /dev/null +++ b/src/kernelbench/score.py @@ -0,0 +1,61 @@ +import numpy as np + + +def geometric_mean_speed_ratio_correct_only( + is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int +) -> float: + """ + Geometric mean of the speed ratio for correct samples + """ + filtered_baseline_speed = np.array( + [x for i, x in enumerate(baseline_speed) if is_correct[i]] + ) + filtered_actual_speed = np.array( + [x for i, x in enumerate(actual_speed) if is_correct[i]] + ) + speed_up = filtered_baseline_speed / filtered_actual_speed + prod = np.prod(speed_up) + n_correct = np.sum(is_correct) # Count number of correct samples + + return prod ** (1 / n_correct) if n_correct > 0 else 0 + + +def geometric_mean_speed_ratio_correct_and_faster_only( + is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int +) -> float: + """ + Geometric mean of the speed ratio for correct samples that have speedup > 1 + """ + filtered_baseline_speed = np.array( + [x for i, x in enumerate(baseline_speed) if is_correct[i]] + ) + filtered_actual_speed = np.array( + [x for i, x in enumerate(actual_speed) if is_correct[i]] + ) + speed_up = filtered_baseline_speed / filtered_actual_speed + speed_up = np.array([x for x in speed_up if x > 1]) + prod = np.prod(speed_up) + n_correct_and_faster = len(speed_up) + + return prod ** (1 / n_correct_and_faster) if n_correct_and_faster > 0 else 0 + + +def fastp( + is_correct: np.ndarray, + baseline_speed: np.ndarray, + actual_speed: np.ndarray, + n: int, + p: float, +) -> float: + """ + Rate of samples within a threshold p + """ + filtered_baseline_speed = np.array( + [x for i, x in enumerate(baseline_speed) if is_correct[i]] + ) + filtered_actual_speed = np.array( + [x for i, x in enumerate(actual_speed) if is_correct[i]] + ) + speed_up = filtered_baseline_speed / filtered_actual_speed + fast_p_score = np.sum(speed_up > p) + return fast_p_score / n if n > 0 else 0 diff --git a/src/kernelbench/utils.py b/src/kernelbench/utils.py new file mode 100644 index 00000000..def27496 --- /dev/null +++ b/src/kernelbench/utils.py @@ -0,0 +1,209 @@ +######################## +# Utils Functions +######################## + +import multiprocessing +import re +import os +from tqdm import tqdm + +# from datasets import load_dataset +import time + +from concurrent.futures import ProcessPoolExecutor, as_completed, ThreadPoolExecutor + + +def set_gpu_arch(arch_list: list[str]): + """ + Set env variable for torch cuda arch list to build kernels for specified architectures + """ + valid_archs = ["Maxwell", "Pascal", "Volta", "Turing", "Ampere", "Hopper", "Ada"] + for arch in arch_list: + if arch not in valid_archs: + raise ValueError( + f"Invalid architecture: {arch}. Must be one of {valid_archs}" + ) + + os.environ["TORCH_CUDA_ARCH_LIST"] = ";".join(arch_list) + + +def read_file(file_path: str) -> str: + if not os.path.exists(file_path): + print(f"File {file_path} does not exist") + return "" + + try: + with open(file_path, "r") as file: + return file.read() + except Exception as e: + print(f"Error reading file {file_path}: {e}") + return "" + + +def print_messages(messages): + for message in messages: + print(message["role"]) + print(message["content"]) + print("-" * 50) + print("\n\n") + + +def extract_python_code(text): + """ + Extract python code from model output + """ + pattern = r"```python\n(.*?)```" + matches = re.findall(pattern, text, re.DOTALL) + return "\n".join(matches) if matches else "" + + +def remove_code_block_header(code, code_language_type): + """Assume input is code but just with like python, cpp, etc. at the top""" + if code.startswith(code_language_type): + code = code[len(code_language_type) :].strip() + return code + + +def extract_first_code(output_string: str, code_language_types: list[str]) -> str: + """ + Extract first code block from model output, specified by code_language_type + """ + trimmed = output_string.strip() + + # Extracting the first occurrence of content between backticks + code_match = re.search(r"```(.*?)```", trimmed, re.DOTALL) + + if code_match: + # Strip leading and trailing whitespace from the extracted code + code = code_match.group(1).strip() + + # depends on code_language_type: cpp, python, etc. + # sometimes the block of code is ```cpp ... ``` instead of ``` ... ``` + # in this case strip the cpp out + for code_type in code_language_types: + if code.startswith(code_type): + code = code[len(code_type) :].strip() + + return code + + return None + + +def extract_last_code(output_string: str, code_language_types: list[str]) -> str | None: + """ + Extract last code block from model output, specified by code_language_type + """ + trimmed = output_string.strip() + + # Find all matches of code blocks + code_matches = re.finditer(r"```(.*?)```", trimmed, re.DOTALL) + + # Get the last match by converting to list and taking the last element + matches_list = list(code_matches) + if matches_list: + last_match = matches_list[-1] + code = last_match.group(1).strip() + + # Remove language type headers + for code_type in code_language_types: + if code.startswith(code_type): + code = code[len(code_type) :].strip() + + return code + + return None + + +def extract_code_blocks(text, code_language_types: list[str]) -> str: + """ + Extract all code blocks from text, combine them to return as a single string + """ + pattern = r"```.*?\n(.*?)```" + matches = re.findall(pattern, text, re.DOTALL) + + # Combine all code blocks and remove language type headers + combined_code = [] + for match in matches: + code = match.strip() + # Remove any language type headers + for lang_type in code_language_types: + if code.startswith(lang_type): + code = code[len(lang_type) :].strip() + combined_code.append(code) + + return " \n ".join(combined_code) if combined_code else "" + + +################################################################################ +# Scale up experiments in parallel +################################################################################ + + +def maybe_multithread( + func, instances, num_workers, time_interval=0.0, *shared_args, **shared_kwargs +): + """ + Multithreaded execution of func, with optional time interval between queries + Ideal for querying LLM APIs, does not provide process isolation + """ + output_data = [] + if num_workers not in [1, None]: + with tqdm(total=len(instances), smoothing=0) as pbar: + with ThreadPoolExecutor(max_workers=num_workers) as executor: + + # Submit tasks one at a time with delay between them + futures = [] + for instance in instances: + futures.append( + executor.submit(func, instance, *shared_args, **shared_kwargs) + ) + time.sleep(time_interval) # sleep between submitting each task + + # Wait for each future to complete + for future in as_completed(futures): + pbar.update(1) + try: + result = future.result() + if result is not None: + output_data.append(result) + except Exception as e: + print("Got an error!", e) + continue + else: + for instance in tqdm(instances): + output = func(instance, *shared_args, **shared_kwargs) + if output is not None: + output_data.append(output) + + return output_data + + +def maybe_multiprocess_cuda( + func, instances, num_workers, *shared_args, **shared_kwargs +): + """ + From monkeys, but modified to work with CUDA + """ + output_data = [] + multiprocessing.set_start_method( + "spawn", force=True + ) # this is necessary for CUDA to work + + with tqdm(total=len(instances), smoothing=0) as pbar: + with ProcessPoolExecutor(max_workers=num_workers) as executor: + # Create a future for running each instance + futures = { + executor.submit(func, instance, *shared_args, **shared_kwargs): None + for instance in instances + } + # Wait for each future to complete + for future in as_completed(futures): + pbar.update(1) + try: + result = future.result() + if result is not None: + output_data.append(result) + except Exception as e: + print("Got an error!", e) + continue + return output_data diff --git a/src/score.py b/src/score.py deleted file mode 100644 index 09b8c1fa..00000000 --- a/src/score.py +++ /dev/null @@ -1,36 +0,0 @@ -import numpy as np - -def geometric_mean_speed_ratio_correct_only(is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int) -> float: - """ - Geometric mean of the speed ratio for correct samples - """ - filtered_baseline_speed = np.array([x for i, x in enumerate(baseline_speed) if is_correct[i]]) - filtered_actual_speed = np.array([x for i, x in enumerate(actual_speed) if is_correct[i]]) - speed_up = filtered_baseline_speed / filtered_actual_speed - prod = np.prod(speed_up) - n_correct = np.sum(is_correct) # Count number of correct samples - - return prod ** (1 / n_correct) if n_correct > 0 else 0 - -def geometric_mean_speed_ratio_correct_and_faster_only(is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int) -> float: - """ - Geometric mean of the speed ratio for correct samples that have speedup > 1 - """ - filtered_baseline_speed = np.array([x for i, x in enumerate(baseline_speed) if is_correct[i]]) - filtered_actual_speed = np.array([x for i, x in enumerate(actual_speed) if is_correct[i]]) - speed_up = filtered_baseline_speed / filtered_actual_speed - speed_up = np.array([x for x in speed_up if x > 1]) - prod = np.prod(speed_up) - n_correct_and_faster = len(speed_up) - - return prod ** (1 / n_correct_and_faster) if n_correct_and_faster > 0 else 0 - -def fastp(is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int, p: float) -> float: - """ - Rate of samples within a threshold p - """ - filtered_baseline_speed = np.array([x for i, x in enumerate(baseline_speed) if is_correct[i]]) - filtered_actual_speed = np.array([x for i, x in enumerate(actual_speed) if is_correct[i]]) - speed_up = filtered_baseline_speed / filtered_actual_speed - fast_p_score = np.sum(speed_up > p) - return fast_p_score / n if n > 0 else 0 \ No newline at end of file diff --git a/src/scratch/log.txt b/src/scratch/log.txt deleted file mode 100644 index 9b231c5f..00000000 --- a/src/scratch/log.txt +++ /dev/null @@ -1,60 +0,0 @@ -[1/3] /usr/local/cuda-12.3/bin/nvcc --generate-dependencies-with-compile --dependency-output cuda.cuda.o.d -DTORCH_EXTENSION_NAME=matmul_sum_max -DTORCH_API_INCLUDE_EXTENSION_H -DPYBIND11_COMPILER_TYPE=\"_gcc\" -DPYBIND11_STDLIB=\"_libstdcpp\" -DPYBIND11_BUILD_ABI=\"_cxxabi1011\" -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/torch/csrc/api/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/TH -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/THC -isystem /usr/local/cuda-12.3/include -isystem /matx/u/aco/miniconda3/envs/myenv/include/python3.11 -D_GLIBCXX_USE_CXX11_ABI=0 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_89,code=compute_89 -gencode=arch=compute_89,code=sm_89 --compiler-options '-fPIC' -std=c++17 -c /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu -o cuda.cuda.o -FAILED: cuda.cuda.o -/usr/local/cuda-12.3/bin/nvcc --generate-dependencies-with-compile --dependency-output cuda.cuda.o.d -DTORCH_EXTENSION_NAME=matmul_sum_max -DTORCH_API_INCLUDE_EXTENSION_H -DPYBIND11_COMPILER_TYPE=\"_gcc\" -DPYBIND11_STDLIB=\"_libstdcpp\" -DPYBIND11_BUILD_ABI=\"_cxxabi1011\" -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/torch/csrc/api/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/TH -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/THC -isystem /usr/local/cuda-12.3/include -isystem /matx/u/aco/miniconda3/envs/myenv/include/python3.11 -D_GLIBCXX_USE_CXX11_ABI=0 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_89,code=compute_89 -gencode=arch=compute_89,code=sm_89 --compiler-options '-fPIC' -std=c++17 -c /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu -o cuda.cuda.o -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(24): error: identifier "dtype" is undefined - auto out = torch::zeros({batch_size}, dtype=torch::float32, device=a.device); - ^ - -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(24): error: namespace "torch" has no member "float32" - auto out = torch::zeros({batch_size}, dtype=torch::float32, device=a.device); - ^ - -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(24): error: identifier "device" is undefined - auto out = torch::zeros({batch_size}, dtype=torch::float32, device=a.device); - ^ - -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(24): error: a pointer to a bound function may only be used to call the function - auto out = torch::zeros({batch_size}, dtype=torch::float32, device=a.device); - ^ - -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(30): error: no instance of overloaded function "at::Tensor::view" matches the argument list - argument types are: (int, int) - object type is: at::Tensor - return out.view(-1, 1); - ^ - -5 errors detected in the compilation of "/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu". -[2/3] c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=matmul_sum_max -DTORCH_API_INCLUDE_EXTENSION_H -DPYBIND11_COMPILER_TYPE=\"_gcc\" -DPYBIND11_STDLIB=\"_libstdcpp\" -DPYBIND11_BUILD_ABI=\"_cxxabi1011\" -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/torch/csrc/api/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/TH -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/THC -isystem /usr/local/cuda-12.3/include -isystem /matx/u/aco/miniconda3/envs/myenv/include/python3.11 -D_GLIBCXX_USE_CXX11_ABI=0 -fPIC -std=c++17 -c /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/main.cpp -o main.o -ninja: build stopped: subcommand failed. -Using /sailhome/aco/.cache/torch_extensions/py311_cu121 as PyTorch extensions root... -Creating extension directory /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max... -Detected CUDA files, patching ldflags -Emitting ninja build file /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/build.ninja... -/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py:1965: UserWarning: TORCH_CUDA_ARCH_LIST is not set, all archs for visible cards are included for compilation. -If this is not desired, please set os.environ['TORCH_CUDA_ARCH_LIST']. - warnings.warn( -Building extension module matmul_sum_max... -Allowing ninja to set a default number of workers... (overridable by setting the environment variable MAX_JOBS=N) -Traceback (most recent call last): - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 2105, in _run_ninja_build - subprocess.run( - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/subprocess.py", line 571, in run - raise CalledProcessError(retcode, process.args, -subprocess.CalledProcessError: Command '['ninja', '-v']' returned non-zero exit status 1. - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "/matx/u/aco/KernelBenchInternal/src/scratch/model_new.py", line 40, in - matmul_sum_max = load_inline( - ^^^^^^^^^^^^ - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 1647, in load_inline - return _jit_compile( - ^^^^^^^^^^^^^ - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 1722, in _jit_compile - _write_ninja_file_and_build_library( - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 1834, in _write_ninja_file_and_build_library - _run_ninja_build( - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 2121, in _run_ninja_build - raise RuntimeError(message) from e -RuntimeError: Error building extension 'matmul_sum_max' diff --git a/src/scratch/model_new.py b/src/scratch/model_new.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/scratch/prompt.txt b/src/scratch/prompt.txt deleted file mode 100644 index 78dbb009..00000000 --- a/src/scratch/prompt.txt +++ /dev/null @@ -1,137 +0,0 @@ - - You write custom CUDA kernels to replace the pytorch operators in the given architecture to get speedups. - - You have complete freedom to choose the set of operators you want to replace. You may make the decision to replace some operators with custom CUDA kernels and leave others unchanged. You may replace multiple operators with custom implementations, consider operator fusion opportunities (combining multiple operators into a single kernel, for example, combining matmul+relu), or algorithmic changes (such as online softmax). You are only limited by your imagination. - - - Here's an example to show you the syntax of inline embedding custom CUDA operators in torch: The example given architecture is: - - ``` - import torch -import torch.nn as nn -import torch.nn.functional as F - -class Model(nn.Module): - def __init__(self) -> None: - super().__init__() - - def forward(self, a, b): - return a + b - -def get_inputs(): - # randomly generate input tensors based on the model architecture - a = torch.randn(1, 128).cuda() - b = torch.randn(1, 128).cuda() - return [a, b] - -def get_init_inputs(): - # randomly generate tensors required for initialization based on the model architecture - return [] - ``` - - The example new arch with custom CUDA kernels looks like this: - ``` - import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.utils.cpp_extension import load_inline - -# Define the custom CUDA kernel for element-wise addition -elementwise_add_source = """ -#include -#include - -__global__ void elementwise_add_kernel(const float* a, const float* b, float* out, int size) { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < size) { - out[idx] = a[idx] + b[idx]; - } -} - -torch::Tensor elementwise_add_cuda(torch::Tensor a, torch::Tensor b) { - auto size = a.numel(); - auto out = torch::zeros_like(a); - - const int block_size = 256; - const int num_blocks = (size + block_size - 1) / block_size; - - elementwise_add_kernel<<>>(a.data_ptr(), b.data_ptr(), out.data_ptr(), size); - - return out; -} -""" - -elementwise_add_cpp_source = "torch::Tensor elementwise_add_cuda(torch::Tensor a, torch::Tensor b);" - -# Compile the inline CUDA code for element-wise addition -elementwise_add = load_inline( - name='elementwise_add', - cpp_sources=elementwise_add_cpp_source, - cuda_sources=elementwise_add_source, - functions=['elementwise_add_cuda'], - verbose=True, - extra_cflags=[''], - extra_ldflags=[''] -) - -class ModelNew(nn.Module): - def __init__(self) -> None: - super().__init__() - self.elementwise_add = elementwise_add - - def forward(self, a, b): - return self.elementwise_add.elementwise_add_cuda(a, b) - -def get_inputs(): - # randomly generate input tensors based on the model architecture - a = torch.randn(4096).cuda() - b = torch.randn(4096).cuda() - return [a, b] - -def get_init_inputs(): - # randomly generate tensors required for initialization based on the model architecture - return [] - ``` - - - You are given the following architecture: - - ``` - import torch -import torch.nn as nn - -class Model(nn.Module): - """ - Simple model that performs a single matrix multiplication (C = A * B) - """ - def __init__(self): - super(Model, self).__init__() - - def forward(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """ - Performs matrix multiplication. - - Args: - A: Input tensor of shape (M, K). - B: Input tensor of shape (K, N). - - Returns: - Output tensor of shape (M, N). - """ - return torch.matmul(A, B) - -M = 1024 -K = 4096 -N = 2048 - -def get_inputs(): - A = torch.randn(M, K) - B = torch.randn(K, N) - return [A, B] - -def get_init_inputs(): - return [] # No special initialization inputs needed - ``` - Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. - - \ No newline at end of file diff --git a/src/scratch/test.py b/src/scratch/test.py deleted file mode 100644 index dbead51c..00000000 --- a/src/scratch/test.py +++ /dev/null @@ -1,69 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -import random -import numpy as np - -# load model and the modified model -from model import Model -from model import get_inputs -from model import get_init_inputs -from model_new import ModelNew - -torch.cuda.synchronize() - - -def set_seed(seed): - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - random.seed(seed) - np.random.seed(seed) - - -def check_correctness(): - # run the model and check correctness - with torch.no_grad(): - - # generate inputs and init_inputs, and instantiate models - set_seed(42) - inputs = get_inputs() - set_seed(42) - init_inputs = get_init_inputs() - - # move to GPU - inputs = [x.cuda() if isinstance(x, torch.Tensor) else x for x in inputs] - init_inputs = [ - x.cuda() if isinstance(x, torch.Tensor) else x for x in init_inputs - ] - - set_seed(42) - model = Model(*init_inputs).cuda() - set_seed(42) - model_new = ModelNew(*init_inputs).cuda() - - # forward pass - output = model(*inputs) - output_new = model_new(*inputs) - - # move to CPU - torch.cuda.synchronize() - output = output.cpu() - output_new = output_new.cpu() - - # check correctness - assert output.shape == output_new.shape - assert torch.allclose(output, output_new, atol=1e-02) - - return "PASS" - - -def run(random_seed=42): - - # run both models and check correctness - check_correctness() - - return "PASS" - - -if __name__ == "__main__": - print(run()) diff --git a/src/unit_tests/test_dataset.py b/src/unit_tests/test_dataset.py deleted file mode 100644 index a23768e0..00000000 --- a/src/unit_tests/test_dataset.py +++ /dev/null @@ -1,48 +0,0 @@ - -import pytest -from src.dataset import get_code_hash - -""" -Usage -pytest test_dataset.py -""" - - -def test_get_code_hash(): - """ - Test collision and equivalence checking - """ - - code_snippet_batch_1_v1 = """ - import torch - # This is for a single batch - ''' - Some random multi-line comment - ''' - B = 1 - """ - - code_snippet_batch_1_v2 = """ - import torch - ''' - More problem descriptions (updated) - ''' - # low batch setting - - B = 1 - """ - - code_snippet_batch_64 = """ - import torch - # This is for a single batch - ''' - Some random multi-line comment - ''' - B = 64 - """ - - assert get_code_hash(code_snippet_batch_1_v1) == get_code_hash(code_snippet_batch_1_v2), \ - "Hash should be equal for semantically equivalent code with different comments" - - assert get_code_hash(code_snippet_batch_1_v1) != get_code_hash(code_snippet_batch_64), \ - "Hash should differ for code with different batch sizes" \ No newline at end of file diff --git a/src/unit_tests/test_score.py b/src/unit_tests/test_score.py deleted file mode 100644 index 918b1f12..00000000 --- a/src/unit_tests/test_score.py +++ /dev/null @@ -1,123 +0,0 @@ -import pytest -from src.score import * -import math - -''' -Usage: -pytest test_score.py -''' - -abs_tol = 0.0000001 - -def test_geometric_mean_speed_ratio_correct_only(): - - is_correct = [1,0,1,1,0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - - ''' - Geometric mean of the speed ratio for correct samples - ''' - assert math.isclose(geometric_mean_speed_ratio_correct_only(is_correct, baseline_speed, actual_speed, n), 1.185631101, abs_tol=abs_tol) - - is_correct = [1,1,1,1,0] - baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] - actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] - n = 5 - - assert math.isclose(geometric_mean_speed_ratio_correct_only(is_correct, baseline_speed, actual_speed, n), 0.801816719, abs_tol=abs_tol) - - is_correct = [0,0,0,0,0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - - assert math.isclose(geometric_mean_speed_ratio_correct_only(is_correct, baseline_speed, actual_speed, n), 0, abs_tol=abs_tol) - - -def test_geometric_mean_speed_ratio_correct_and_faster_only(): - - is_correct = [1,0,1,1,0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - - ''' - Geometric mean of the speed ratio for correct samples that have speedup > 1 - ''' - - assert math.isclose(geometric_mean_speed_ratio_correct_and_faster_only(is_correct, baseline_speed, actual_speed, n), 5, abs_tol=abs_tol) - - is_correct = [1,1,1,1,0] - baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] - actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] - n = 5 - - assert math.isclose(geometric_mean_speed_ratio_correct_and_faster_only(is_correct, baseline_speed, actual_speed, n), 1.033333333, abs_tol=abs_tol) - - is_correct = [0,0,0,0,0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - - assert math.isclose(geometric_mean_speed_ratio_correct_and_faster_only(is_correct, baseline_speed, actual_speed, n), 0, abs_tol=abs_tol) - -def test_fastp(): - - ''' - Rate of samples within a threshold p (1.0) - ''' - - is_correct = [1,0,1,1,0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - p = 1.0 - - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0.2, abs_tol=abs_tol) - - is_correct = [1,1,1,1,0] - baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] - actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] - n = 5 - p = 1.0 - - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0.2, abs_tol=abs_tol) - - is_correct = [0,0,0,0,0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - p = 1.0 - - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0, abs_tol=abs_tol) - - ''' - Rate of samples within a threshold p (0.5) - ''' - - is_correct = [1,0,1,1,0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - p = 0.5 - - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0.4, abs_tol=abs_tol) - - is_correct = [1,1,1,1,0] - baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] - actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] - n = 5 - p = 0.5 - - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0.6, abs_tol=abs_tol) - - is_correct = [0,0,0,0,0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - p = 0.5 - - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0, abs_tol=abs_tol) - diff --git a/src/unit_tests/test_utils.py b/src/unit_tests/test_utils.py deleted file mode 100644 index 9b569dd5..00000000 --- a/src/unit_tests/test_utils.py +++ /dev/null @@ -1,110 +0,0 @@ -import pytest # noqa - -from src.utils import extract_first_code, extract_code_blocks, extract_last_code - -def check_code_assertions(code: str, expected_code: str): - """ - Check code is equivalent (don't worry about whitespace) - """ - if code is None: - assert expected_code == "" - else: - assert code.replace("\n", "").replace(" ", "") == expected_code.replace("\n", "").replace(" ", "") - - -def test_extract_last_code(): - # Test with Python code block - example_output = """The LLM wrote some code here - ```python - def hello(): - print("Hello") - ``` - and it says more stuff afterwards""" - code = extract_last_code(example_output, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") - - - example_output = """The LLM wrote some code here - ```cpp - int main() { - return 0; - } - ``` - - and some other code block - ```python - def hello(): - print("Hello") - ``` - and it says more stuff afterwards""" - code = extract_last_code(example_output, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") - - - -def test_extract_first_code(): - # Test with Python code block - example_output = """The LLM wrote some code here - ```python - def hello(): - print("Hello") - ``` - and it says more stuff afterwards""" - - code = extract_first_code(example_output, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") - - # Test with no code block - text = "Some code here" - code = extract_first_code(text, ["python", "cpp"]) - check_code_assertions(code, "") - - # Test with empty code block - text = "```python\n```" - code = extract_first_code(text, ["python", "cpp"]) - check_code_assertions(code, "") - - - # Test with multiple code blocks - text = """```python - def hello(): - print("Hello") - ``` - - ```cpp - int main() { - return 0; - } - ``` - """ - # NOTE: is this a problem - code = extract_first_code(text, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") -# Test python hash - - - -def test_extract_code_blocks(): - text = """```python - def hello(): - print("Hello") - ``` - """ - code = extract_code_blocks(text, ["python", "rust"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") - - text = """```python - def hello(): - print("Hello") - ``` - - ```cpp - int main() { - return 0; - } - ``` - """ - # NOTE: is this a problem - code = extract_code_blocks(text, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\") \n int main() { \n return 0; \n }") - diff --git a/tests/test_analysis.py b/tests/test_analysis.py new file mode 100644 index 00000000..20d142ba --- /dev/null +++ b/tests/test_analysis.py @@ -0,0 +1,61 @@ +import pytest +import numpy as np +from kernelbench.analysis import pass_at_k, extract_all_cuda_sources + +""" +Usage: +pytest test_analysis.py +""" + + +def test_pass_at_k(): + """Test the pass@k metric calculation""" + # Common use cases + assert pass_at_k(10, 5, 1) == 0.5 # 5/10 correct = 50% pass@1 + assert pass_at_k(10, 0, 5) == 0.0 # None correct = 0% + assert pass_at_k(10, 10, 1) == 1.0 # All correct = 100% + + # Pass@k should be higher for larger k values + # (more chances to pass when drawing more samples) + assert pass_at_k(10, 5, 5) > pass_at_k(10, 5, 1) + + +def test_extract_all_cuda_sources(): + """Test extraction of CUDA code from triple-quoted strings""" + # Test with a single CUDA kernel + code_single = ''' + kernel = """ + __global__ void add(int *a, int *b, int *c) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + c[i] = a[i] + b[i]; + } + """ + ''' + extracted = extract_all_cuda_sources(code_single) + assert len(extracted) == 1 + assert "__global__" in extracted[0] + assert "c[i] = a[i] + b[i]" in extracted[0] + + # Test with multiple CUDA kernels + code_multiple = ''' + kernel1 = """ + __global__ void add(int *a, int *b, int *c) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + c[i] = a[i] + b[i]; + } + """ + + kernel2 = """ + __global__ void multiply(int *a, int *b, int *c) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + c[i] = a[i] * b[i]; + } + """ + ''' + extracted = extract_all_cuda_sources(code_multiple) + assert len(extracted) == 2 + assert "add" in extracted[0] + assert "multiply" in extracted[1] + + # Test with no CUDA code + assert len(extract_all_cuda_sources("def python_function(): pass")) == 0 diff --git a/tests/test_compile.py b/tests/test_compile.py new file mode 100644 index 00000000..ed16f47b --- /dev/null +++ b/tests/test_compile.py @@ -0,0 +1,64 @@ +import pytest +from unittest.mock import patch, MagicMock + +""" +Usage: +pytest test_compile.py +""" + + +@patch("torch.cuda.is_available", return_value=False) +def test_cuda_detection(mock_cuda_available): + """Test CUDA availability detection""" + import torch + + # Should detect that CUDA is not available + assert torch.cuda.is_available() == False + mock_cuda_available.assert_called_once() + + +@patch("kernelbench.compile.compile_and_benchmark_kernel") +def test_compile_code(mock_compile_benchmark): + """Test the code compilation handling""" + from kernelbench.compile import compile_code + + # Mock successful compilation + mock_compile_benchmark.return_value = { + "status": "success", + "compile_time": 0.5, + "benchmark_results": {"mean": 1.2, "std": 0.1, "min": 1.0, "max": 1.5}, + } + + # Test with valid kernel + valid_code = ''' + valid_kernel = """ + __global__ void add(int *a, int *b, int *c) { + int tid = blockIdx.x * blockDim.x + threadIdx.x; + c[tid] = a[tid] + b[tid]; + } + """ + ''' + + result = compile_code(valid_code) + assert result["status"] == "success" + assert "benchmark_results" in result + + # Test handling of invalid code + mock_compile_benchmark.side_effect = Exception("Compilation error") + result = compile_code("invalid code", skip_on_error=True) + assert result["status"] == "skipped" + assert "error" in result + + +@patch("kernelbench.compile.torch.cuda.is_available", return_value=True) +def test_data_type_mappings(mock_cuda_available): + """Test the data type mapping functionality""" + from kernelbench.compile import get_data_type_mappings + + # Check that we get back a valid mapping dictionary + dtype_map = get_data_type_mappings() + assert isinstance(dtype_map, dict) + + # Should contain common CUDA types + assert "float32" in dtype_map + assert "int32" in dtype_map diff --git a/tests/test_dataset.py b/tests/test_dataset.py new file mode 100644 index 00000000..8a8d1fae --- /dev/null +++ b/tests/test_dataset.py @@ -0,0 +1,61 @@ +import pytest +from kernelbench.dataset import get_code_hash + +""" +Usage +pytest test_dataset.py +""" + + +def test_get_code_hash(): + """Test the code hashing functionality""" + + # Test semantic equivalence (different comments, whitespace, but same code) + code1 = """ + import torch + # This is for a single batch + ''' + Some random multi-line comment + ''' + B = 1 + """ + + code2 = """ + import torch + ''' + More problem descriptions (updated) + ''' + # low batch setting + + B = 1 + """ + + code3 = "import torch\nB = 1" + + # All three versions should hash to the same value + assert get_code_hash(code1) == get_code_hash(code2) == get_code_hash(code3) + + # Test that meaningful code changes cause different hashes + code_different = """ + import torch + B = 64 # Different batch size + """ + + assert get_code_hash(code1) != get_code_hash(code_different) + + # Test case sensitivity + code_case1 = "def test(): pass" + code_case2 = "def TEST(): pass" + + assert get_code_hash(code_case1) != get_code_hash(code_case2) + + # Test stability (multiple calls should return the same hash) + complex_code = """ + import torch + def complex_function(x, y): + return torch.matmul(x, y) + """ + + hash1 = get_code_hash(complex_code) + hash2 = get_code_hash(complex_code) + assert hash1 == hash2 diff --git a/tests/test_score.py b/tests/test_score.py new file mode 100644 index 00000000..88e902ab --- /dev/null +++ b/tests/test_score.py @@ -0,0 +1,77 @@ +import pytest +from kernelbench.score import * +import math + +""" +Usage: +pytest test_score.py +""" + +abs_tol = 0.0000001 + + +def test_geometric_mean_speed_ratio(): + """Test geometric mean calculations with representative test cases""" + + # Test case with mixed correct/incorrect results + is_correct = [1, 0, 1, 1, 0] + baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] + actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] + n = 5 + + # Test correct-only metric + assert math.isclose( + geometric_mean_speed_ratio_correct_only( + is_correct, baseline_speed, actual_speed, n + ), + 1.185631101, + abs_tol=abs_tol, + ) + + # Test correct-and-faster-only metric + assert math.isclose( + geometric_mean_speed_ratio_correct_and_faster_only( + is_correct, baseline_speed, actual_speed, n + ), + 5, + abs_tol=abs_tol, + ) + + # Test edge case: no correct samples + is_correct_none = [0, 0, 0, 0, 0] + assert ( + geometric_mean_speed_ratio_correct_only( + is_correct_none, baseline_speed, actual_speed, n + ) + == 0 + ) + + assert ( + geometric_mean_speed_ratio_correct_and_faster_only( + is_correct_none, baseline_speed, actual_speed, n + ) + == 0 + ) + + +def test_fastp(): + """Test fastp metric with different thresholds""" + + is_correct = [1, 0, 1, 1, 0] + baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] + actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] + n = 5 + + # Test with threshold p=1.0 (strict speedup) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, 1.0), 0.2, abs_tol=abs_tol + ) + + # Test with threshold p=0.5 (allowing more tolerance) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, 0.5), 0.4, abs_tol=abs_tol + ) + + # Edge case: no correct samples + is_correct_none = [0, 0, 0, 0, 0] + assert fastp(is_correct_none, baseline_speed, actual_speed, n, 1.0) == 0 diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..4e26ddc3 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,169 @@ +import pytest +import os +import tempfile +from unittest.mock import patch +from kernelbench.utils import ( + read_file, + set_gpu_arch, + remove_code_block_header, + maybe_multithread, + extract_first_code, + extract_code_blocks, + extract_last_code, + extract_python_code, +) + +""" +Usage: +pytest test_utils.py +""" + + +def test_read_file(): + """Test the read_file function""" + # Create a temporary file for testing + with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file: + temp_file.write("Test content") + temp_file_path = temp_file.name + + try: + # Test reading an existing file + content = read_file(temp_file_path) + assert content == "Test content" + + # Test reading a non-existent file + assert read_file(temp_file_path + "_nonexistent") == "" + finally: + # Clean up + if os.path.exists(temp_file_path): + os.unlink(temp_file_path) + + +def test_set_gpu_arch(): + """Test GPU architecture setting""" + # Test valid architectures + set_gpu_arch(["Volta", "Ampere"]) + assert os.environ["TORCH_CUDA_ARCH_LIST"] == "Volta;Ampere" + + # Test invalid architecture + with pytest.raises(ValueError): + set_gpu_arch(["InvalidArch"]) + + +def test_remove_code_block_header(): + """Test code block header removal""" + # Test with language header + assert ( + remove_code_block_header("python\ndef hello(): pass", "python") + == "def hello(): pass" + ) + + # Test with no header + assert ( + remove_code_block_header("def hello(): pass", "python") == "def hello(): pass" + ) + + # Test with different header + code = "cpp\nint main() {}" + assert remove_code_block_header(code, "python") == code # Should not change + assert remove_code_block_header(code, "cpp") == "int main() {}" + + +def test_maybe_multithread(): + """Test the multithreading utility""" + + # Define a simple test function + def test_func(x, multiplier=2): + return x * multiplier + + # Test with single thread + results = maybe_multithread(test_func, [1, 2, 3], num_workers=1, multiplier=3) + assert results == [3, 6, 9] + + # Test filtering behavior + def filter_func(x): + return x if x > 2 else None + + results = maybe_multithread(filter_func, [1, 2, 3, 4], num_workers=1) + assert results == [3, 4] + + +def test_code_extraction(): + """Test the code extraction utilities""" + + # Test input with code blocks + example = """Here's some code: + ```python + def hello(): + print("Hello") + ``` + + And another block: + ```cpp + int main() { + return 0; + } + ``` + """ + + # Test extract_first_code - should get the Python block + first_code = extract_first_code(example, ["python", "cpp"]) + assert "def hello()" in first_code + assert 'print("Hello")' in first_code + + # Test extract_last_code - should get the C++ block + last_code = extract_last_code(example, ["python", "cpp"]) + assert "int main()" in last_code + assert "return 0" in last_code + + # Test extract_code_blocks - should get both blocks + all_code = extract_code_blocks(example, ["python", "cpp"]) + assert "def hello()" in all_code + assert "int main()" in all_code + + # Test with no code blocks + no_code = "This is text with no code blocks" + assert extract_first_code(no_code, ["python"]) is None + assert extract_last_code(no_code, ["python"]) is None + assert extract_code_blocks(no_code, ["python"]) == "" + + # Test with empty code block + empty_block = "```python\n```" + assert extract_first_code(empty_block, ["python"]) == "" + + +def test_extract_python_code(): + """Test extracting Python code specifically""" + # Input with Python code block + text = """Here's some Python code: + ```python + def add(a, b): + return a + b + ``` + """ + + # Should extract the Python code + code = extract_python_code(text) + assert "def add(a, b):" in code + assert "return a + b" in code + + # Multiple Python blocks + text_multiple = """ + ```python + def add(a, b): + return a + b + ``` + + And another: + ```python + def multiply(a, b): + return a * b + ``` + """ + + code = extract_python_code(text_multiple) + assert "def add" in code + assert "def multiply" in code + + # No Python code + assert extract_python_code("No code here") == "" diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..53d54bc7 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2216 @@ +version = 1 +revision = 1 +requires-python = "==3.10.*" +resolution-markers = [ + "sys_platform == 'linux'", + "sys_platform != 'linux'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, +] + +[[package]] +name = "aiohttp" +version = "3.11.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/96/91e93ae5fd04d428c101cdbabce6c820d284d61d2614d00518f4fa52ea24/aiohttp-3.11.14.tar.gz", hash = "sha256:d6edc538c7480fa0a3b2bdd705f8010062d74700198da55d16498e1b49549b9c", size = 7676994 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/e1/f1ccc6cf29a31fb33e4eaa07a9d8e4dff00e23b32423b679cdb89536fe71/aiohttp-3.11.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e2bc827c01f75803de77b134afdbf74fa74b62970eafdf190f3244931d7a5c0d", size = 709390 }, + { url = "https://files.pythonhosted.org/packages/80/7d/195965f183a724d0470560b097543e96dc4a672fc2714012d1be87d6775c/aiohttp-3.11.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e365034c5cf6cf74f57420b57682ea79e19eb29033399dd3f40de4d0171998fa", size = 469246 }, + { url = "https://files.pythonhosted.org/packages/46/02/3a4f05e966c2edeace5103f40d296ba0159cee633ab0f162fbea579653e3/aiohttp-3.11.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c32593ead1a8c6aabd58f9d7ee706e48beac796bb0cb71d6b60f2c1056f0a65f", size = 456384 }, + { url = "https://files.pythonhosted.org/packages/68/a6/c96cd5452af267fdda1cf46accc356d1295fb14da4a7a0e081567ea297af/aiohttp-3.11.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4e7c7ec4146a94a307ca4f112802a8e26d969018fabed526efc340d21d3e7d0", size = 1589803 }, + { url = "https://files.pythonhosted.org/packages/7f/f4/e50ef78483485bcdae9cf29c9144af2b42457e18175a6ace7c560d89325e/aiohttp-3.11.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8b2df9feac55043759aa89f722a967d977d80f8b5865a4153fc41c93b957efc", size = 1632525 }, + { url = "https://files.pythonhosted.org/packages/8b/92/b6bd4b89304eee827cf07a40b98af171342cddfa1f8b02b55cd0485b9d4f/aiohttp-3.11.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7571f99525c76a6280f5fe8e194eeb8cb4da55586c3c61c59c33a33f10cfce7", size = 1666839 }, + { url = "https://files.pythonhosted.org/packages/c7/21/f3230a9f78bb4a4c4462040bf8425ebb673e3773dd17fd9d06d1af43a955/aiohttp-3.11.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b59d096b5537ec7c85954cb97d821aae35cfccce3357a2cafe85660cc6295628", size = 1590572 }, + { url = "https://files.pythonhosted.org/packages/8e/12/e4fd2616950a39425b739476c3eccc820061ea5f892815566d27282e7825/aiohttp-3.11.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b42dbd097abb44b3f1156b4bf978ec5853840802d6eee2784857be11ee82c6a0", size = 1543380 }, + { url = "https://files.pythonhosted.org/packages/6a/7c/3f82c2fdcca53cc8732fa342abbe0372bbbd8af3162d6629ac0a7dc8b281/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b05774864c87210c531b48dfeb2f7659407c2dda8643104fb4ae5e2c311d12d9", size = 1530160 }, + { url = "https://files.pythonhosted.org/packages/aa/3e/60af2d40f78612062788c2bf6be38738f9525750d3a7678d31f950047536/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4e2e8ef37d4bc110917d038807ee3af82700a93ab2ba5687afae5271b8bc50ff", size = 1558543 }, + { url = "https://files.pythonhosted.org/packages/08/71/93e11c4ef9a72f5f26d7e9f92294707437fae8de49c2019ed713dea7625b/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e9faafa74dbb906b2b6f3eb9942352e9e9db8d583ffed4be618a89bd71a4e914", size = 1536286 }, + { url = "https://files.pythonhosted.org/packages/da/4b/77b170ae7eb9859d80b9648a7439991425663f66422f3ef0b27f29bde9d0/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7e7abe865504f41b10777ac162c727af14e9f4db9262e3ed8254179053f63e6d", size = 1608387 }, + { url = "https://files.pythonhosted.org/packages/02/0b/5fcad20243799e9a3f326140d3d767884449e293fb5d8fca10f83001787c/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4848ae31ad44330b30f16c71e4f586cd5402a846b11264c412de99fa768f00f3", size = 1629633 }, + { url = "https://files.pythonhosted.org/packages/3f/e3/bb454add253f939c7331794b2619c156ef5a108403000221ff2dc01f9072/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d0b46abee5b5737cb479cc9139b29f010a37b1875ee56d142aefc10686a390b", size = 1565329 }, + { url = "https://files.pythonhosted.org/packages/6f/08/6b061de352a614461a4a19e60a87e578fe28e1d3fca38315484a17ff484f/aiohttp-3.11.14-cp310-cp310-win32.whl", hash = "sha256:a0d2c04a623ab83963576548ce098baf711a18e2c32c542b62322a0b4584b990", size = 417394 }, + { url = "https://files.pythonhosted.org/packages/91/f7/533384607d35a8c7a9dbe4497cee7899aa7c3b29c14cd83373c0f415bdcf/aiohttp-3.11.14-cp310-cp310-win_amd64.whl", hash = "sha256:5409a59d5057f2386bb8b8f8bbcfb6e15505cedd8b2445db510563b5d7ea1186", size = 442856 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anthropic" +version = "0.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tokenizers" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/a6/10efc0ca36712673a11ac90095bdb84a299cd6f591d5111bfa9acbb2e76e/anthropic-0.34.2.tar.gz", hash = "sha256:808ea19276f26646bfde9ee535669735519376e4eeb301a2974fc69892be1d6e", size = 902318 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/6d/c739c11fb3838cda8d4052d0ab3462b2b7d2499a726a7869e5d3e228cb74/anthropic-0.34.2-py3-none-any.whl", hash = "sha256:f50a628eb71e2c76858b106c8cbea278c45c6bd2077cb3aff716a112abddc9fc", size = 891945 }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup" }, + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, +] + +[[package]] +name = "archon-ai" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anthropic" }, + { name = "google-generativeai" }, + { name = "groq" }, + { name = "litellm" }, + { name = "loguru" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "tiktoken" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/e2/7f084ae2109d7e5eb1e25028f03e609dd9891b43f19ed15d0d237f04da4e/archon_ai-0.1.4.tar.gz", hash = "sha256:d00b160a32bf51b58d4ffeb43562ae03f8e32a3c23c46d9cd821f221a4ae72f4", size = 27745 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/c3/0b989c86b4f408a602993ba5503692784be17ec61a6606c9384ce23a2db1/archon_ai-0.1.4-py3-none-any.whl", hash = "sha256:04942caa758d0fbd4aab6a817a9b272b1c7f6e687ede490d634147c5fb3c352a", size = 32322 }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, +] + +[[package]] +name = "backoff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, +] + +[[package]] +name = "cachetools" +version = "5.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080 }, +] + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, + { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, + { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, + { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, + { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, + { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, + { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, + { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, + { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, + { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, + { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, + { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, + { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "datasets" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/dd/6afb1d440a21da104adefb93269a02058bf9f3cf665db6421fe04eceab7d/datasets-3.5.0.tar.gz", hash = "sha256:9e39560e34f83a64e48ceca7adeb645ede3c3055c5cf48ed2b454f8ed2b89754", size = 568325 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/83/50abe521eb75744a01efe2ebe836a4b61f4df37941a776f650f291aabdf9/datasets-3.5.0-py3-none-any.whl", hash = "sha256:b3b7f163acc6ac4e01a1b00eef26d48bd4039288ceea3601d169272bd5581006", size = 491167 }, +] + +[[package]] +name = "dill" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, +] + +[[package]] +name = "diskcache" +version = "5.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + +[[package]] +name = "docker-pycreds" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/e6/d1f6c00b7221e2d7c4b470132c931325c8b22c51ca62417e300f5ce16009/docker-pycreds-0.4.0.tar.gz", hash = "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4", size = 8754 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/e8/f6bd1eee09314e7e6dee49cbe2c5e22314ccdb38db16c9fc72d2fa80d054/docker_pycreds-0.4.0-py2.py3-none-any.whl", hash = "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49", size = 8982 }, +] + +[[package]] +name = "einops" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, +] + +[[package]] +name = "emoji" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/7d/01cddcbb6f5cc0ba72e00ddf9b1fa206c802d557fd0a20b18e130edf1336/emoji-2.14.1.tar.gz", hash = "sha256:f8c50043d79a2c1410ebfae833ae1868d5941a67a6cd4d18377e2eb0bd79346b", size = 597182 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/db/a0335710caaa6d0aebdaa65ad4df789c15d89b7babd9a30277838a7d9aac/emoji-2.14.1-py3-none-any.whl", hash = "sha256:35a8a486c1460addb1499e3bf7929d3889b2e2841a57401903699fef595e942b", size = 590617 }, +] + +[[package]] +name = "eval-type-backport" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/ea/8b0ac4469d4c347c6a385ff09dc3c048c2d021696664e26c7ee6791631b5/eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1", size = 9079 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/31/55cd413eaccd39125368be33c46de24a1f639f2e12349b0361b4678f3915/eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a", size = 5830 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "fastapi" +version = "0.115.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164 }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, +] + +[[package]] +name = "frozenlist" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/79/29d44c4af36b2b240725dce566b20f63f9b36ef267aaaa64ee7466f4f2f8/frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a", size = 94451 }, + { url = "https://files.pythonhosted.org/packages/47/47/0c999aeace6ead8a44441b4f4173e2261b18219e4ad1fe9a479871ca02fc/frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb", size = 54301 }, + { url = "https://files.pythonhosted.org/packages/8d/60/107a38c1e54176d12e06e9d4b5d755b677d71d1219217cee063911b1384f/frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec", size = 52213 }, + { url = "https://files.pythonhosted.org/packages/17/62/594a6829ac5679c25755362a9dc93486a8a45241394564309641425d3ff6/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5", size = 240946 }, + { url = "https://files.pythonhosted.org/packages/7e/75/6c8419d8f92c80dd0ee3f63bdde2702ce6398b0ac8410ff459f9b6f2f9cb/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76", size = 264608 }, + { url = "https://files.pythonhosted.org/packages/88/3e/82a6f0b84bc6fb7e0be240e52863c6d4ab6098cd62e4f5b972cd31e002e8/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17", size = 261361 }, + { url = "https://files.pythonhosted.org/packages/fd/85/14e5f9ccac1b64ff2f10c927b3ffdf88772aea875882406f9ba0cec8ad84/frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba", size = 231649 }, + { url = "https://files.pythonhosted.org/packages/ee/59/928322800306f6529d1852323014ee9008551e9bb027cc38d276cbc0b0e7/frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d", size = 241853 }, + { url = "https://files.pythonhosted.org/packages/7d/bd/e01fa4f146a6f6c18c5d34cab8abdc4013774a26c4ff851128cd1bd3008e/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2", size = 243652 }, + { url = "https://files.pythonhosted.org/packages/a5/bd/e4771fd18a8ec6757033f0fa903e447aecc3fbba54e3630397b61596acf0/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f", size = 241734 }, + { url = "https://files.pythonhosted.org/packages/21/13/c83821fa5544af4f60c5d3a65d054af3213c26b14d3f5f48e43e5fb48556/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c", size = 260959 }, + { url = "https://files.pythonhosted.org/packages/71/f3/1f91c9a9bf7ed0e8edcf52698d23f3c211d8d00291a53c9f115ceb977ab1/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab", size = 262706 }, + { url = "https://files.pythonhosted.org/packages/4c/22/4a256fdf5d9bcb3ae32622c796ee5ff9451b3a13a68cfe3f68e2c95588ce/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5", size = 250401 }, + { url = "https://files.pythonhosted.org/packages/af/89/c48ebe1f7991bd2be6d5f4ed202d94960c01b3017a03d6954dd5fa9ea1e8/frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb", size = 45498 }, + { url = "https://files.pythonhosted.org/packages/28/2f/cc27d5f43e023d21fe5c19538e08894db3d7e081cbf582ad5ed366c24446/frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4", size = 51622 }, + { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, +] + +[[package]] +name = "fsspec" +version = "2024.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/11/de70dee31455c546fbc88301971ec03c328f3d1138cfba14263f651e9551/fsspec-2024.12.0.tar.gz", hash = "sha256:670700c977ed2fb51e0d9f9253177ed20cbde4a3e5c0283cc5385b5870c8533f", size = 291600 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/86/5486b0188d08aa643e127774a99bac51ffa6cf343e3deb0583956dca5b22/fsspec-2024.12.0-py3-none-any.whl", hash = "sha256:b520aed47ad9804237ff878b504267a3b0b441e97508bd6d2d8774e3db85cee2", size = 183862 }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, +] + +[[package]] +name = "google-ai-generativelanguage" +version = "0.6.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-auth" }, + { name = "proto-plus" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/d1/48fe5d7a43d278e9f6b5ada810b0a3530bbeac7ed7fcbcd366f932f05316/google_ai_generativelanguage-0.6.15.tar.gz", hash = "sha256:8f6d9dc4c12b065fe2d0289026171acea5183ebf2d0b11cefe12f3821e159ec3", size = 1375443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/a3/67b8a6ff5001a1d8864922f2d6488dc2a14367ceb651bc3f09a947f2f306/google_ai_generativelanguage-0.6.15-py3-none-any.whl", hash = "sha256:5a03ef86377aa184ffef3662ca28f19eeee158733e45d7947982eb953c6ebb6c", size = 1327356 }, +] + +[[package]] +name = "google-api-core" +version = "2.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/5c/085bcb872556934bb119e5e09de54daa07873f6866b8f0303c49e72287f7/google_api_core-2.24.2.tar.gz", hash = "sha256:81718493daf06d96d6bc76a91c23874dbf2fac0adbbf542831b805ee6e974696", size = 163516 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/95/f472d85adab6e538da2025dfca9e976a0d125cc0af2301f190e77b76e51c/google_api_core-2.24.2-py3-none-any.whl", hash = "sha256:810a63ac95f3c441b7c0e43d344e372887f62ce9071ba972eacf32672e072de9", size = 160061 }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio" }, + { name = "grpcio-status" }, +] + +[[package]] +name = "google-api-python-client" +version = "2.166.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, + { name = "google-auth-httplib2" }, + { name = "httplib2" }, + { name = "uritemplate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/c9/eac7b4e843039f0a54a563c2328d43de6f02e426a11b6a7e378996f667db/google_api_python_client-2.166.0.tar.gz", hash = "sha256:b8cf843bd9d736c134aef76cf1dc7a47c9283a2ef24267b97207b9dd43b30ef7", size = 12680525 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/44/ae1528a6ca296d89704c8febb72b3e263c28b4e50ab29b9202df7a0f273d/google_api_python_client-2.166.0-py2.py3-none-any.whl", hash = "sha256:dd8cc74d9fc18538ab05cbd2e93cb4f82382f910c5f6945db06c91f1deae6e45", size = 13190078 }, +] + +[[package]] +name = "google-auth" +version = "2.38.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/eb/d504ba1daf190af6b204a9d4714d457462b486043744901a6eeea711f913/google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4", size = 270866 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/47/603554949a37bca5b7f894d51896a9c534b9eab808e2520a748e081669d0/google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a", size = 210770 }, +] + +[[package]] +name = "google-auth-httplib2" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "httplib2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05", size = 10842 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d", size = 9253 }, +] + +[[package]] +name = "google-generativeai" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-ai-generativelanguage" }, + { name = "google-api-core" }, + { name = "google-api-python-client" }, + { name = "google-auth" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/b0/6c6af327a8a6ef3be6fe79be1d6f1e2914d6c363aa6b081b93396f4460a7/google_generativeai-0.8.4-py3-none-any.whl", hash = "sha256:e987b33ea6decde1e69191ddcaec6ef974458864d243de7191db50c21a7c5b82", size = 175409 }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.69.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/d7/ee9d56af4e6dbe958562b5020f46263c8a4628e7952070241fc0e9b182ae/googleapis_common_protos-1.69.2.tar.gz", hash = "sha256:3e1b904a27a33c821b4b749fd31d334c0c9c30e6113023d495e48979a3dc9c5f", size = 144496 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/53/d35476d547a286506f0a6a634ccf1e5d288fffd53d48f0bd5fef61d68684/googleapis_common_protos-1.69.2-py3-none-any.whl", hash = "sha256:0b30452ff9c7a27d80bfc5718954063e8ab53dd3697093d3bc99581f5fd24212", size = 293215 }, +] + +[[package]] +name = "gql" +version = "3.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "backoff" }, + { name = "graphql-core" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/ef/5298d9d628b6a54b3b810052cb5a935d324fe28d9bfdeb741733d5c2446b/gql-3.5.2.tar.gz", hash = "sha256:07e1325b820c8ba9478e95de27ce9f23250486e7e79113dbb7659a442dc13e74", size = 180502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/71/b028b937992056e721bbf0371e13819fcca0dacde7b3c821f775ed903917/gql-3.5.2-py2.py3-none-any.whl", hash = "sha256:c830ffc38b3997b2a146317b27758305ab3d0da3bde607b49f34e32affb23ba2", size = 74346 }, +] + +[package.optional-dependencies] +aiohttp = [ + { name = "aiohttp" }, +] +requests = [ + { name = "requests" }, + { name = "requests-toolbelt" }, +] + +[[package]] +name = "graphql-core" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/9e/aa527fb09a9d7399d5d7d2aa2da490e4580707652d3b4fc156996ae88a5b/graphql-core-3.2.4.tar.gz", hash = "sha256:acbe2e800980d0e39b4685dd058c2f4042660b89ebca38af83020fd872ff1264", size = 504611 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/cc72c4c658c6316f188a60bc4e5a91cd4ceaaa8c3e7e691ac9297e4e72c7/graphql_core-3.2.4-py3-none-any.whl", hash = "sha256:1604f2042edc5f3114f49cac9d77e25863be51b23a54a61a23245cf32f6476f0", size = 203179 }, +] + +[[package]] +name = "groq" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/4d/14b9a0c27695b2aa3bcd7f397a9c6d5aa84165d0bed4e4ca3f08fe59a546/groq-0.11.0.tar.gz", hash = "sha256:dbb9aefedf388ddd4801ec7bf3eba7f5edb67948fec0cd2829d97244059f42a7", size = 104986 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/c3/8bf3cd2987e262f9fd45024313dae1d009edaa7a5b429566edfdaedaa024/groq-0.11.0-py3-none-any.whl", hash = "sha256:e328531c979542e563668c62260aec13b43a6ee0ca9e2fb22dff1d26f8c8ce54", size = 106518 }, +] + +[[package]] +name = "grpcio" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/95/aa11fc09a85d91fbc7dd405dcb2a1e0256989d67bf89fa65ae24b3ba105a/grpcio-1.71.0.tar.gz", hash = "sha256:2b85f7820475ad3edec209d3d89a7909ada16caab05d3f2e08a7e8ae3200a55c", size = 12549828 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/c5/ef610b3f988cc0cc67b765f72b8e2db06a1db14e65acb5ae7810a6b7042e/grpcio-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:c200cb6f2393468142eb50ab19613229dcc7829b5ccee8b658a36005f6669fdd", size = 5210643 }, + { url = "https://files.pythonhosted.org/packages/bf/de/c84293c961622df302c0d5d07ec6e2d4cd3874ea42f602be2df09c4ad44f/grpcio-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:b2266862c5ad664a380fbbcdbdb8289d71464c42a8c29053820ee78ba0119e5d", size = 11308962 }, + { url = "https://files.pythonhosted.org/packages/7c/38/04c9e0dc8c904570c80faa1f1349b190b63e45d6b2782ec8567b050efa9d/grpcio-1.71.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:0ab8b2864396663a5b0b0d6d79495657ae85fa37dcb6498a2669d067c65c11ea", size = 5699236 }, + { url = "https://files.pythonhosted.org/packages/95/96/e7be331d1298fa605ea7c9ceafc931490edd3d5b33c4f695f1a0667f3491/grpcio-1.71.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c30f393f9d5ff00a71bb56de4aa75b8fe91b161aeb61d39528db6b768d7eac69", size = 6339767 }, + { url = "https://files.pythonhosted.org/packages/5d/b7/7e7b7bb6bb18baf156fd4f2f5b254150dcdd6cbf0def1ee427a2fb2bfc4d/grpcio-1.71.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f250ff44843d9a0615e350c77f890082102a0318d66a99540f54769c8766ab73", size = 5943028 }, + { url = "https://files.pythonhosted.org/packages/13/aa/5fb756175995aeb47238d706530772d9a7ac8e73bcca1b47dc145d02c95f/grpcio-1.71.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6d8de076528f7c43a2f576bc311799f89d795aa6c9b637377cc2b1616473804", size = 6031841 }, + { url = "https://files.pythonhosted.org/packages/54/93/172783e01eed61f7f180617b7fa4470f504e383e32af2587f664576a7101/grpcio-1.71.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9b91879d6da1605811ebc60d21ab6a7e4bae6c35f6b63a061d61eb818c8168f6", size = 6651039 }, + { url = "https://files.pythonhosted.org/packages/6f/99/62654b220a27ed46d3313252214f4bc66261143dc9b58004085cd0646753/grpcio-1.71.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f71574afdf944e6652203cd1badcda195b2a27d9c83e6d88dc1ce3cfb73b31a5", size = 6198465 }, + { url = "https://files.pythonhosted.org/packages/68/35/96116de833b330abe4412cc94edc68f99ed2fa3e39d8713ff307b3799e81/grpcio-1.71.0-cp310-cp310-win32.whl", hash = "sha256:8997d6785e93308f277884ee6899ba63baafa0dfb4729748200fcc537858a509", size = 3620382 }, + { url = "https://files.pythonhosted.org/packages/b7/09/f32ef637e386f3f2c02effac49699229fa560ce9007682d24e9e212d2eb4/grpcio-1.71.0-cp310-cp310-win_amd64.whl", hash = "sha256:7d6ac9481d9d0d129224f6d5934d5832c4b1cddb96b59e7eba8416868909786a", size = 4280302 }, +] + +[[package]] +name = "grpcio-status" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/53/a911467bece076020456401f55a27415d2d70d3bc2c37af06b44ea41fc5c/grpcio_status-1.71.0.tar.gz", hash = "sha256:11405fed67b68f406b3f3c7c5ae5104a79d2d309666d10d61b152e91d28fb968", size = 13669 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/d6/31fbc43ff097d8c4c9fc3df741431b8018f67bf8dfbe6553a555f6e5f675/grpcio_status-1.71.0-py3-none-any.whl", hash = "sha256:843934ef8c09e3e858952887467f8256aac3910c55f077a359a65b2b3cde3e68", size = 14424 }, +] + +[[package]] +name = "grpclib" +version = "0.4.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h2" }, + { name = "multidict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/b9/55936e462a5925190d7427e880b3033601d1effd13809b483d13a926061a/grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3", size = 61254 } + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "h2" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 }, +] + +[[package]] +name = "hpack" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 }, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httplib2" +version = "0.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/ad/2371116b22d616c194aa25ec410c9c6c37f23599dcd590502b74db197584/httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81", size = 351116 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/6c/d2fbdaaa5959339d53ba38e94c123e4e84b8fbc4b84beb0e70d7c1608486/httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", size = 96854 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/b6a403e31a806d3b2560adf73b60881330b954d4000fdfb6b0e3beac5f90/huggingface_hub-0.30.0.tar.gz", hash = "sha256:21ac16bc79c950dc058fad6f25e6e13444d878cc504e386d3a9b83096b3851cb", size = 400791 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/54/ab179a3cbc6cb285b34dfb1686176db4c3e7d9634b23cabf5aff0d00c776/huggingface_hub-0.30.0-py3-none-any.whl", hash = "sha256:17470b2b0f902209beb0344d659a18c48b69bc202af27c1cfc814d637807322f", size = 481128 }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jiter" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/c2/e4562507f52f0af7036da125bb699602ead37a2332af0788f8e0a3417f36/jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893", size = 162604 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/82/39f7c9e67b3b0121f02a0b90d433626caa95a565c3d2449fea6bcfa3f5f5/jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad", size = 314540 }, + { url = "https://files.pythonhosted.org/packages/01/07/7bf6022c5a152fca767cf5c086bb41f7c28f70cf33ad259d023b53c0b858/jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea", size = 321065 }, + { url = "https://files.pythonhosted.org/packages/6c/b2/de3f3446ecba7c48f317568e111cc112613da36c7b29a6de45a1df365556/jiter-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1339f839b91ae30b37c409bf16ccd3dc453e8b8c3ed4bd1d6a567193651a4a51", size = 341664 }, + { url = "https://files.pythonhosted.org/packages/13/cf/6485a4012af5d407689c91296105fcdb080a3538e0658d2abf679619c72f/jiter-0.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffba79584b3b670fefae66ceb3a28822365d25b7bf811e030609a3d5b876f538", size = 364635 }, + { url = "https://files.pythonhosted.org/packages/0d/f7/4a491c568f005553240b486f8e05c82547340572d5018ef79414b4449327/jiter-0.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cfc7d0a8e899089d11f065e289cb5b2daf3d82fbe028f49b20d7b809193958d", size = 406288 }, + { url = "https://files.pythonhosted.org/packages/d3/ca/f4263ecbce7f5e6bded8f52a9f1a66540b270c300b5c9f5353d163f9ac61/jiter-0.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e00a1a2bbfaaf237e13c3d1592356eab3e9015d7efd59359ac8b51eb56390a12", size = 397499 }, + { url = "https://files.pythonhosted.org/packages/ac/a2/522039e522a10bac2f2194f50e183a49a360d5f63ebf46f6d890ef8aa3f9/jiter-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1d9870561eb26b11448854dce0ff27a9a27cb616b632468cafc938de25e9e51", size = 352926 }, + { url = "https://files.pythonhosted.org/packages/b1/67/306a5c5abc82f2e32bd47333a1c9799499c1c3a415f8dde19dbf876f00cb/jiter-0.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9872aeff3f21e437651df378cb75aeb7043e5297261222b6441a620218b58708", size = 384506 }, + { url = "https://files.pythonhosted.org/packages/0f/89/c12fe7b65a4fb74f6c0d7b5119576f1f16c79fc2953641f31b288fad8a04/jiter-0.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1fd19112d1049bdd47f17bfbb44a2c0001061312dcf0e72765bfa8abd4aa30e5", size = 520621 }, + { url = "https://files.pythonhosted.org/packages/c4/2b/d57900c5c06e6273fbaa76a19efa74dbc6e70c7427ab421bf0095dfe5d4a/jiter-0.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef5da104664e526836070e4a23b5f68dec1cc673b60bf1edb1bfbe8a55d0678", size = 512613 }, + { url = "https://files.pythonhosted.org/packages/89/05/d8b90bfb21e58097d5a4e0224f2940568366f68488a079ae77d4b2653500/jiter-0.9.0-cp310-cp310-win32.whl", hash = "sha256:cb12e6d65ebbefe5518de819f3eda53b73187b7089040b2d17f5b39001ff31c4", size = 206613 }, + { url = "https://files.pythonhosted.org/packages/2c/1d/5767f23f88e4f885090d74bbd2755518050a63040c0f59aa059947035711/jiter-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c43ca669493626d8672be3b645dbb406ef25af3f4b6384cfd306da7eb2e70322", size = 208371 }, +] + +[[package]] +name = "jsonschema" +version = "4.23.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/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, +] + +[[package]] +name = "kernelbench" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "anthropic" }, + { name = "archon-ai" }, + { name = "datasets" }, + { name = "einops" }, + { name = "google-generativeai" }, + { name = "modal" }, + { name = "ninja" }, + { name = "numpy" }, + { name = "openai" }, + { name = "packaging" }, + { name = "pydra-config" }, + { name = "pytest" }, + { name = "together" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, +] + +[package.dev-dependencies] +dev = [ + { name = "weave" }, +] + +[package.metadata] +requires-dist = [ + { name = "anthropic", specifier = ">=0.34.2" }, + { name = "archon-ai", specifier = ">=0.1.4" }, + { name = "datasets", specifier = ">=3.5.0" }, + { name = "einops", specifier = ">=0.8.1" }, + { name = "google-generativeai", specifier = ">=0.8.4" }, + { name = "modal", specifier = ">=0.73.136" }, + { name = "ninja", specifier = ">=1.11.1.4" }, + { name = "numpy", specifier = ">=2.2.4" }, + { name = "openai", specifier = ">=1.69.0" }, + { name = "packaging", specifier = ">=24.2" }, + { name = "pydra-config", specifier = ">=0.0.14" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "together", specifier = ">=1.5.4" }, + { name = "torch", specifier = "==2.5.0" }, + { name = "tqdm", specifier = ">=4.67.1" }, + { name = "transformers", specifier = ">=4.50.3" }, +] + +[package.metadata.requires-dev] +dev = [{ name = "weave", specifier = ">=0.51.39" }] + +[[package]] +name = "litellm" +version = "1.65.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "click" }, + { name = "httpx" }, + { name = "importlib-metadata" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "tiktoken" }, + { name = "tokenizers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/43/57e2efee4fc7e46d2c8e98e221158b0d9720237fd9d1d5dbff69c58c9037/litellm-1.65.0.tar.gz", hash = "sha256:147a74d18601ccaaff3ca125eba914ab6e5b5854aff480dce5a52be5b9d52ff8", size = 6679690 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/48/6985411f5fcb561c5da9ffef95fb71bb31e9ea812bdcb06d451f9c1727f5/litellm-1.65.0-py3-none-any.whl", hash = "sha256:bbc211f3d03e1830ed7f4304b40f70fa1fa4a2f9109d006ede5f78e83a189aba", size = 7012224 }, +] + +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "modal" +version = "0.73.136" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "certifi" }, + { name = "click" }, + { name = "fastapi" }, + { name = "grpclib" }, + { name = "protobuf" }, + { name = "rich" }, + { name = "synchronicity" }, + { name = "toml" }, + { name = "typer" }, + { name = "types-certifi" }, + { name = "types-toml" }, + { name = "typing-extensions" }, + { name = "watchfiles" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/2f/e5f6d1c1f28a4a449f7625bc58f63f7c4dc01506e6ab71bbb47958abc52b/modal-0.73.136.tar.gz", hash = "sha256:e8a6d3961c11e6440b2ab9a7f344fb1beb9aae8b8511df871ce3b2399f194af0", size = 483697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/e5/b38b0d9f365d685d9f5a0e411c2cceb8439ce44854695eb92fe55d2e8d58/modal-0.73.136-py3-none-any.whl", hash = "sha256:1f812712ea616cce949c06c5a4b45497d1157879775986de54db9ed2023b79e9", size = 551173 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "multidict" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/4a/7874ca44a1c9b23796c767dd94159f6c17e31c0e7d090552a1c623247d82/multidict-6.2.0.tar.gz", hash = "sha256:0085b0afb2446e57050140240a8595846ed64d1cbd26cef936bfab3192c673b8", size = 71066 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/ca/3ae4d9c9ba78e7bcb63e3f12974b8fa16b9a20de44e9785f5d291ccb823c/multidict-6.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b9f6392d98c0bd70676ae41474e2eecf4c7150cb419237a41f8f96043fcb81d1", size = 49238 }, + { url = "https://files.pythonhosted.org/packages/25/a4/55e595d2df586e442c85b2610542d1e14def4c6f641761125d35fb38f87c/multidict-6.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3501621d5e86f1a88521ea65d5cad0a0834c77b26f193747615b7c911e5422d2", size = 29748 }, + { url = "https://files.pythonhosted.org/packages/35/6f/09bc361a34bbf953e9897f69823f9c4b46aec0aaed6ec94ce63093ede317/multidict-6.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32ed748ff9ac682eae7859790d3044b50e3076c7d80e17a44239683769ff485e", size = 30026 }, + { url = "https://files.pythonhosted.org/packages/b6/c7/5b51816f7c38049fc50786f46e63c009e6fecd1953fbbafa8bfe4e2eb39d/multidict-6.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc826b9a8176e686b67aa60fd6c6a7047b0461cae5591ea1dc73d28f72332a8a", size = 132393 }, + { url = "https://files.pythonhosted.org/packages/1a/21/c51aca665afa93b397d2c47369f6c267193977611a55a7c9d8683dc095bc/multidict-6.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:214207dcc7a6221d9942f23797fe89144128a71c03632bf713d918db99bd36de", size = 139237 }, + { url = "https://files.pythonhosted.org/packages/2e/9b/a7b91f8ed63314e7a3c276b4ca90ae5d0267a584ca2e42106baa728622d6/multidict-6.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05fefbc3cddc4e36da209a5e49f1094bbece9a581faa7f3589201fd95df40e5d", size = 134920 }, + { url = "https://files.pythonhosted.org/packages/c8/84/4b590a121b1009fe79d1ae5875b4aa9339d37d23e368dd3bcf5e36d27452/multidict-6.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e851e6363d0dbe515d8de81fd544a2c956fdec6f8a049739562286727d4a00c3", size = 129764 }, + { url = "https://files.pythonhosted.org/packages/b8/de/831be406b5ab0dc0d25430ddf597c6ce1a2e23a4991363f1ca48f16fb817/multidict-6.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32c9b4878f48be3e75808ea7e499d6223b1eea6d54c487a66bc10a1871e3dc6a", size = 122121 }, + { url = "https://files.pythonhosted.org/packages/fa/2f/892334f4d3efc7cd11e3a64dc922a85611627380ee2de3d0627ac159a975/multidict-6.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7243c5a6523c5cfeca76e063efa5f6a656d1d74c8b1fc64b2cd1e84e507f7e2a", size = 135640 }, + { url = "https://files.pythonhosted.org/packages/6c/53/bf91c5fdede9406247dcbceaa9d7e7fa08e4d0e27fa3c76a0dab126bc6b2/multidict-6.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0e5a644e50ef9fb87878d4d57907f03a12410d2aa3b93b3acdf90a741df52c49", size = 129655 }, + { url = "https://files.pythonhosted.org/packages/d4/7a/f98e1c5d14c1bbbb83025a69da9a37344f7556c09fef39979cf62b464d60/multidict-6.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0dc25a3293c50744796e87048de5e68996104d86d940bb24bc3ec31df281b191", size = 140691 }, + { url = "https://files.pythonhosted.org/packages/dd/c9/af0ab78b53d5b769bc1fa751e53cc7356cef422bd1cf38ed653985a46ddf/multidict-6.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a49994481b99cd7dedde07f2e7e93b1d86c01c0fca1c32aded18f10695ae17eb", size = 135254 }, + { url = "https://files.pythonhosted.org/packages/c9/53/28cc971b17e25487a089bcf720fe284478f264a6fc619427ddf7145fcb2b/multidict-6.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641cf2e3447c9ecff2f7aa6e9eee9eaa286ea65d57b014543a4911ff2799d08a", size = 133620 }, + { url = "https://files.pythonhosted.org/packages/b6/9a/d7637fbe1d5928b9f6a33ce36c2ff37e0aab9aa22f5fc9552fd75fe7f364/multidict-6.2.0-cp310-cp310-win32.whl", hash = "sha256:0c383d28857f66f5aebe3e91d6cf498da73af75fbd51cedbe1adfb85e90c0460", size = 27044 }, + { url = "https://files.pythonhosted.org/packages/4e/11/04758cc18a51227dbb350a8a25c7db0620d63fb23db5b8d1f87762f05cbe/multidict-6.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:a33273a541f1e1a8219b2a4ed2de355848ecc0254264915b9290c8d2de1c74e1", size = 29149 }, + { url = "https://files.pythonhosted.org/packages/9c/fd/b247aec6add5601956d440488b7f23151d8343747e82c038af37b28d6098/multidict-6.2.0-py3-none-any.whl", hash = "sha256:5d26547423e5e71dcc562c4acdc134b900640a39abd9066d7326a7cc2324c530", size = 10266 }, +] + +[[package]] +name = "multiprocess" +version = "0.70.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/76/6e712a2623d146d314f17598df5de7224c85c0060ef63fd95cc15a25b3fa/multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee", size = 134980 }, + { url = "https://files.pythonhosted.org/packages/0f/ab/1e6e8009e380e22254ff539ebe117861e5bdb3bff1fc977920972237c6c7/multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec", size = 134982 }, + { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824 }, + { url = "https://files.pythonhosted.org/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628 }, + { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, +] + +[[package]] +name = "ninja" +version = "1.11.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/d4/6b0324541018561c5e73e617bd16f20a4fc17d1179bb3b3520b6ca8beb7b/ninja-1.11.1.4.tar.gz", hash = "sha256:6aa39f6e894e0452e5b297327db00019383ae55d5d9c57c73b04f13bf79d438a", size = 201256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/b1/3a61b348936b62a386465b1937cd778fa3a5748582e26d832dbab844ff27/ninja-1.11.1.4-py3-none-macosx_10_9_universal2.whl", hash = "sha256:b33923c8da88e8da20b6053e38deb433f53656441614207e01d283ad02c5e8e7", size = 279071 }, + { url = "https://files.pythonhosted.org/packages/12/42/4c94fdad51fcf1f039a156e97de9e4d564c2a8cc0303782d36f9bd893a4b/ninja-1.11.1.4-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cede0af00b58e27b31f2482ba83292a8e9171cdb9acc2c867a3b6e40b3353e43", size = 472026 }, + { url = "https://files.pythonhosted.org/packages/eb/7a/455d2877fe6cf99886849c7f9755d897df32eaf3a0fba47b56e615f880f7/ninja-1.11.1.4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:096487995473320de7f65d622c3f1d16c3ad174797602218ca8c967f51ec38a0", size = 422814 }, + { url = "https://files.pythonhosted.org/packages/e3/ad/fb6cca942528e25e8e0ab0f0cf98fe007319bf05cf69d726c564b815c4af/ninja-1.11.1.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3090d4488fadf6047d0d7a1db0c9643a8d391f0d94729554dbb89b5bdc769d7", size = 156965 }, + { url = "https://files.pythonhosted.org/packages/a8/e7/d94a1b60031b115dd88526834b3da69eaacdc3c1a6769773ca8e2b1386b5/ninja-1.11.1.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecce44a00325a93631792974659cf253a815cc6da4ec96f89742925dfc295a0d", size = 179937 }, + { url = "https://files.pythonhosted.org/packages/08/cc/e9316a28235409e9363794fc3d0b3083e48dd80d441006de66421e55f364/ninja-1.11.1.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c29bb66d2aa46a2409ab369ea804c730faec7652e8c22c1e428cc09216543e5", size = 157020 }, + { url = "https://files.pythonhosted.org/packages/e3/30/389b22300541aa5f2e9dad322c4de2f84be4e32aa4e8babd9160d620b5f1/ninja-1.11.1.4-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:055f386fb550c2c9d6157e45e20a84d29c47968876b9c5794ae2aec46f952306", size = 130389 }, + { url = "https://files.pythonhosted.org/packages/a9/10/e27f35cb92813aabbb7ae771b1685b45be1cc8a0798ce7d4bfd08d142b93/ninja-1.11.1.4-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:f6186d7607bb090c3be1e10c8a56b690be238f953616626f5032238c66e56867", size = 372435 }, + { url = "https://files.pythonhosted.org/packages/c2/26/e3559619756739aae124c6abf7fe41f7e546ab1209cfbffb13137bff2d2e/ninja-1.11.1.4-py3-none-musllinux_1_1_i686.whl", hash = "sha256:cf4453679d15babc04ba023d68d091bb613091b67101c88f85d2171c6621c6eb", size = 419300 }, + { url = "https://files.pythonhosted.org/packages/35/46/809e4e9572570991b8e6f88f3583807d017371ab4cb09171cbc72a7eb3e4/ninja-1.11.1.4-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:d4a6f159b08b0ac4aca5ee1572e3e402f969139e71d85d37c0e2872129098749", size = 420239 }, + { url = "https://files.pythonhosted.org/packages/e6/64/5cb5710d15f844edf02ada577f8eddfdcd116f47eec15850f3371a3a4b33/ninja-1.11.1.4-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:c3b96bd875f3ef1db782470e9e41d7508905a0986571f219d20ffed238befa15", size = 415986 }, + { url = "https://files.pythonhosted.org/packages/95/b2/0e9ab1d926f423b12b09925f78afcc5e48b3c22e7121be3ddf6c35bf06a3/ninja-1.11.1.4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:cf554e73f72c04deb04d0cf51f5fdb1903d9c9ca3d2344249c8ce3bd616ebc02", size = 379657 }, + { url = "https://files.pythonhosted.org/packages/c8/3e/fd6d330d0434168e7fe070d414b57dd99c4c133faa69c05b42a3cbdc6c13/ninja-1.11.1.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:cfdd09776436a1ff3c4a2558d3fc50a689fb9d7f1bdbc3e6f7b8c2991341ddb3", size = 454466 }, + { url = "https://files.pythonhosted.org/packages/e6/df/a25f3ad0b1c59d1b90564096e4fd89a6ca30d562b1e942f23880c3000b89/ninja-1.11.1.4-py3-none-win32.whl", hash = "sha256:2ab67a41c90bea5ec4b795bab084bc0b3b3bb69d3cd21ca0294fc0fc15a111eb", size = 255931 }, + { url = "https://files.pythonhosted.org/packages/5b/10/9b8fe9ac004847490cc7b54896124c01ce2d87d95dc60aabd0b8591addff/ninja-1.11.1.4-py3-none-win_amd64.whl", hash = "sha256:4617b3c12ff64b611a7d93fd9e378275512bb36eff8babff7c83f5116b4f8d66", size = 296461 }, + { url = "https://files.pythonhosted.org/packages/b9/58/612a17593c2d117f96c7f6b7f1e6570246bddc4b1e808519403a1417f217/ninja-1.11.1.4-py3-none-win_arm64.whl", hash = "sha256:5713cf50c5be50084a8693308a63ecf9e55c3132a78a41ab1363a28b6caaaee1", size = 271441 }, +] + +[[package]] +name = "numpy" +version = "2.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/78/31103410a57bc2c2b93a3597340a8119588571f6a4539067546cb9a0bfac/numpy-2.2.4.tar.gz", hash = "sha256:9ba03692a45d3eef66559efe1d1096c4b9b75c0986b5dff5530c378fb8331d4f", size = 20270701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/89/a79e86e5c1433926ed7d60cb267fb64aa578b6101ab645800fd43b4801de/numpy-2.2.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8146f3550d627252269ac42ae660281d673eb6f8b32f113538e0cc2a9aed42b9", size = 21250661 }, + { url = "https://files.pythonhosted.org/packages/79/c2/f50921beb8afd60ed9589ad880332cfefdb805422210d327fb48f12b7a81/numpy-2.2.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e642d86b8f956098b564a45e6f6ce68a22c2c97a04f5acd3f221f57b8cb850ae", size = 14389926 }, + { url = "https://files.pythonhosted.org/packages/c7/b9/2c4e96130b0b0f97b0ef4a06d6dae3b39d058b21a5e2fa2decd7fd6b1c8f/numpy-2.2.4-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:a84eda42bd12edc36eb5b53bbcc9b406820d3353f1994b6cfe453a33ff101775", size = 5428329 }, + { url = "https://files.pythonhosted.org/packages/7f/a5/3d7094aa898f4fc5c84cdfb26beeae780352d43f5d8bdec966c4393d644c/numpy-2.2.4-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:4ba5054787e89c59c593a4169830ab362ac2bee8a969249dc56e5d7d20ff8df9", size = 6963559 }, + { url = "https://files.pythonhosted.org/packages/4c/22/fb1be710a14434c09080dd4a0acc08939f612ec02efcb04b9e210474782d/numpy-2.2.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7716e4a9b7af82c06a2543c53ca476fa0b57e4d760481273e09da04b74ee6ee2", size = 14368066 }, + { url = "https://files.pythonhosted.org/packages/c2/07/2e5cc71193e3ef3a219ffcf6ca4858e46ea2be09c026ddd480d596b32867/numpy-2.2.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf8c1d66f432ce577d0197dceaac2ac00c0759f573f28516246351c58a85020", size = 16417040 }, + { url = "https://files.pythonhosted.org/packages/1a/97/3b1537776ad9a6d1a41813818343745e8dd928a2916d4c9edcd9a8af1dac/numpy-2.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:218f061d2faa73621fa23d6359442b0fc658d5b9a70801373625d958259eaca3", size = 15879862 }, + { url = "https://files.pythonhosted.org/packages/b0/b7/4472f603dd45ef36ff3d8e84e84fe02d9467c78f92cc121633dce6da307b/numpy-2.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:df2f57871a96bbc1b69733cd4c51dc33bea66146b8c63cacbfed73eec0883017", size = 18206032 }, + { url = "https://files.pythonhosted.org/packages/0d/bd/6a092963fb82e6c5aa0d0440635827bbb2910da229545473bbb58c537ed3/numpy-2.2.4-cp310-cp310-win32.whl", hash = "sha256:a0258ad1f44f138b791327961caedffbf9612bfa504ab9597157806faa95194a", size = 6608517 }, + { url = "https://files.pythonhosted.org/packages/01/e3/cb04627bc2a1638948bc13e818df26495aa18e20d5be1ed95ab2b10b6847/numpy-2.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:0d54974f9cf14acf49c60f0f7f4084b6579d24d439453d5fc5805d46a165b542", size = 12943498 }, + { url = "https://files.pythonhosted.org/packages/b2/5c/f09c33a511aff41a098e6ef3498465d95f6360621034a3d95f47edbc9119/numpy-2.2.4-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7051ee569db5fbac144335e0f3b9c2337e0c8d5c9fee015f259a5bd70772b7e8", size = 21081956 }, + { url = "https://files.pythonhosted.org/packages/ba/30/74c48b3b6494c4b820b7fa1781d441e94d87a08daa5b35d222f06ba41a6f/numpy-2.2.4-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ab2939cd5bec30a7430cbdb2287b63151b77cf9624de0532d629c9a1c59b1d5c", size = 6827143 }, + { url = "https://files.pythonhosted.org/packages/54/f5/ab0d2f48b490535c7a80e05da4a98902b632369efc04f0e47bb31ca97d8f/numpy-2.2.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f35b19894a9e08639fd60a1ec1978cb7f5f7f1eace62f38dd36be8aecdef4d", size = 16233350 }, + { url = "https://files.pythonhosted.org/packages/3b/3a/2f6d8c1f8e45d496bca6baaec93208035faeb40d5735c25afac092ec9a12/numpy-2.2.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b4adfbbc64014976d2f91084915ca4e626fbf2057fb81af209c1a6d776d23e3d", size = 12857565 }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.4.5.8" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805 }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306 }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737 }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117 }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.5.147" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206 }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.6.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057 }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.3.1.170" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.21.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0", size = 188654414 }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810 }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144 }, +] + +[[package]] +name = "openai" +version = "1.69.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/99/d164612528dfb7a9b19330623daded608e75d25823b01f81e0376eb388a4/openai-1.69.0.tar.gz", hash = "sha256:7b8a10a8ff77e1ae827e5e4c8480410af2070fb68bc973d6c994cf8218f1f98d", size = 409579 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/a4/28113be8b7bc937656aaf7b06feff7e9a5eb742ee4e405c6c48c30d879c4/openai-1.69.0-py3-none-any.whl", hash = "sha256:73c4b2ddfd050060f8d93c70367189bd891e70a5adb6d69c04c3571f4fea5627", size = 599068 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827 }, + { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897 }, + { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908 }, + { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210 }, + { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292 }, + { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379 }, + { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471 }, +] + +[[package]] +name = "pillow" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/1c/2dcea34ac3d7bc96a1fd1bd0a6e06a57c67167fec2cff8d95d88229a8817/pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8", size = 3229983 }, + { url = "https://files.pythonhosted.org/packages/14/ca/6bec3df25e4c88432681de94a3531cc738bd85dea6c7aa6ab6f81ad8bd11/pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192", size = 3101831 }, + { url = "https://files.pythonhosted.org/packages/d4/2c/668e18e5521e46eb9667b09e501d8e07049eb5bfe39d56be0724a43117e6/pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2", size = 4314074 }, + { url = "https://files.pythonhosted.org/packages/02/80/79f99b714f0fc25f6a8499ecfd1f810df12aec170ea1e32a4f75746051ce/pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26", size = 4394933 }, + { url = "https://files.pythonhosted.org/packages/81/aa/8d4ad25dc11fd10a2001d5b8a80fdc0e564ac33b293bdfe04ed387e0fd95/pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07", size = 4353349 }, + { url = "https://files.pythonhosted.org/packages/84/7a/cd0c3eaf4a28cb2a74bdd19129f7726277a7f30c4f8424cd27a62987d864/pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482", size = 4476532 }, + { url = "https://files.pythonhosted.org/packages/8f/8b/a907fdd3ae8f01c7670dfb1499c53c28e217c338b47a813af8d815e7ce97/pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e", size = 4279789 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/9f139d9e8cccd661c3efbf6898967a9a337eb2e9be2b454ba0a09533100d/pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269", size = 4413131 }, + { url = "https://files.pythonhosted.org/packages/a8/68/0d8d461f42a3f37432203c8e6df94da10ac8081b6d35af1c203bf3111088/pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49", size = 2291213 }, + { url = "https://files.pythonhosted.org/packages/14/81/d0dff759a74ba87715509af9f6cb21fa21d93b02b3316ed43bda83664db9/pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a", size = 2625725 }, + { url = "https://files.pythonhosted.org/packages/ce/1f/8d50c096a1d58ef0584ddc37e6f602828515219e9d2428e14ce50f5ecad1/pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65", size = 2375213 }, + { url = "https://files.pythonhosted.org/packages/fa/c5/389961578fb677b8b3244fcd934f720ed25a148b9a5cc81c91bdf59d8588/pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90", size = 3198345 }, + { url = "https://files.pythonhosted.org/packages/c4/fa/803c0e50ffee74d4b965229e816af55276eac1d5806712de86f9371858fd/pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb", size = 3072938 }, + { url = "https://files.pythonhosted.org/packages/dc/67/2a3a5f8012b5d8c63fe53958ba906c1b1d0482ebed5618057ef4d22f8076/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442", size = 3400049 }, + { url = "https://files.pythonhosted.org/packages/e5/a0/514f0d317446c98c478d1872497eb92e7cde67003fed74f696441e647446/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83", size = 3422431 }, + { url = "https://files.pythonhosted.org/packages/cd/00/20f40a935514037b7d3f87adfc87d2c538430ea625b63b3af8c3f5578e72/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f", size = 3446208 }, + { url = "https://files.pythonhosted.org/packages/28/3c/7de681727963043e093c72e6c3348411b0185eab3263100d4490234ba2f6/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73", size = 3509746 }, + { url = "https://files.pythonhosted.org/packages/41/67/936f9814bdd74b2dfd4822f1f7725ab5d8ff4103919a1664eb4874c58b2f/pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0", size = 2626353 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "propcache" +version = "0.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/56/e27c136101addf877c8291dbda1b3b86ae848f3837ce758510a0d806c92f/propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98", size = 80224 }, + { url = "https://files.pythonhosted.org/packages/63/bd/88e98836544c4f04db97eefd23b037c2002fa173dd2772301c61cd3085f9/propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180", size = 46491 }, + { url = "https://files.pythonhosted.org/packages/15/43/0b8eb2a55753c4a574fc0899885da504b521068d3b08ca56774cad0bea2b/propcache-0.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:730178f476ef03d3d4d255f0c9fa186cb1d13fd33ffe89d39f2cda4da90ceb71", size = 45927 }, + { url = "https://files.pythonhosted.org/packages/ad/6c/d01f9dfbbdc613305e0a831016844987a1fb4861dd221cd4c69b1216b43f/propcache-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967a8eec513dbe08330f10137eacb427b2ca52118769e82ebcfcab0fba92a649", size = 206135 }, + { url = "https://files.pythonhosted.org/packages/9a/8a/e6e1c77394088f4cfdace4a91a7328e398ebed745d59c2f6764135c5342d/propcache-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b9145c35cc87313b5fd480144f8078716007656093d23059e8993d3a8fa730f", size = 220517 }, + { url = "https://files.pythonhosted.org/packages/19/3b/6c44fa59d6418f4239d5db8b1ece757351e85d6f3ca126dfe37d427020c8/propcache-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e64e948ab41411958670f1093c0a57acfdc3bee5cf5b935671bbd5313bcf229", size = 218952 }, + { url = "https://files.pythonhosted.org/packages/7c/e4/4aeb95a1cd085e0558ab0de95abfc5187329616193a1012a6c4c930e9f7a/propcache-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:319fa8765bfd6a265e5fa661547556da381e53274bc05094fc9ea50da51bfd46", size = 206593 }, + { url = "https://files.pythonhosted.org/packages/da/6a/29fa75de1cbbb302f1e1d684009b969976ca603ee162282ae702287b6621/propcache-0.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66d8ccbc902ad548312b96ed8d5d266d0d2c6d006fd0f66323e9d8f2dd49be7", size = 196745 }, + { url = "https://files.pythonhosted.org/packages/19/7e/2237dad1dbffdd2162de470599fa1a1d55df493b16b71e5d25a0ac1c1543/propcache-0.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d219b0dbabe75e15e581fc1ae796109b07c8ba7d25b9ae8d650da582bed01b0", size = 203369 }, + { url = "https://files.pythonhosted.org/packages/a4/bc/a82c5878eb3afb5c88da86e2cf06e1fe78b7875b26198dbb70fe50a010dc/propcache-0.3.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd6a55f65241c551eb53f8cf4d2f4af33512c39da5d9777694e9d9c60872f519", size = 198723 }, + { url = "https://files.pythonhosted.org/packages/17/76/9632254479c55516f51644ddbf747a45f813031af5adcb8db91c0b824375/propcache-0.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9979643ffc69b799d50d3a7b72b5164a2e97e117009d7af6dfdd2ab906cb72cd", size = 200751 }, + { url = "https://files.pythonhosted.org/packages/3e/c3/a90b773cf639bd01d12a9e20c95be0ae978a5a8abe6d2d343900ae76cd71/propcache-0.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf9e93a81979f1424f1a3d155213dc928f1069d697e4353edb8a5eba67c6259", size = 210730 }, + { url = "https://files.pythonhosted.org/packages/ed/ec/ad5a952cdb9d65c351f88db7c46957edd3d65ffeee72a2f18bd6341433e0/propcache-0.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2fce1df66915909ff6c824bbb5eb403d2d15f98f1518e583074671a30fe0c21e", size = 213499 }, + { url = "https://files.pythonhosted.org/packages/83/c0/ea5133dda43e298cd2010ec05c2821b391e10980e64ee72c0a76cdbb813a/propcache-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4d0dfdd9a2ebc77b869a0b04423591ea8823f791293b527dc1bb896c1d6f1136", size = 207132 }, + { url = "https://files.pythonhosted.org/packages/79/dd/71aae9dec59333064cfdd7eb31a63fa09f64181b979802a67a90b2abfcba/propcache-0.3.1-cp310-cp310-win32.whl", hash = "sha256:1f6cc0ad7b4560e5637eb2c994e97b4fa41ba8226069c9277eb5ea7101845b42", size = 40952 }, + { url = "https://files.pythonhosted.org/packages/31/0a/49ff7e5056c17dfba62cbdcbb90a29daffd199c52f8e65e5cb09d5f53a57/propcache-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:47ef24aa6511e388e9894ec16f0fbf3313a53ee68402bc428744a367ec55b833", size = 45163 }, + { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, +] + +[[package]] +name = "proto-plus" +version = "1.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, +] + +[[package]] +name = "protobuf" +version = "5.29.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/7d/b9dca7365f0e2c4fa7c193ff795427cfa6290147e5185ab11ece280a18e7/protobuf-5.29.4.tar.gz", hash = "sha256:4f1dfcd7997b31ef8f53ec82781ff434a28bf71d9102ddde14d076adcfc78c99", size = 424902 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/b2/043a1a1a20edd134563699b0e91862726a0dc9146c090743b6c44d798e75/protobuf-5.29.4-cp310-abi3-win32.whl", hash = "sha256:13eb236f8eb9ec34e63fc8b1d6efd2777d062fa6aaa68268fb67cf77f6839ad7", size = 422709 }, + { url = "https://files.pythonhosted.org/packages/79/fc/2474b59570daa818de6124c0a15741ee3e5d6302e9d6ce0bdfd12e98119f/protobuf-5.29.4-cp310-abi3-win_amd64.whl", hash = "sha256:bcefcdf3976233f8a502d265eb65ea740c989bacc6c30a58290ed0e519eb4b8d", size = 434506 }, + { url = "https://files.pythonhosted.org/packages/46/de/7c126bbb06aa0f8a7b38aaf8bd746c514d70e6a2a3f6dd460b3b7aad7aae/protobuf-5.29.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:307ecba1d852ec237e9ba668e087326a67564ef83e45a0189a772ede9e854dd0", size = 417826 }, + { url = "https://files.pythonhosted.org/packages/a2/b5/bade14ae31ba871a139aa45e7a8183d869efe87c34a4850c87b936963261/protobuf-5.29.4-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:aec4962f9ea93c431d5714ed1be1c93f13e1a8618e70035ba2b0564d9e633f2e", size = 319574 }, + { url = "https://files.pythonhosted.org/packages/46/88/b01ed2291aae68b708f7d334288ad5fb3e7aa769a9c309c91a0d55cb91b0/protobuf-5.29.4-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:d7d3f7d1d5a66ed4942d4fefb12ac4b14a29028b209d4bfb25c68ae172059922", size = 319672 }, + { url = "https://files.pythonhosted.org/packages/12/fb/a586e0c973c95502e054ac5f81f88394f24ccc7982dac19c515acd9e2c93/protobuf-5.29.4-py3-none-any.whl", hash = "sha256:3fde11b505e1597f71b875ef2fc52062b6a9740e5f7c8997ce878b6009145862", size = 172551 }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, +] + +[[package]] +name = "pyarrow" +version = "19.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/01/b23b514d86b839956238d3f8ef206fd2728eee87ff1b8ce150a5678d9721/pyarrow-19.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:fc28912a2dc924dddc2087679cc8b7263accc71b9ff025a1362b004711661a69", size = 30688914 }, + { url = "https://files.pythonhosted.org/packages/c6/68/218ff7cf4a0652a933e5f2ed11274f724dd43b9813cb18dd72c0a35226a2/pyarrow-19.0.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fca15aabbe9b8355800d923cc2e82c8ef514af321e18b437c3d782aa884eaeec", size = 32102866 }, + { url = "https://files.pythonhosted.org/packages/98/01/c295050d183014f4a2eb796d7d2bbfa04b6cccde7258bb68aacf6f18779b/pyarrow-19.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad76aef7f5f7e4a757fddcdcf010a8290958f09e3470ea458c80d26f4316ae89", size = 41147682 }, + { url = "https://files.pythonhosted.org/packages/40/17/a6c3db0b5f3678f33bbb552d2acbc16def67f89a72955b67b0109af23eb0/pyarrow-19.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d03c9d6f2a3dffbd62671ca070f13fc527bb1867b4ec2b98c7eeed381d4f389a", size = 42179192 }, + { url = "https://files.pythonhosted.org/packages/cf/75/c7c8e599300d8cebb6cb339014800e1c720c9db2a3fcb66aa64ec84bac72/pyarrow-19.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:65cf9feebab489b19cdfcfe4aa82f62147218558d8d3f0fc1e9dea0ab8e7905a", size = 40517272 }, + { url = "https://files.pythonhosted.org/packages/ef/c9/68ab123ee1528699c4d5055f645ecd1dd68ff93e4699527249d02f55afeb/pyarrow-19.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:41f9706fbe505e0abc10e84bf3a906a1338905cbbcf1177b71486b03e6ea6608", size = 42069036 }, + { url = "https://files.pythonhosted.org/packages/54/e3/d5cfd7654084e6c0d9c3ce949e5d9e0ccad569ae1e2d5a68a3ec03b2be89/pyarrow-19.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6cb2335a411b713fdf1e82a752162f72d4a7b5dbc588e32aa18383318b05866", size = 25277951 }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, +] + +[[package]] +name = "pydantic" +version = "2.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/a3/698b87a4d4d303d7c5f62ea5fbf7a79cab236ccfbd0a17847b7f77f8163e/pydantic-2.11.1.tar.gz", hash = "sha256:442557d2910e75c991c39f4b4ab18963d57b9b55122c8b2a9cd176d8c29ce968", size = 782817 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/12/f9221a949f2419e2e23847303c002476c26fbcfd62dc7f3d25d0bec5ca99/pydantic-2.11.1-py3-none-any.whl", hash = "sha256:5b6c415eee9f8123a14d859be0c84363fec6b1feb6b688d6435801230b56e0b8", size = 442648 }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/05/91ce14dfd5a3a99555fce436318cc0fd1f08c4daa32b3248ad63669ea8b4/pydantic_core-2.33.0.tar.gz", hash = "sha256:40eb8af662ba409c3cbf4a8150ad32ae73514cd7cb1f1a2113af39763dd616b3", size = 434080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/43/0649ad07e66b36a3fb21442b425bd0348ac162c5e686b36471f363201535/pydantic_core-2.33.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71dffba8fe9ddff628c68f3abd845e91b028361d43c5f8e7b3f8b91d7d85413e", size = 2042968 }, + { url = "https://files.pythonhosted.org/packages/a0/a6/975fea4774a459e495cb4be288efd8b041ac756a0a763f0b976d0861334b/pydantic_core-2.33.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:abaeec1be6ed535a5d7ffc2e6c390083c425832b20efd621562fbb5bff6dc518", size = 1860347 }, + { url = "https://files.pythonhosted.org/packages/aa/49/7858dadad305101a077ec4d0c606b6425a2b134ea8d858458a6d287fd871/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759871f00e26ad3709efc773ac37b4d571de065f9dfb1778012908bcc36b3a73", size = 1910060 }, + { url = "https://files.pythonhosted.org/packages/8d/4f/6522527911d9c5fe6d76b084d8b388d5c84b09d113247b39f91937500b34/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dcfebee69cd5e1c0b76a17e17e347c84b00acebb8dd8edb22d4a03e88e82a207", size = 1997129 }, + { url = "https://files.pythonhosted.org/packages/75/d0/06f396da053e3d73001ea4787e56b4d7132a87c0b5e2e15a041e808c35cd/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b1262b912435a501fa04cd213720609e2cefa723a07c92017d18693e69bf00b", size = 2140389 }, + { url = "https://files.pythonhosted.org/packages/f5/6b/b9ff5b69cd4ef007cf665463f3be2e481dc7eb26c4a55b2f57a94308c31a/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4726f1f3f42d6a25678c67da3f0b10f148f5655813c5aca54b0d1742ba821b8f", size = 2754237 }, + { url = "https://files.pythonhosted.org/packages/53/80/b4879de375cdf3718d05fcb60c9aa1f119d28e261dafa51b6a69c78f7178/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e790954b5093dff1e3a9a2523fddc4e79722d6f07993b4cd5547825c3cbf97b5", size = 2007433 }, + { url = "https://files.pythonhosted.org/packages/46/24/54054713dc0af98a94eab37e0f4294dfd5cd8f70b2ca9dcdccd15709fd7e/pydantic_core-2.33.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34e7fb3abe375b5c4e64fab75733d605dda0f59827752debc99c17cb2d5f3276", size = 2123980 }, + { url = "https://files.pythonhosted.org/packages/3a/4c/257c1cb89e14cfa6e95ebcb91b308eb1dd2b348340ff76a6e6fcfa9969e1/pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ecb158fb9b9091b515213bed3061eb7deb1d3b4e02327c27a0ea714ff46b0760", size = 2087433 }, + { url = "https://files.pythonhosted.org/packages/0c/62/927df8a39ad78ef7b82c5446e01dec9bb0043e1ad71d8f426062f5f014db/pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:4d9149e7528af8bbd76cc055967e6e04617dcb2a2afdaa3dea899406c5521faa", size = 2260242 }, + { url = "https://files.pythonhosted.org/packages/74/f2/389414f7c77a100954e84d6f52a82bd1788ae69db72364376d8a73b38765/pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e81a295adccf73477220e15ff79235ca9dcbcee4be459eb9d4ce9a2763b8386c", size = 2258227 }, + { url = "https://files.pythonhosted.org/packages/53/99/94516313e15d906a1264bb40faf24a01a4af4e2ca8a7c10dd173b6513c5a/pydantic_core-2.33.0-cp310-cp310-win32.whl", hash = "sha256:f22dab23cdbce2005f26a8f0c71698457861f97fc6318c75814a50c75e87d025", size = 1925523 }, + { url = "https://files.pythonhosted.org/packages/7d/67/cc789611c6035a0b71305a1ec6ba196256ced76eba8375f316f840a70456/pydantic_core-2.33.0-cp310-cp310-win_amd64.whl", hash = "sha256:9cb2390355ba084c1ad49485d18449b4242da344dea3e0fe10babd1f0db7dcfc", size = 1951872 }, + { url = "https://files.pythonhosted.org/packages/44/77/85e173b715e1a277ce934f28d877d82492df13e564fa68a01c96f36a47ad/pydantic_core-2.33.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e2762c568596332fdab56b07060c8ab8362c56cf2a339ee54e491cd503612c50", size = 2040129 }, + { url = "https://files.pythonhosted.org/packages/33/e7/33da5f8a94bbe2191cfcd15bd6d16ecd113e67da1b8c78d3cc3478112dab/pydantic_core-2.33.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bf637300ff35d4f59c006fff201c510b2b5e745b07125458a5389af3c0dff8c", size = 1872656 }, + { url = "https://files.pythonhosted.org/packages/b4/7a/9600f222bea840e5b9ba1f17c0acc79b669b24542a78c42c6a10712c0aae/pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c151ce3d59ed56ebd7ce9ce5986a409a85db697d25fc232f8e81f195aa39a1", size = 1903731 }, + { url = "https://files.pythonhosted.org/packages/81/d2/94c7ca4e24c5dcfb74df92e0836c189e9eb6814cf62d2f26a75ea0a906db/pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee65f0cc652261744fd07f2c6e6901c914aa6c5ff4dcfaf1136bc394d0dd26b", size = 2083966 }, + { url = "https://files.pythonhosted.org/packages/b8/74/a0259989d220e8865ed6866a6d40539e40fa8f507e587e35d2414cc081f8/pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:024d136ae44d233e6322027bbf356712b3940bee816e6c948ce4b90f18471b3d", size = 2118951 }, + { url = "https://files.pythonhosted.org/packages/13/4c/87405ed04d6d07597920b657f082a8e8e58bf3034178bb9044b4d57a91e2/pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e37f10f6d4bc67c58fbd727108ae1d8b92b397355e68519f1e4a7babb1473442", size = 2079632 }, + { url = "https://files.pythonhosted.org/packages/5a/4c/bcb02970ef91d4cd6de7c6893101302637da456bc8b52c18ea0d047b55ce/pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:502ed542e0d958bd12e7c3e9a015bce57deaf50eaa8c2e1c439b512cb9db1e3a", size = 2250541 }, + { url = "https://files.pythonhosted.org/packages/a3/2b/dbe5450c4cd904be5da736dcc7f2357b828199e29e38de19fc81f988b288/pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:715c62af74c236bf386825c0fdfa08d092ab0f191eb5b4580d11c3189af9d330", size = 2255685 }, + { url = "https://files.pythonhosted.org/packages/ca/a6/ca1d35f695d81f639c5617fc9efb44caad21a9463383fa45364b3044175a/pydantic_core-2.33.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bccc06fa0372151f37f6b69834181aa9eb57cf8665ed36405fb45fbf6cac3bae", size = 2082395 }, +] + +[[package]] +name = "pydra-config" +version = "0.0.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "pydantic" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/8f/e834d71d308b98c97c37f94b7df4bee01761c57ec16d506dbe6cb7d8deda/pydra_config-0.0.14.tar.gz", hash = "sha256:a486cbb3921a49131a6e9151948ce42e731bfafe1695a143132bd9d9bc16994b", size = 16261 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/ff/745adc70a88f0e4752972b30b591ebed0824597306b2329ce706f05a04da/pydra_config-0.0.14-py3-none-any.whl", hash = "sha256:f24d3a8b8364e75bcc3f854a7763befffc56738c563425bf62d1b62f12582009", size = 10803 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pyparsing" +version = "3.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120 }, +] + +[[package]] +name = "pytest" +version = "8.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 }, +] + +[[package]] +name = "rich" +version = "13.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, +] + +[[package]] +name = "rpds-py" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/b3/52b213298a0ba7097c7ea96bee95e1947aa84cc816d48cebb539770cdf41/rpds_py-0.24.0.tar.gz", hash = "sha256:772cc1b2cd963e7e17e6cc55fe0371fb9c704d63e44cacec7b9b7f523b78919e", size = 26863 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/21/cbc43b220c9deb536b07fbd598c97d463bbb7afb788851891252fc920742/rpds_py-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:006f4342fe729a368c6df36578d7a348c7c716be1da0a1a0f86e3021f8e98724", size = 377531 }, + { url = "https://files.pythonhosted.org/packages/42/15/cc4b09ef160483e49c3aab3b56f3d375eadf19c87c48718fb0147e86a446/rpds_py-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2d53747da70a4e4b17f559569d5f9506420966083a31c5fbd84e764461c4444b", size = 362273 }, + { url = "https://files.pythonhosted.org/packages/8c/a2/67718a188a88dbd5138d959bed6efe1cc7413a4caa8283bd46477ed0d1ad/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8acd55bd5b071156bae57b555f5d33697998752673b9de554dd82f5b5352727", size = 388111 }, + { url = "https://files.pythonhosted.org/packages/e5/e6/cbf1d3163405ad5f4a1a6d23f80245f2204d0c743b18525f34982dec7f4d/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7e80d375134ddb04231a53800503752093dbb65dad8dabacce2c84cccc78e964", size = 394447 }, + { url = "https://files.pythonhosted.org/packages/21/bb/4fe220ccc8a549b38b9e9cec66212dc3385a82a5ee9e37b54411cce4c898/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60748789e028d2a46fc1c70750454f83c6bdd0d05db50f5ae83e2db500b34da5", size = 448028 }, + { url = "https://files.pythonhosted.org/packages/a5/41/d2d6e0fd774818c4cadb94185d30cf3768de1c2a9e0143fc8bc6ce59389e/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e1daf5bf6c2be39654beae83ee6b9a12347cb5aced9a29eecf12a2d25fff664", size = 447410 }, + { url = "https://files.pythonhosted.org/packages/a7/a7/6d04d438f53d8bb2356bb000bea9cf5c96a9315e405b577117e344cc7404/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b221c2457d92a1fb3c97bee9095c874144d196f47c038462ae6e4a14436f7bc", size = 389531 }, + { url = "https://files.pythonhosted.org/packages/23/be/72e6df39bd7ca5a66799762bf54d8e702483fdad246585af96723109d486/rpds_py-0.24.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:66420986c9afff67ef0c5d1e4cdc2d0e5262f53ad11e4f90e5e22448df485bf0", size = 420099 }, + { url = "https://files.pythonhosted.org/packages/8c/c9/ca100cd4688ee0aa266197a5cb9f685231676dd7d573041ca53787b23f4e/rpds_py-0.24.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:43dba99f00f1d37b2a0265a259592d05fcc8e7c19d140fe51c6e6f16faabeb1f", size = 564950 }, + { url = "https://files.pythonhosted.org/packages/05/98/908cd95686d33b3ac8ac2e582d7ae38e2c3aa2c0377bf1f5663bafd1ffb2/rpds_py-0.24.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a88c0d17d039333a41d9bf4616bd062f0bd7aa0edeb6cafe00a2fc2a804e944f", size = 591778 }, + { url = "https://files.pythonhosted.org/packages/7b/ac/e143726f1dd3215efcb974b50b03bd08a8a1556b404a0a7872af6d197e57/rpds_py-0.24.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc31e13ce212e14a539d430428cd365e74f8b2d534f8bc22dd4c9c55b277b875", size = 560421 }, + { url = "https://files.pythonhosted.org/packages/60/28/add1c1d2fcd5aa354f7225d036d4492261759a22d449cff14841ef36a514/rpds_py-0.24.0-cp310-cp310-win32.whl", hash = "sha256:fc2c1e1b00f88317d9de6b2c2b39b012ebbfe35fe5e7bef980fd2a91f6100a07", size = 222089 }, + { url = "https://files.pythonhosted.org/packages/b0/ac/81f8066c6de44c507caca488ba336ae30d35d57f61fe10578824d1a70196/rpds_py-0.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0145295ca415668420ad142ee42189f78d27af806fcf1f32a18e51d47dd2052", size = 234622 }, + { url = "https://files.pythonhosted.org/packages/99/48/11dae46d0c7f7e156ca0971a83f89c510af0316cd5d42c771b7cef945f0c/rpds_py-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:619ca56a5468f933d940e1bf431c6f4e13bef8e688698b067ae68eb4f9b30e3a", size = 378224 }, + { url = "https://files.pythonhosted.org/packages/33/18/e8398d255369e35d312942f3bb8ecaff013c44968904891be2ab63b3aa94/rpds_py-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b28e5122829181de1898c2c97f81c0b3246d49f585f22743a1246420bb8d399", size = 363252 }, + { url = "https://files.pythonhosted.org/packages/17/39/dd73ba691f4df3e6834bf982de214086ac3359ab3ac035adfb30041570e3/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e5ab32cf9eb3647450bc74eb201b27c185d3857276162c101c0f8c6374e098", size = 388871 }, + { url = "https://files.pythonhosted.org/packages/2f/2e/da0530b25cabd0feca2a759b899d2df325069a94281eeea8ac44c6cfeff7/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:208b3a70a98cf3710e97cabdc308a51cd4f28aa6e7bb11de3d56cd8b74bab98d", size = 394766 }, + { url = "https://files.pythonhosted.org/packages/4c/ee/dd1c5040a431beb40fad4a5d7868acf343444b0bc43e627c71df2506538b/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbc4362e06f950c62cad3d4abf1191021b2ffaf0b31ac230fbf0526453eee75e", size = 448712 }, + { url = "https://files.pythonhosted.org/packages/f5/ec/6b93ffbb686be948e4d91ec76f4e6757f8551034b2a8176dd848103a1e34/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebea2821cdb5f9fef44933617be76185b80150632736f3d76e54829ab4a3b4d1", size = 447150 }, + { url = "https://files.pythonhosted.org/packages/55/d5/a1c23760adad85b432df074ced6f910dd28f222b8c60aeace5aeb9a6654e/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4df06c35465ef4d81799999bba810c68d29972bf1c31db61bfdb81dd9d5bb", size = 390662 }, + { url = "https://files.pythonhosted.org/packages/a5/f3/419cb1f9bfbd3a48c256528c156e00f3349e3edce5ad50cbc141e71f66a5/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3aa13bdf38630da298f2e0d77aca967b200b8cc1473ea05248f6c5e9c9bdb44", size = 421351 }, + { url = "https://files.pythonhosted.org/packages/98/8e/62d1a55078e5ede0b3b09f35e751fa35924a34a0d44d7c760743383cd54a/rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:041f00419e1da7a03c46042453598479f45be3d787eb837af382bfc169c0db33", size = 566074 }, + { url = "https://files.pythonhosted.org/packages/fc/69/b7d1003166d78685da032b3c4ff1599fa536a3cfe6e5ce2da87c9c431906/rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d8754d872a5dfc3c5bf9c0e059e8107451364a30d9fd50f1f1a85c4fb9481164", size = 592398 }, + { url = "https://files.pythonhosted.org/packages/ea/a8/1c98bc99338c37faadd28dd667d336df7409d77b4da999506a0b6b1c0aa2/rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:896c41007931217a343eff197c34513c154267636c8056fb409eafd494c3dcdc", size = 561114 }, + { url = "https://files.pythonhosted.org/packages/2b/41/65c91443685a4c7b5f1dd271beadc4a3e063d57c3269221548dd9416e15c/rpds_py-0.24.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:92558d37d872e808944c3c96d0423b8604879a3d1c86fdad508d7ed91ea547d5", size = 235548 }, +] + +[[package]] +name = "rsa" +version = "4.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 }, +] + +[[package]] +name = "safetensors" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917 }, + { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419 }, + { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493 }, + { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400 }, + { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891 }, + { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694 }, + { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642 }, + { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241 }, + { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001 }, + { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013 }, + { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687 }, + { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147 }, + { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677 }, + { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878 }, +] + +[[package]] +name = "sentry-sdk" +version = "2.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/ef/4847dcd63e3f3c451cf701a825d21200f1322d46ac97586d5c90a13dfea1/sentry_sdk-2.24.1.tar.gz", hash = "sha256:8ba3c29990fa48865b908b3b9dc5ae7fa7e72407c7c9e91303e5206b32d7b8b1", size = 318124 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/95/91137ffe7a5956496155af5ffbe45ee4ddfa795a569136147e766abd14b1/sentry_sdk-2.24.1-py2.py3-none-any.whl", hash = "sha256:36baa6a1128b9d98d2adc5e9b2f887eff0a6af558fc2b96ed51919042413556d", size = 336945 }, +] + +[[package]] +name = "setproctitle" +version = "1.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/4d/6a840c8d2baa07b57329490e7094f90aac177a1d5226bc919046f1106860/setproctitle-1.3.5.tar.gz", hash = "sha256:1e6eaeaf8a734d428a95d8c104643b39af7d247d604f40a7bebcf3960a853c5e", size = 26737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/e1/9ccff2682c38061baa07e128b60712bc18e3398aa7d5471c51a704f9d24c/setproctitle-1.3.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02870e0cb0de7f68a7a8a5b23c2bc0ce63821cab3d9b126f9be80bb6cd674c80", size = 17256 }, + { url = "https://files.pythonhosted.org/packages/ed/64/936c1f92d60052f11a8de9f90a4b7ec4996b8ebd6d67ba425ed214c80771/setproctitle-1.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55b278135be742b8901067479626d909f6613bd2d2c4fd0de6bb46f80e07a919", size = 11893 }, + { url = "https://files.pythonhosted.org/packages/01/2d/abc817b3778d9b1f7675020030379a0c39e0bf74b36af211b26191a63da3/setproctitle-1.3.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53fc971f7bf7a674f571a23cdec70f2f0ac88152c59c06aa0808d0be6d834046", size = 31295 }, + { url = "https://files.pythonhosted.org/packages/03/4d/e2055dfb1b492fd3a3b27deeaa642d81c580d48a16bc9b07afc3504af677/setproctitle-1.3.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb0500e1bc6f00b8ba696c3743ddff14c8679e3c2ca9d292c008ac51488d17cf", size = 32637 }, + { url = "https://files.pythonhosted.org/packages/89/28/a1f23d7d127dff59fe75ad671d1d5c83ab8cba10d0e343820b96d5d8a2f7/setproctitle-1.3.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:995b3ac1b5fe510f4e1d1c19ebf19f4bceb448f2d6e8d99ea23f33cb6f1a277e", size = 29772 }, + { url = "https://files.pythonhosted.org/packages/df/46/2ea4d436c7d664d41df7e60fbd3103f1139a931638e998f478e870e72255/setproctitle-1.3.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a05e2c3fdfbda32b9c9da72d0506398d1efb5bd2c5981b9e12d3622eb3d4f9", size = 30811 }, + { url = "https://files.pythonhosted.org/packages/45/60/4c17211c2d80e6fe9fa486fa3214d565d0cd9a6eff0b67e6219ddb2ba49c/setproctitle-1.3.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:310c7f4ca4c8476a9840b2cd4b22ee602a49a3c902fdcd2dd8284685abd10a9a", size = 30442 }, + { url = "https://files.pythonhosted.org/packages/7e/bf/65a8f8f2d03cd9a9429cfa0d6b22282ff7a609a4d08602bcb8351a271bec/setproctitle-1.3.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:867af4a5c3d85484fbcc50ea88bcd375acf709cff88a3259575361849c0da351", size = 29492 }, + { url = "https://files.pythonhosted.org/packages/c6/96/56f45f0b81fcc776f925c34e2699040df39cfc6b3cc7520d9b378314435b/setproctitle-1.3.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8ec0a7fe9f1ba90900144489bc93ce7dd4dec3f3df1e7f188c9e58364fe4a4c5", size = 31947 }, + { url = "https://files.pythonhosted.org/packages/ec/9d/6b697c1562b21368e579d820bca2a607e565638fd332247841eb65dec4b2/setproctitle-1.3.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:aaee7acba2733a14a886488b7495bfec4a8d6407124c04a0946dbde1684230a3", size = 29863 }, + { url = "https://files.pythonhosted.org/packages/ba/0f/4551cbb120d003fa1284ee35d559366e09b513a87dfee02f804da1936054/setproctitle-1.3.5-cp310-cp310-win32.whl", hash = "sha256:bd2cccd972e4282af4ce2c13cd9ebdf07be157eabafd8ce648fffdc8ae6fbe28", size = 11471 }, + { url = "https://files.pythonhosted.org/packages/a6/f4/2dd926687b7a3bdaa83533e2898f929e1ff3bdeb6aa271bdb1d4d5923c7e/setproctitle-1.3.5-cp310-cp310-win_amd64.whl", hash = "sha256:81f2328ac34c9584e1e5f87eea916c0bc48476a06606a07debae07acdd7ab5ea", size = 12196 }, + { url = "https://files.pythonhosted.org/packages/4a/ba/2524329ce958599069f0d0e4cfd3d6fbb7c58a4408b9e5609698e47353ec/setproctitle-1.3.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dc66b84beb0d5eb03abf0c3140c6d2cbe3d67ae9f0824a09dfa8c6ff164319a6", size = 11418 }, + { url = "https://files.pythonhosted.org/packages/a6/5f/a049640b05c609585ad0f471e667be0fd9ab533219127b455826d31587d5/setproctitle-1.3.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31dc9b330e7cac7685bdef790747c07914081c11ee1066eb0c597303dfb52010", size = 13425 }, + { url = "https://files.pythonhosted.org/packages/a9/15/caa47039e267ea67316b285e2e308ae529872ad6a143edf03a7d8edf6175/setproctitle-1.3.5-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4028639b511f5e641d116b3b54ad70c637ebd1b4baac0948283daf11b104119f", size = 13026 }, + { url = "https://files.pythonhosted.org/packages/c1/a2/1fb0647a251f4c788b94f751cf23171b2a905758fd13ef8d126222d41428/setproctitle-1.3.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6bddef4e27d0ed74e44b58bf050bc3108591bf17d20d461fc59cd141282f849c", size = 12222 }, +] + +[[package]] +name = "setuptools" +version = "78.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/5a/0db4da3bc908df06e5efae42b44e75c81dd52716e10192ff36d0c1c8e379/setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", size = 1367827 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/21/f43f0a1fa8b06b32812e0975981f4677d28e0f3271601dc88ac5a5b83220/setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8", size = 1256108 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[package]] +name = "sigtools" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/db/669ca14166814da187b3087b908ca924cf83f5b504fe23b3859a3ef67d4f/sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c", size = 71910 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/91/853dbf6ec096197dba9cd5fd0c836c5fc19142038b7db60ebe6332b1bab1/sigtools-4.0.1-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c", size = 76419 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "starlette" +version = "0.46.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/1b/52b27f2e13ceedc79a908e29eac426a63465a1a01248e5f24aa36a62aeb3/starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", size = 2580102 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 }, +] + +[[package]] +name = "sympy" +version = "1.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177 }, +] + +[[package]] +name = "synchronicity" +version = "0.9.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sigtools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/52/f34a9ab6d514e0808d0f572affb360411d596b3439107318c00889277dd6/synchronicity-0.9.11.tar.gz", hash = "sha256:cb5dbbcb43d637e516ae50db05a776da51a705d1e1a9c0e301f6049afc3c2cae", size = 50323 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/d5/7675cd9b8e18f05b9ea261acad5d197fcb8027d2a65b1a750427ec084593/synchronicity-0.9.11-py3-none-any.whl", hash = "sha256:231129654d2f56b1aa148e85ebd8545231be135771f6d2196d414175b1594ef6", size = 36827 }, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, +] + +[[package]] +name = "tenacity" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, +] + +[[package]] +name = "tiktoken" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/4a/abaec53e93e3ef37224a4dd9e2fc6bb871e7a538c2b6b9d2a6397271daf4/tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6", size = 33437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/10/28d59d43d72a0ebd4211371d0bf10c935cdecbb62b812ae04c58bfc37d96/tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f", size = 961465 }, + { url = "https://files.pythonhosted.org/packages/f8/0c/d4125348dedd1f8f38e3f85245e7fc38858ffc77c9b7edfb762a8191ba0b/tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225", size = 906849 }, + { url = "https://files.pythonhosted.org/packages/b9/ab/f9c7675747f259d133d66065106cf732a7c2bef6043062fbca8e011f7f4d/tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590", size = 1048795 }, + { url = "https://files.pythonhosted.org/packages/e7/8c/7d1007557b343d5cf18349802e94d3a14397121e9105b4661f8cd753f9bf/tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c", size = 1080866 }, + { url = "https://files.pythonhosted.org/packages/72/40/61d6354cb64a563fce475a2907039be9fe809ca5f801213856353b01a35b/tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311", size = 1092776 }, + { url = "https://files.pythonhosted.org/packages/f2/6c/83ca40527d072739f0704b9f59b325786c444ca63672a77cb69adc8181f7/tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5", size = 1142591 }, + { url = "https://files.pythonhosted.org/packages/ec/1f/a5d72755118e9e1b62cdf3ef9138eb83d49088f3cb37a9540025c81c0e75/tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702", size = 798864 }, +] + +[[package]] +name = "together" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "click" }, + { name = "eval-type-backport" }, + { name = "filelock" }, + { name = "numpy" }, + { name = "pillow" }, + { name = "pyarrow" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "rich" }, + { name = "tabulate" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/42/90cdaeac6e9db959228f77c84ab0069445302cc2e684db356417f1979052/together-1.5.4.tar.gz", hash = "sha256:b6cb28ea8d8d30723e53b2b1020b2f5924c395e24f358e948c334b049265bdfb", size = 64205 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/c5/2911b399b9ec755e8a4ba67a848bf80816ae55b6561fdeee739612d994f2/together-1.5.4-py3-none-any.whl", hash = "sha256:fef7d1b8a4b497dbe2b148750dfc2473da0cbc195390cf9194f99f17873690f3", size = 87774 }, +] + +[[package]] +name = "tokenizers" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + +[[package]] +name = "torch" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/82/adc3a77b9fbbcb79d398d565d39dc0e09f43fff088599d15da81e6cfaaec/torch-2.5.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:7f179373a047b947dec448243f4e6598a1c960fa3bb978a9a7eecd529fbc363f", size = 906443143 }, + { url = "https://files.pythonhosted.org/packages/64/b0/0d2056c8d379a3f7f0c9fa9adece180f64fd6c339e2007a4fffbea7ecaa0/torch-2.5.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:15fbc95e38d330e5b0ef1593b7bc0a19f30e5bdad76895a5cffa1a6a044235e9", size = 91839507 }, + { url = "https://files.pythonhosted.org/packages/60/41/073193dd2566012eaeae44d6c5e55ba6a9b1d5687a251f12e1804a9e2968/torch-2.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:f499212f1cffea5d587e5f06144630ed9aa9c399bba12ec8905798d833bd1404", size = 203108822 }, + { url = "https://files.pythonhosted.org/packages/93/d4/6e7bda4e52c37a78b5066e407baff2426fd4543356ead3419383a0bf4011/torch-2.5.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:c54db1fade17287aabbeed685d8e8ab3a56fea9dd8d46e71ced2da367f09a49f", size = 64283014 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "transformers" +version = "4.50.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/29/37877123d6633a188997d75dc17d6f526745d63361794348ce748db23d49/transformers-4.50.3.tar.gz", hash = "sha256:1d795d24925e615a8e63687d077e4f7348c2702eb87032286eaa76d83cdc684f", size = 8774363 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/22/733a6fc4a6445d835242f64c490fdd30f4a08d58f2b788613de3f9170692/transformers-4.50.3-py3-none-any.whl", hash = "sha256:6111610a43dec24ef32c3df0632c6b25b07d9711c01d9e1077bdd2ff6b14a38c", size = 10180411 }, +] + +[[package]] +name = "triton" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/29/69aa56dc0b2eb2602b553881e34243475ea2afd9699be042316842788ff5/triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8", size = 209460013 }, +] + +[[package]] +name = "typer" +version = "0.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6f/3991f0f1c7fcb2df31aef28e0594d8d54b05393a0e4e34c65e475c2a5d41/typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5", size = 100711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061 }, +] + +[[package]] +name = "types-certifi" +version = "2021.10.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136 }, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 }, +] + +[[package]] +name = "typing-extensions" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/3e/b00a62db91a83fff600de219b6ea9908e6918664899a2d85db222f4fbf19/typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b", size = 106520 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, +] + +[[package]] +name = "uritemplate" +version = "4.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d2/5a/4742fdba39cd02a56226815abfa72fe0aa81c33bed16ed045647d6000eba/uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0", size = 273898 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c0/7461b49cd25aeece13766f02ee576d1db528f1c37ce69aee300e075b485b/uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e", size = 10356 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "uuid-utils" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/0a/cbdb2eb4845dafeb632d02a18f47b02f87f2ce4f25266f5e3c017976ce89/uuid_utils-0.10.0.tar.gz", hash = "sha256:5db0e1890e8f008657ffe6ded4d9459af724ab114cfe82af1557c87545301539", size = 18828 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/54/9d22fa16b19e5d1676eba510f08a9c458d96e2a62ff2c8ebad64251afb18/uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d5a4508feefec62456cd6a41bcdde458d56827d908f226803b886d22a3d5e63", size = 573006 }, + { url = "https://files.pythonhosted.org/packages/08/8e/f895c6e52aa603e521fbc13b8626ba5dd99b6e2f5a55aa96ba5b232f4c53/uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dbefc2b9113f9dfe56bdae58301a2b3c53792221410d422826f3d1e3e6555fe7", size = 292543 }, + { url = "https://files.pythonhosted.org/packages/b6/58/cc4834f377a5e97d6e184408ad96d13042308de56643b6e24afe1f6f34df/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffc49c33edf87d1ec8112a9b43e4cf55326877716f929c165a2cc307d31c73d5", size = 323340 }, + { url = "https://files.pythonhosted.org/packages/37/e3/6aeddf148f6a7dd7759621b000e8c85382ec83f52ae79b60842d1dc3ab6b/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0636b6208f69d5a4e629707ad2a89a04dfa8d1023e1999181f6830646ca048a1", size = 329653 }, + { url = "https://files.pythonhosted.org/packages/0c/00/dd6c2164ace70b7b1671d9129267df331481d7d1e5f9c5e6a564f07953f6/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bc06452856b724df9dedfc161c3582199547da54aeb81915ec2ed54f92d19b0", size = 365471 }, + { url = "https://files.pythonhosted.org/packages/b4/e7/0ab8080fcae5462a7b5e555c1cef3d63457baffb97a59b9bc7b005a3ecb1/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:263b2589111c61decdd74a762e8f850c9e4386fb78d2cf7cb4dfc537054cda1b", size = 325844 }, + { url = "https://files.pythonhosted.org/packages/73/39/52d94e9ef75b03f44b39ffc6ac3167e93e74ef4d010a93d25589d9f48540/uuid_utils-0.10.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a558db48b7096de6b4d2d2210d82bba8586a6d55f99106b03bb7d01dc5c5bcd6", size = 344389 }, + { url = "https://files.pythonhosted.org/packages/7c/29/4824566f62666238290d99c62a58e4ab2a8b9cf2eccf94cebd9b3359131e/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:807465067f3c892514230326ac71a79b28a8dfe2c88ecd2d5675fc844f3c76b5", size = 510078 }, + { url = "https://files.pythonhosted.org/packages/5e/8f/bbcc7130d652462c685f0d3bd26bb214b754215b476340885a4cb50fb89a/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:57423d4a2b9d7b916de6dbd75ba85465a28f9578a89a97f7d3e098d9aa4e5d4a", size = 515937 }, + { url = "https://files.pythonhosted.org/packages/23/f8/34e0c00f5f188604d336713e6a020fcf53b10998e8ab24735a39ab076740/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:76d8d660f18ff6b767e319b1b5f927350cd92eafa4831d7ef5b57fdd1d91f974", size = 494111 }, + { url = "https://files.pythonhosted.org/packages/1a/52/b7f0066cc90a7a9c28d54061ed195cd617fde822e5d6ac3ccc88509c3c44/uuid_utils-0.10.0-cp39-abi3-win32.whl", hash = "sha256:6c11a71489338837db0b902b75e1ba7618d5d29f05fde4f68b3f909177dbc226", size = 173520 }, + { url = "https://files.pythonhosted.org/packages/8b/15/f04f58094674d333974243fb45d2c740cf4b79186fb707168e57943c84a3/uuid_utils-0.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:11c55ae64f6c0a7a0c741deae8ca2a4eaa11e9c09dbb7bec2099635696034cf7", size = 182965 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/8f3288797487c82981134732dee13b1ad12082890905476f95994ce49e0f/uuid_utils-0.10.0-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:acea543dfc7b87df749e3e814c54ac739a82ff5e3800d25bd25a3e00599e1554", size = 573053 }, + { url = "https://files.pythonhosted.org/packages/91/28/0eb5190aa39547015d60ce5453cfd37c4d87a48d25026d72044c20cad4fc/uuid_utils-0.10.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0767eefa7b1e96f06cfa9b95758d286240c01bbf19e9d8f1b6043cdbe76cc639", size = 292596 }, + { url = "https://files.pythonhosted.org/packages/e4/27/a451725d5df0db8baaa84adde94bbac4a33c3816a5215740c3f1dbdc46d3/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973fe4bb5258fd2ccb144d8b40c2d3158f16cc856a20527f8b40d14b2ae1dee9", size = 323414 }, + { url = "https://files.pythonhosted.org/packages/22/6b/0edc2ad855cbe07ffd891ec636c6ff57ae3a56cdf0e90467b2edbe5b7b43/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71b8505b67a0d77d0fbd765d8463094a8f447677125da7647bec7ea0b99406f0", size = 329720 }, + { url = "https://files.pythonhosted.org/packages/4b/1d/f73af741d9a4d3168704235ef06fbda823bf2ecf551ac29caa8d7cf8ea2a/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdcb1211bb61476cbef12a87101fa48243e20ed82b2bd324c816b1b5826bd5e", size = 365545 }, + { url = "https://files.pythonhosted.org/packages/b1/06/92104c8ea66a6d645f00520222a52c4b91a444c2c30201ff0036dedfb8da/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c5247f1df040aae71ea313819b563debe69bca7086a2cc6a3ac0eaddd3dadac", size = 325920 }, + { url = "https://files.pythonhosted.org/packages/94/fe/0710e28b94f2311b40757dc43513290134cb4579f79981127c58640d736c/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a50bd29ef89660b93aa07ffa95ac691a0e12832375030569a8bd5c9272f3b8e6", size = 344458 }, +] + +[[package]] +name = "wandb" +version = "0.19.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "docker-pycreds" }, + { name = "gitpython" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sentry-sdk" }, + { name = "setproctitle" }, + { name = "setuptools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f2/001aee271c0665afc7424c14ea2fa6fd9987d9d4e186d187cd0bac2d11db/wandb-0.19.8.tar.gz", hash = "sha256:3a4844bb38758657b94b090e72ee355fe5b926e3a048232f0ca4248f801d8d80", size = 39244743 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/79/058be304cddf78e53ebaddeefbfeec66c3d67d6f733653f9f7de48efcfe0/wandb-0.19.8-py3-none-any.whl", hash = "sha256:75dea834d579f38e0e1f857e644020e22c851f9b920e9c6c6345bacb98c3f3fc", size = 6305883 }, + { url = "https://files.pythonhosted.org/packages/3c/df/e8e0ec80afd0a437e3ddc10da3e2286d9bab2169b48fd0f768a455d49971/wandb-0.19.8-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:6556147ba33b7ff4a0111bb6bf5ea485e4974c22f520f1e2a5eaad670a058c80", size = 20474304 }, + { url = "https://files.pythonhosted.org/packages/9a/6e/171701d80f0f20e53c74e8e0ecab06c31a59d53cab295ec108ac39140fef/wandb-0.19.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f68517c2059d12912a90ae32ce95a2711e39f6c157c759eb191527739a12db8b", size = 19942528 }, + { url = "https://files.pythonhosted.org/packages/59/24/24720683f6b9c19dd41b081e32d4585dc9a2f1e2d0b7a9cb63cde690868e/wandb-0.19.8-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:96cb534b19c2d301ac4fb0e7cfbc32198a704e29e87337133d6b71fdad33cf2f", size = 20471015 }, + { url = "https://files.pythonhosted.org/packages/22/0a/a9f6dcc96a6ee7cd5365af3a8e4b896cd373e4a11cbb1468b6d9aaac37f3/wandb-0.19.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1781b36434d494d6b34e2149201bae8cab960cb31571f11b981c4a62462d5af8", size = 19460731 }, + { url = "https://files.pythonhosted.org/packages/e0/71/7b7050ecab7288782ae0c7560f1ca06f4cf854a5ae08abeaf643785af1a0/wandb-0.19.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c25f0e40025b838b7a424b51837a2a5fd071686c59e1c46d73f04e760d305f79", size = 20792273 }, + { url = "https://files.pythonhosted.org/packages/45/54/8b6f1f41cf4a8b67439d4f0842de80084709cad2939152503046b42d863c/wandb-0.19.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:068eb0154f80be973ab291346d831e9cc80a9de1b8752bdeb48a997c3506fec4", size = 19470793 }, + { url = "https://files.pythonhosted.org/packages/d7/bb/28d94b0369f0055dc4aef704971858a414490f6eb23b9bbfa70d090f4b59/wandb-0.19.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:82a956150e53df0b4c193933b3e62c3e8255dc8b43bb187270939ef35b03fda3", size = 20872380 }, + { url = "https://files.pythonhosted.org/packages/80/82/9d653fe043d48075342bed7a545611391fc62095fb1e77d6574a8f2091e3/wandb-0.19.8-py3-none-win32.whl", hash = "sha256:9d71f153cb9330e307b1b054be01971a1bd164fb9bd4190d7f57989c2d6b86e8", size = 20165481 }, + { url = "https://files.pythonhosted.org/packages/b6/90/038a64abcbe5f991468f057bd21bead84a5c39d9b0409b652893263a47b4/wandb-0.19.8-py3-none-win_amd64.whl", hash = "sha256:f7da8e6fc6693014c72fb7db3ecd5e1116066198d2aca96f6eb7220cea03081c", size = 20165486 }, +] + +[[package]] +name = "watchfiles" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/02/22fcaed0396730b0d362bc8d1ffb3be2658fd473eecbb2ba84243e157f11/watchfiles-1.0.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ba5bb3073d9db37c64520681dd2650f8bd40902d991e7b4cfaeece3e32561d08", size = 395212 }, + { url = "https://files.pythonhosted.org/packages/e9/3d/ec5a2369a46edf3ebe092c39d9ae48e8cb6dacbde51c4b4f98936c524269/watchfiles-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f25d0ba0fe2b6d2c921cf587b2bf4c451860086534f40c384329fb96e2044d1", size = 384815 }, + { url = "https://files.pythonhosted.org/packages/df/b4/898991cececbe171e67142c31905510203649569d9817848f47c4177ee42/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47eb32ef8c729dbc4f4273baece89398a4d4b5d21a1493efea77a17059f4df8a", size = 450680 }, + { url = "https://files.pythonhosted.org/packages/58/f7/d4aa3000e812cfb5e5c2c6c0a3ec9d0a46a42489a8727edd160631c4e210/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:076f293100db3b0b634514aa0d294b941daa85fc777f9c698adb1009e5aca0b1", size = 455923 }, + { url = "https://files.pythonhosted.org/packages/dd/95/7e2e4c6aba1b02fb5c76d2f6a450b85215921ec5f8f7ad5efd075369563f/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1eacd91daeb5158c598fe22d7ce66d60878b6294a86477a4715154990394c9b3", size = 482339 }, + { url = "https://files.pythonhosted.org/packages/bb/67/4265b0fabcc2ef2c9e3e8802ba7908cf718a357ebfb49c72e53787156a48/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13c2ce7b72026cfbca120d652f02c7750f33b4c9395d79c9790b27f014c8a5a2", size = 519908 }, + { url = "https://files.pythonhosted.org/packages/0d/96/b57802d5f8164bdf070befb4fd3dec4edba5a364ec0670965a97eb8098ce/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90192cdc15ab7254caa7765a98132a5a41471cf739513cc9bcf7d2ffcc0ec7b2", size = 501410 }, + { url = "https://files.pythonhosted.org/packages/8b/18/6db0de4e8911ba14e31853201b40c0fa9fea5ecf3feb86b0ad58f006dfc3/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278aaa395f405972e9f523bd786ed59dfb61e4b827856be46a42130605fd0899", size = 452876 }, + { url = "https://files.pythonhosted.org/packages/df/df/092a961815edf723a38ba2638c49491365943919c3526cc9cf82c42786a6/watchfiles-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a462490e75e466edbb9fc4cd679b62187153b3ba804868452ef0577ec958f5ff", size = 615353 }, + { url = "https://files.pythonhosted.org/packages/f3/cf/b85fe645de4ff82f3f436c5e9032379fce37c303f6396a18f9726cc34519/watchfiles-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8d0d0630930f5cd5af929040e0778cf676a46775753e442a3f60511f2409f48f", size = 613187 }, + { url = "https://files.pythonhosted.org/packages/f6/d4/a9fea27aef4dd69689bc3556718c1157a7accb72aa035ece87c1fa8483b5/watchfiles-1.0.4-cp310-cp310-win32.whl", hash = "sha256:cc27a65069bcabac4552f34fd2dce923ce3fcde0721a16e4fb1b466d63ec831f", size = 270799 }, + { url = "https://files.pythonhosted.org/packages/df/02/dbe9d4439f15dd4ad0720b6e039bde9d66d1f830331f34c18eb70fa6608e/watchfiles-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:8b1f135238e75d075359cf506b27bf3f4ca12029c47d3e769d8593a2024ce161", size = 284145 }, + { url = "https://files.pythonhosted.org/packages/6f/06/175d5ac6b838fb319008c0cd981d7bf289317c510154d411d3584ca2b67b/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdcc92daeae268de1acf5b7befcd6cfffd9a047098199056c72e4623f531de18", size = 396269 }, + { url = "https://files.pythonhosted.org/packages/86/ee/5db93b0b57dc0587abdbac4149296ee73275f615d790a82cb5598af0557f/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8d3d9203705b5797f0af7e7e5baa17c8588030aaadb7f6a86107b7247303817", size = 386010 }, + { url = "https://files.pythonhosted.org/packages/75/61/fe0dc5fedf152bfc085a53711f740701f6bdb8ab6b5c950402b681d4858b/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdef5a1be32d0b07dcea3318a0be95d42c98ece24177820226b56276e06b63b0", size = 450913 }, + { url = "https://files.pythonhosted.org/packages/9f/dd/3c7731af3baf1a9957afc643d176f94480921a690ec3237c9f9d11301c08/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342622287b5604ddf0ed2d085f3a589099c9ae8b7331df3ae9845571586c4f3d", size = 453474 }, +] + +[[package]] +name = "weave" +version = "0.51.39" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "diskcache" }, + { name = "emoji" }, + { name = "gql", extra = ["aiohttp", "requests"] }, + { name = "jsonschema" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "rich" }, + { name = "tenacity" }, + { name = "uuid-utils" }, + { name = "wandb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/89/44633cc13c25aeec0edfddf65c4c439ca6d85d6cfc79656afdaabd2fd9f9/weave-0.51.39.tar.gz", hash = "sha256:011e0886edcd50e3ae36256ede78ca51d69e37417313e228cb673947cbecd3c8", size = 325182 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/05/43dbe7cd40fe2b7efb5b7c02afa49e4009c8cf4f48209e9c18567a5a885b/weave-0.51.39-py3-none-any.whl", hash = "sha256:1d6778556998fc6d6f62b7aba6a9a4a201adcfbdf18c2b5e11ec50180186cbcd", size = 417269 }, +] + +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 }, +] + +[[package]] +name = "xxhash" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/8a/0e9feca390d512d293afd844d31670e25608c4a901e10202aa98785eab09/xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212", size = 31970 }, + { url = "https://files.pythonhosted.org/packages/16/e6/be5aa49580cd064a18200ab78e29b88b1127e1a8c7955eb8ecf81f2626eb/xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520", size = 30801 }, + { url = "https://files.pythonhosted.org/packages/20/ee/b8a99ebbc6d1113b3a3f09e747fa318c3cde5b04bd9c197688fadf0eeae8/xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680", size = 220927 }, + { url = "https://files.pythonhosted.org/packages/58/62/15d10582ef159283a5c2b47f6d799fc3303fe3911d5bb0bcc820e1ef7ff4/xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da", size = 200360 }, + { url = "https://files.pythonhosted.org/packages/23/41/61202663ea9b1bd8e53673b8ec9e2619989353dba8cfb68e59a9cbd9ffe3/xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23", size = 428528 }, + { url = "https://files.pythonhosted.org/packages/f2/07/d9a3059f702dec5b3b703737afb6dda32f304f6e9da181a229dafd052c29/xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196", size = 194149 }, + { url = "https://files.pythonhosted.org/packages/eb/58/27caadf78226ecf1d62dbd0c01d152ed381c14c1ee4ad01f0d460fc40eac/xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c", size = 207703 }, + { url = "https://files.pythonhosted.org/packages/b1/08/32d558ce23e1e068453c39aed7b3c1cdc690c177873ec0ca3a90d5808765/xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482", size = 216255 }, + { url = "https://files.pythonhosted.org/packages/3f/d4/2b971e2d2b0a61045f842b622ef11e94096cf1f12cd448b6fd426e80e0e2/xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296", size = 202744 }, + { url = "https://files.pythonhosted.org/packages/19/ae/6a6438864a8c4c39915d7b65effd85392ebe22710412902487e51769146d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415", size = 210115 }, + { url = "https://files.pythonhosted.org/packages/48/7d/b3c27c27d1fc868094d02fe4498ccce8cec9fcc591825c01d6bcb0b4fc49/xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198", size = 414247 }, + { url = "https://files.pythonhosted.org/packages/a1/05/918f9e7d2fbbd334b829997045d341d6239b563c44e683b9a7ef8fe50f5d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442", size = 191419 }, + { url = "https://files.pythonhosted.org/packages/08/29/dfe393805b2f86bfc47c290b275f0b7c189dc2f4e136fd4754f32eb18a8d/xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da", size = 30114 }, + { url = "https://files.pythonhosted.org/packages/7b/d7/aa0b22c4ebb7c3ccb993d4c565132abc641cd11164f8952d89eb6a501909/xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9", size = 30003 }, + { url = "https://files.pythonhosted.org/packages/69/12/f969b81541ee91b55f1ce469d7ab55079593c80d04fd01691b550e535000/xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6", size = 26773 }, + { url = "https://files.pythonhosted.org/packages/ab/9a/233606bada5bd6f50b2b72c45de3d9868ad551e83893d2ac86dc7bb8553a/xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c", size = 29732 }, + { url = "https://files.pythonhosted.org/packages/0c/67/f75276ca39e2c6604e3bee6c84e9db8a56a4973fde9bf35989787cf6e8aa/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986", size = 36214 }, + { url = "https://files.pythonhosted.org/packages/0f/f8/f6c61fd794229cc3848d144f73754a0c107854372d7261419dcbbd286299/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6", size = 32020 }, + { url = "https://files.pythonhosted.org/packages/79/d3/c029c99801526f859e6b38d34ab87c08993bf3dcea34b11275775001638a/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b", size = 40515 }, + { url = "https://files.pythonhosted.org/packages/62/e3/bef7b82c1997579c94de9ac5ea7626d01ae5858aa22bf4fcb38bf220cb3e/xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da", size = 30064 }, +] + +[[package]] +name = "yarl" +version = "1.18.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/98/e005bc608765a8a5569f58e650961314873c8469c333616eb40bff19ae97/yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34", size = 141458 }, + { url = "https://files.pythonhosted.org/packages/df/5d/f8106b263b8ae8a866b46d9be869ac01f9b3fb7f2325f3ecb3df8003f796/yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7", size = 94365 }, + { url = "https://files.pythonhosted.org/packages/56/3e/d8637ddb9ba69bf851f765a3ee288676f7cf64fb3be13760c18cbc9d10bd/yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed", size = 92181 }, + { url = "https://files.pythonhosted.org/packages/76/f9/d616a5c2daae281171de10fba41e1c0e2d8207166fc3547252f7d469b4e1/yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde", size = 315349 }, + { url = "https://files.pythonhosted.org/packages/bb/b4/3ea5e7b6f08f698b3769a06054783e434f6d59857181b5c4e145de83f59b/yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b", size = 330494 }, + { url = "https://files.pythonhosted.org/packages/55/f1/e0fc810554877b1b67420568afff51b967baed5b53bcc983ab164eebf9c9/yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5", size = 326927 }, + { url = "https://files.pythonhosted.org/packages/a9/42/b1753949b327b36f210899f2dd0a0947c0c74e42a32de3f8eb5c7d93edca/yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc", size = 319703 }, + { url = "https://files.pythonhosted.org/packages/f0/6d/e87c62dc9635daefb064b56f5c97df55a2e9cc947a2b3afd4fd2f3b841c7/yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd", size = 310246 }, + { url = "https://files.pythonhosted.org/packages/e3/ef/e2e8d1785cdcbd986f7622d7f0098205f3644546da7919c24b95790ec65a/yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990", size = 319730 }, + { url = "https://files.pythonhosted.org/packages/fc/15/8723e22345bc160dfde68c4b3ae8b236e868f9963c74015f1bc8a614101c/yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db", size = 321681 }, + { url = "https://files.pythonhosted.org/packages/86/09/bf764e974f1516efa0ae2801494a5951e959f1610dd41edbfc07e5e0f978/yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62", size = 324812 }, + { url = "https://files.pythonhosted.org/packages/f6/4c/20a0187e3b903c97d857cf0272d687c1b08b03438968ae8ffc50fe78b0d6/yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760", size = 337011 }, + { url = "https://files.pythonhosted.org/packages/c9/71/6244599a6e1cc4c9f73254a627234e0dad3883ece40cc33dce6265977461/yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b", size = 338132 }, + { url = "https://files.pythonhosted.org/packages/af/f5/e0c3efaf74566c4b4a41cb76d27097df424052a064216beccae8d303c90f/yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690", size = 331849 }, + { url = "https://files.pythonhosted.org/packages/8a/b8/3d16209c2014c2f98a8f658850a57b716efb97930aebf1ca0d9325933731/yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6", size = 84309 }, + { url = "https://files.pythonhosted.org/packages/fd/b7/2e9a5b18eb0fe24c3a0e8bae994e812ed9852ab4fd067c0107fadde0d5f0/yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8", size = 90484 }, + { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +]