akseljoonas HF Staff commited on
Commit
3da9761
·
1 Parent(s): 78e49c7

adding hf datasets i/o

Browse files
Files changed (5) hide show
  1. eval/evaluate.py +25 -2
  2. eval/generate_rubrics.py +43 -15
  3. eval/hf_dataset_io.py +517 -0
  4. pyproject.toml +2 -0
  5. uv.lock +185 -0
eval/evaluate.py CHANGED
@@ -9,6 +9,8 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
9
  from typing import Dict, List, Optional
10
 
11
  import litellm
 
 
12
  from pydantic import BaseModel
13
 
14
 
@@ -190,6 +192,7 @@ def evaluate_dataset_with_rubrics(
190
  model: str = "gpt-4o-mini",
191
  max_concurrent: int = 10,
192
  limit: Optional[int] = None,
 
193
  ) -> None:
194
  """
195
  Evaluate all responses using rubric-based assessment.
@@ -202,6 +205,7 @@ def evaluate_dataset_with_rubrics(
202
  model: LLM model for judging
203
  max_concurrent: Maximum concurrent evaluations
204
  limit: Optional limit on number of examples
 
205
  """
206
  # Load data
207
  print(f"Loading responses from {input_file}...")
@@ -293,8 +297,23 @@ def evaluate_dataset_with_rubrics(
293
  output_data.append(evaluated_response)
294
  total_score += evaluation.normalized_score
295
 
296
- # Write results
297
- print(f"Writing results to {output_file}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  with open(output_file, "w") as f:
299
  for entry in output_data:
300
  f.write(entry.model_dump_json() + "\n")
@@ -319,6 +338,9 @@ def evaluate_dataset_with_rubrics(
319
  satisfaction_rate = total_satisfied / total_criteria if total_criteria > 0 else 0.0
320
  print(f"Criteria satisfaction rate: {satisfaction_rate * 100:.1f}%")
321
 
 
 
 
322
  print("=" * 60)
323
 
324
 
@@ -331,4 +353,5 @@ if __name__ == "__main__":
331
  model="gpt-4o-mini",
332
  max_concurrent=10,
333
  limit=30, # Set to None to evaluate all
 
334
  )
 
9
  from typing import Dict, List, Optional
10
 
11
  import litellm
12
+ import pandas as pd
13
+ from hf_dataset_io import df_to_hub
14
  from pydantic import BaseModel
15
 
16
 
 
192
  model: str = "gpt-4o-mini",
193
  max_concurrent: int = 10,
194
  limit: Optional[int] = None,
195
+ push_to_hub: Optional[str] = None,
196
  ) -> None:
197
  """
198
  Evaluate all responses using rubric-based assessment.
 
205
  model: LLM model for judging
206
  max_concurrent: Maximum concurrent evaluations
207
  limit: Optional limit on number of examples
208
+ push_to_hub: Optional HuggingFace dataset spec (e.g., username/dataset@evaluations)
209
  """
210
  # Load data
211
  print(f"Loading responses from {input_file}...")
 
297
  output_data.append(evaluated_response)
298
  total_score += evaluation.normalized_score
299
 
300
+ # Convert to DataFrame for HuggingFace upload
301
+ results_df = pd.DataFrame([entry.model_dump() for entry in output_data])
302
+
303
+ # Upload to HuggingFace if specified (before saving JSONL)
304
+ if push_to_hub:
305
+ print(f"\nUploading to HuggingFace: {push_to_hub}")
306
+ upload_success = df_to_hub(
307
+ df=results_df,
308
+ dataset_spec=push_to_hub,
309
+ split="test",
310
+ private=False,
311
+ )
312
+ if not upload_success:
313
+ print("Warning: HuggingFace upload failed, but continuing to save JSONL...")
314
+
315
+ # Write results to JSONL file
316
+ print(f"\nWriting results to {output_file}...")
317
  with open(output_file, "w") as f:
318
  for entry in output_data:
319
  f.write(entry.model_dump_json() + "\n")
 
338
  satisfaction_rate = total_satisfied / total_criteria if total_criteria > 0 else 0.0
339
  print(f"Criteria satisfaction rate: {satisfaction_rate * 100:.1f}%")
340
 
341
+ if push_to_hub and upload_success:
342
+ print(f"Pushed to: {push_to_hub}")
343
+
344
  print("=" * 60)
345
 
346
 
 
353
  model="gpt-4o-mini",
354
  max_concurrent=10,
355
  limit=30, # Set to None to evaluate all
356
+ push_to_hub="akseljoonas/hf-agent-benchmark@ground-truth", # Set to "username/dataset@evaluations" to upload
357
  )
eval/generate_rubrics.py CHANGED
@@ -17,6 +17,7 @@ from typing import Any, Dict, List
17
  import litellm
18
  import pandas as pd
19
  from dotenv import load_dotenv
 
20
  from pydantic import BaseModel
21
 
22
 
@@ -246,6 +247,12 @@ def main():
246
  default=30,
247
  help="Maximum number of concurrent workers (default: 30)",
248
  )
 
 
 
 
 
 
249
 
250
  args = parser.parse_args()
251
 
@@ -284,31 +291,52 @@ def main():
284
 
285
  print() # New line after progress
286
 
287
- # Write results to file
288
- print(f"Writing results to {args.outfile}...")
 
289
  success_count = 0
290
  failure_count = 0
291
 
292
- with open(args.outfile, "w") as outf:
293
- for idx, (_, row) in enumerate(df.iterrows()):
294
- rubric_result = results[idx]
295
-
296
- if rubric_result is None:
297
- failure_count += 1
298
- continue
299
-
300
- # Merge with original data
301
- output_row = row.to_dict()
302
- output_row["rubric"] = rubric_result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
 
304
- # Write JSONL line
 
 
 
305
  outf.write(json.dumps(output_row, default=str) + "\n")
306
- success_count += 1
307
 
308
  print("\nComplete!")
309
  print(f"Success: {success_count}/{len(df)}")
310
  print(f"Failures: {failure_count}/{len(df)}")
311
  print(f"Output written to: {args.outfile}")
 
 
312
 
313
 
314
  if __name__ == "__main__":
 
17
  import litellm
18
  import pandas as pd
19
  from dotenv import load_dotenv
20
+ from hf_dataset_io import df_to_hub
21
  from pydantic import BaseModel
22
 
23
 
 
247
  default=30,
248
  help="Maximum number of concurrent workers (default: 30)",
249
  )
250
+ parser.add_argument(
251
+ "--push-to-hub",
252
+ type=str,
253
+ default=None,
254
+ help="Push to HuggingFace dataset (e.g., username/dataset@rubrics)",
255
+ )
256
 
257
  args = parser.parse_args()
258
 
 
291
 
292
  print() # New line after progress
293
 
294
+ # Prepare results DataFrame
295
+ print("Preparing results...")
296
+ output_rows = []
297
  success_count = 0
298
  failure_count = 0
299
 
300
+ for idx, (_, row) in enumerate(df.iterrows()):
301
+ rubric_result = results[idx]
302
+
303
+ if rubric_result is None:
304
+ failure_count += 1
305
+ continue
306
+
307
+ # Merge with original data
308
+ output_row = row.to_dict()
309
+ output_row["rubric"] = rubric_result
310
+ output_rows.append(output_row)
311
+ success_count += 1
312
+
313
+ # Create DataFrame with results
314
+ results_df = pd.DataFrame(output_rows)
315
+
316
+ # Upload to HuggingFace if specified (before saving JSONL)
317
+ if args.push_to_hub:
318
+ print(f"\nUploading to HuggingFace: {args.push_to_hub}")
319
+ upload_success = df_to_hub(
320
+ df=results_df,
321
+ dataset_spec=args.push_to_hub,
322
+ split="train",
323
+ private=False,
324
+ )
325
+ if not upload_success:
326
+ print("Warning: HuggingFace push failed, but continuing to save JSONL...")
327
 
328
+ # Write results to JSONL file
329
+ print(f"\nWriting results to {args.outfile}...")
330
+ with open(args.outfile, "w") as outf:
331
+ for output_row in output_rows:
332
  outf.write(json.dumps(output_row, default=str) + "\n")
 
333
 
334
  print("\nComplete!")
335
  print(f"Success: {success_count}/{len(df)}")
336
  print(f"Failures: {failure_count}/{len(df)}")
337
  print(f"Output written to: {args.outfile}")
338
+ if args.push_to_hub and upload_success:
339
+ print(f"Pushed to: {args.push_to_hub}")
340
 
341
 
342
  if __name__ == "__main__":
eval/hf_dataset_io.py ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HuggingFace Dataset I/O Utilities
3
+
4
+ Reusable functions for uploading and downloading JSONL data to/from HuggingFace Hub.
5
+ Supports the dataset_name@config_name notation for managing multiple configurations.
6
+ """
7
+
8
+ import json
9
+ from pathlib import Path
10
+ from typing import Dict, List, Optional, Union
11
+
12
+ import pandas as pd
13
+ from datasets import Dataset, load_dataset
14
+
15
+
16
+ def upload_jsonl_to_hf(
17
+ jsonl_file: Union[str, Path],
18
+ dataset_spec: str,
19
+ split: str = "train",
20
+ private: bool = False,
21
+ ) -> bool:
22
+ """
23
+ Upload a JSONL file to HuggingFace Hub as a dataset.
24
+
25
+ This function reads a JSONL file where each line is a complete JSON object,
26
+ converts it to a HuggingFace Dataset, and uploads it to the Hub.
27
+
28
+ Args:
29
+ jsonl_file: Path to the JSONL file to upload. Each line should be a valid
30
+ JSON object. Example format:
31
+ ```
32
+ {"question": "How to...", "solution": "...", "rubric": "[...]"}
33
+ {"question": "What is...", "solution": "...", "rubric": "[...]"}
34
+ ```
35
+
36
+ dataset_spec: Dataset specification in the format "dataset_name" or
37
+ "dataset_name@config_name". Examples:
38
+ - "username/my-dataset" (uses "default" config)
39
+ - "username/my-dataset@rubrics" (uses "rubrics" config)
40
+ - "username/my-dataset@evaluations" (uses "evaluations" config)
41
+
42
+ Multiple configs allow you to store different data types in the same
43
+ dataset repository (e.g., raw data, rubrics, evaluation results).
44
+
45
+ split: The dataset split name. Defaults to "train". Common values:
46
+ - "train": Training or main data
47
+ - "validation": Validation data
48
+ - "test": Test data
49
+
50
+ private: Whether to create a private dataset. Defaults to False (public).
51
+
52
+ Returns:
53
+ bool: True if upload succeeded, False otherwise
54
+
55
+ Raises:
56
+ FileNotFoundError: If the JSONL file doesn't exist
57
+ ValueError: If the JSONL file is empty or contains invalid JSON
58
+ Exception: For HuggingFace Hub upload errors
59
+
60
+ Example:
61
+ >>> # Upload rubrics with custom config
62
+ >>> upload_jsonl_to_hf(
63
+ ... "qa_rubrics.jsonl",
64
+ ... "username/hf-agent-benchmark@rubrics",
65
+ ... split="train"
66
+ ... )
67
+
68
+ >>> # Upload evaluation results with different config
69
+ >>> upload_jsonl_to_hf(
70
+ ... "evaluation_results.jsonl",
71
+ ... "username/hf-agent-benchmark@evaluations",
72
+ ... split="test"
73
+ ... )
74
+
75
+ Notes:
76
+ - Requires authentication via `huggingface-cli login` or HF_TOKEN env var
77
+ - If the dataset doesn't exist, it will be created automatically
78
+ - If it exists, the specified config/split will be updated
79
+ - Empty files will raise ValueError to prevent uploading invalid data
80
+ """
81
+ jsonl_path = Path(jsonl_file)
82
+
83
+ # Validate file exists
84
+ if not jsonl_path.exists():
85
+ raise FileNotFoundError(f"JSONL file not found: {jsonl_file}")
86
+
87
+ # Parse dataset specification
88
+ if "@" in dataset_spec:
89
+ dataset_name, config_name = dataset_spec.split("@", 1)
90
+ else:
91
+ dataset_name = dataset_spec
92
+ config_name = "default"
93
+
94
+ try:
95
+ print(f"\nUploading {jsonl_path.name} to HuggingFace Hub...")
96
+ print(f" Dataset: {dataset_name}")
97
+ print(f" Config: {config_name}")
98
+ print(f" Split: {split}")
99
+
100
+ # Load JSONL file
101
+ records = []
102
+ with open(jsonl_path, "r") as f:
103
+ for line_num, line in enumerate(f, start=1):
104
+ line = line.strip()
105
+ if line: # Skip empty lines
106
+ try:
107
+ records.append(json.loads(line))
108
+ except json.JSONDecodeError as e:
109
+ raise ValueError(f"Invalid JSON on line {line_num}: {e}") from e
110
+
111
+ if not records:
112
+ raise ValueError("JSONL file is empty or contains no valid records")
113
+
114
+ print(f" Loaded {len(records)} records from JSONL")
115
+
116
+ # Create HuggingFace Dataset
117
+ dataset = Dataset.from_list(records)
118
+
119
+ # Upload to HuggingFace Hub
120
+ dataset.push_to_hub(
121
+ dataset_name,
122
+ config_name=config_name,
123
+ split=split,
124
+ private=private,
125
+ )
126
+
127
+ print(
128
+ f"✓ Successfully uploaded to {dataset_name}@{config_name} (split: {split})"
129
+ )
130
+ return True
131
+
132
+ except Exception as e:
133
+ print(f"✗ Failed to upload to HuggingFace: {type(e).__name__}: {str(e)}")
134
+ print(f" JSONL file preserved at: {jsonl_path}")
135
+ return False
136
+
137
+
138
+ def download_hf_to_jsonl(
139
+ dataset_spec: str,
140
+ output_file: Union[str, Path],
141
+ split: str = "train",
142
+ overwrite: bool = False,
143
+ ) -> bool:
144
+ """
145
+ Download a dataset from HuggingFace Hub and save as JSONL.
146
+
147
+ This function downloads a dataset from the HuggingFace Hub and saves it as a
148
+ JSONL file where each line is a complete JSON object.
149
+
150
+ Args:
151
+ dataset_spec: Dataset specification in the format "dataset_name" or
152
+ "dataset_name@config_name". Examples:
153
+ - "username/my-dataset" (uses "default" config)
154
+ - "username/my-dataset@rubrics" (uses "rubrics" config)
155
+ - "username/my-dataset@evaluations" (uses "evaluations" config)
156
+
157
+ output_file: Path where the JSONL file will be saved. Will create parent
158
+ directories if they don't exist. Example: "data/downloaded_rubrics.jsonl"
159
+
160
+ split: The dataset split to download. Defaults to "train". Common values:
161
+ - "train": Training or main data
162
+ - "validation": Validation data
163
+ - "test": Test data
164
+ - "all": Download all splits (creates one JSONL with all data)
165
+
166
+ overwrite: Whether to overwrite existing file. Defaults to False.
167
+
168
+ Returns:
169
+ bool: True if download succeeded, False otherwise
170
+
171
+ Raises:
172
+ FileExistsError: If output file exists and overwrite=False
173
+ ValueError: If the dataset/config/split doesn't exist
174
+ Exception: For HuggingFace Hub download errors
175
+
176
+ Example:
177
+ >>> # Download rubrics from specific config
178
+ >>> download_hf_to_jsonl(
179
+ ... "username/hf-agent-benchmark@rubrics",
180
+ ... "local_rubrics.jsonl",
181
+ ... split="train"
182
+ ... )
183
+
184
+ >>> # Download evaluation results
185
+ >>> download_hf_to_jsonl(
186
+ ... "username/hf-agent-benchmark@evaluations",
187
+ ... "local_evaluations.jsonl",
188
+ ... split="test",
189
+ ... overwrite=True
190
+ ... )
191
+
192
+ Notes:
193
+ - Requires authentication for private datasets via `huggingface-cli login`
194
+ - Downloaded data will be in the same format as uploaded (preserves structure)
195
+ - Each line in the output JSONL is a complete, valid JSON object
196
+ - Large datasets may take time to download
197
+ """
198
+ output_path = Path(output_file)
199
+
200
+ # Check if file exists
201
+ if output_path.exists() and not overwrite:
202
+ raise FileExistsError(
203
+ f"Output file already exists: {output_file}. "
204
+ "Use overwrite=True to replace it."
205
+ )
206
+
207
+ # Parse dataset specification
208
+ if "@" in dataset_spec:
209
+ dataset_name, config_name = dataset_spec.split("@", 1)
210
+ else:
211
+ dataset_name = dataset_spec
212
+ config_name = "default"
213
+
214
+ try:
215
+ print("\nDownloading from HuggingFace Hub...")
216
+ print(f" Dataset: {dataset_name}")
217
+ print(f" Config: {config_name}")
218
+ print(f" Split: {split}")
219
+
220
+ # Download dataset from HuggingFace Hub
221
+ dataset = load_dataset(
222
+ dataset_name,
223
+ name=config_name,
224
+ split=split,
225
+ )
226
+
227
+ print(f" Downloaded {len(dataset)} records")
228
+
229
+ # Create parent directories if needed
230
+ output_path.parent.mkdir(parents=True, exist_ok=True)
231
+
232
+ # Write to JSONL
233
+ with open(output_path, "w") as f:
234
+ for record in dataset:
235
+ # Convert record to JSON and write as line
236
+ f.write(json.dumps(record) + "\n")
237
+
238
+ print(f"✓ Successfully saved to {output_path}")
239
+ print(f" Total records: {len(dataset)}")
240
+ return True
241
+
242
+ except Exception as e:
243
+ print(f"✗ Failed to download from HuggingFace: {type(e).__name__}: {str(e)}")
244
+ return False
245
+
246
+
247
+ def list_dataset_configs(dataset_name: str) -> Optional[List[str]]:
248
+ """
249
+ List all available configs for a dataset on HuggingFace Hub.
250
+
251
+ Args:
252
+ dataset_name: Name of the dataset (e.g., "username/my-dataset")
253
+
254
+ Returns:
255
+ List of config names, or None if unable to retrieve
256
+
257
+ Example:
258
+ >>> configs = list_dataset_configs("username/hf-agent-benchmark")
259
+ >>> print(configs)
260
+ ['default', 'rubrics', 'evaluations']
261
+ """
262
+ try:
263
+ from datasets import get_dataset_config_names
264
+
265
+ configs = get_dataset_config_names(dataset_name)
266
+ return configs
267
+ except Exception as e:
268
+ print(f"✗ Failed to list configs: {type(e).__name__}: {str(e)}")
269
+ return None
270
+
271
+
272
+ def get_dataset_info(dataset_spec: str, split: str = "train") -> Optional[Dict]:
273
+ """
274
+ Get information about a dataset on HuggingFace Hub.
275
+
276
+ Args:
277
+ dataset_spec: Dataset specification ("dataset_name" or "dataset_name@config")
278
+ split: The split to get info for (default: "train")
279
+
280
+ Returns:
281
+ Dictionary with dataset info, or None if unable to retrieve
282
+
283
+ Example:
284
+ >>> info = get_dataset_info("username/hf-agent-benchmark@rubrics")
285
+ >>> print(f"Records: {info['num_rows']}")
286
+ >>> print(f"Columns: {info['column_names']}")
287
+ """
288
+ # Parse dataset specification
289
+ if "@" in dataset_spec:
290
+ dataset_name, config_name = dataset_spec.split("@", 1)
291
+ else:
292
+ dataset_name = dataset_spec
293
+ config_name = "default"
294
+
295
+ try:
296
+ # Load just to get info (streaming mode for efficiency)
297
+ dataset = load_dataset(
298
+ dataset_name,
299
+ name=config_name,
300
+ split=split,
301
+ streaming=True,
302
+ )
303
+
304
+ # Get basic info
305
+ info = {
306
+ "dataset_name": dataset_name,
307
+ "config_name": config_name,
308
+ "split": split,
309
+ "features": str(dataset.features),
310
+ "column_names": dataset.column_names
311
+ if hasattr(dataset, "column_names")
312
+ else None,
313
+ }
314
+
315
+ # Try to get row count (only works for non-streaming)
316
+ dataset_full = load_dataset(dataset_name, name=config_name, split=split)
317
+ info["num_rows"] = len(dataset_full)
318
+
319
+ return info
320
+
321
+ except Exception as e:
322
+ print(f"✗ Failed to get dataset info: {type(e).__name__}: {str(e)}")
323
+ return None
324
+
325
+
326
+ def df_to_hub(
327
+ df: pd.DataFrame,
328
+ dataset_spec: str,
329
+ split: str = "train",
330
+ private: bool = False,
331
+ ) -> bool:
332
+ """
333
+ Upload a pandas DataFrame directly to HuggingFace Hub as a dataset.
334
+
335
+ This function converts a pandas DataFrame to a HuggingFace Dataset and uploads
336
+ it to the Hub. This is useful for uploading data directly without creating an
337
+ intermediate JSONL file.
338
+
339
+ Args:
340
+ df: pandas DataFrame to upload. All column types should be serializable.
341
+ Example DataFrame:
342
+ ```
343
+ | question | solution | rubric |
344
+ |----------|----------|--------|
345
+ | "How..." | "You..." | {...} |
346
+ ```
347
+
348
+ dataset_spec: Dataset specification in the format "dataset_name" or
349
+ "dataset_name@config_name". Examples:
350
+ - "username/my-dataset" (uses "default" config)
351
+ - "username/my-dataset@rubrics" (uses "rubrics" config)
352
+ - "username/my-dataset@evaluations" (uses "evaluations" config)
353
+
354
+ split: The dataset split name. Defaults to "train". Common values:
355
+ - "train": Training or main data
356
+ - "validation": Validation data
357
+ - "test": Test data
358
+
359
+ private: Whether to create a private dataset. Defaults to False (public).
360
+
361
+ Returns:
362
+ bool: True if upload succeeded, False otherwise
363
+
364
+ Raises:
365
+ ValueError: If DataFrame is empty
366
+ Exception: For HuggingFace Hub upload errors
367
+
368
+ Example:
369
+ >>> import pandas as pd
370
+ >>> df = pd.DataFrame({
371
+ ... "question": ["How to train?", "What is fine-tuning?"],
372
+ ... "solution": ["Use trainer...", "Fine-tuning is..."],
373
+ ... "rubric": ['[{"title": "...", ...}]', '[{"title": "...", ...}]']
374
+ ... })
375
+ >>> upload_dataframe_to_hf(df, "username/dataset@rubrics")
376
+
377
+ Notes:
378
+ - Requires authentication via `huggingface-cli login` or HF_TOKEN env var
379
+ - DataFrame columns with complex objects should be serialized first (e.g., to JSON strings)
380
+ - If the dataset doesn't exist, it will be created automatically
381
+ - Empty DataFrames will raise ValueError to prevent uploading invalid data
382
+ """
383
+ # Validate DataFrame
384
+ if df.empty:
385
+ raise ValueError("DataFrame is empty")
386
+
387
+ # Parse dataset specification
388
+ if "@" in dataset_spec:
389
+ dataset_name, config_name = dataset_spec.split("@", 1)
390
+ else:
391
+ dataset_name = dataset_spec
392
+ config_name = "default"
393
+
394
+ try:
395
+ print("\nUploading DataFrame to HuggingFace Hub...")
396
+ print(f" Dataset: {dataset_name}")
397
+ print(f" Config: {config_name}")
398
+ print(f" Split: {split}")
399
+ print(f" Rows: {len(df)}")
400
+ print(f" Columns: {list(df.columns)}")
401
+
402
+ # Convert DataFrame to HuggingFace Dataset
403
+ dataset = Dataset.from_pandas(df)
404
+
405
+ # Upload to HuggingFace Hub
406
+ dataset.push_to_hub(
407
+ dataset_name,
408
+ config_name=config_name,
409
+ split=split,
410
+ private=private,
411
+ )
412
+
413
+ print(
414
+ f"✓ Successfully uploaded to {dataset_name}@{config_name} (split: {split})"
415
+ )
416
+ return True
417
+
418
+ except Exception as e:
419
+ print(f"✗ Failed to upload to HuggingFace: {type(e).__name__}: {str(e)}")
420
+ return False
421
+
422
+
423
+ def hub_to_df(
424
+ dataset_spec: str,
425
+ split: str = "train",
426
+ ) -> Optional[pd.DataFrame]:
427
+ """
428
+ Download a dataset from HuggingFace Hub as a pandas DataFrame.
429
+
430
+ This function downloads a dataset from the HuggingFace Hub and returns it as a
431
+ pandas DataFrame for immediate use in Python.
432
+
433
+ Args:
434
+ dataset_spec: Dataset specification in the format "dataset_name" or
435
+ "dataset_name@config_name". Examples:
436
+ - "username/my-dataset" (uses "default" config)
437
+ - "username/my-dataset@rubrics" (uses "rubrics" config)
438
+ - "username/my-dataset@evaluations" (uses "evaluations" config)
439
+
440
+ split: The dataset split to download. Defaults to "train". Common values:
441
+ - "train": Training or main data
442
+ - "validation": Validation data
443
+ - "test": Test data
444
+
445
+ Returns:
446
+ pd.DataFrame: Downloaded data as pandas DataFrame, or None if failed
447
+
448
+ Raises:
449
+ ValueError: If the dataset/config/split doesn't exist
450
+ Exception: For HuggingFace Hub download errors
451
+
452
+ Example:
453
+ >>> # Download rubrics from specific config
454
+ >>> df = hub_to_df("username/hf-agent-benchmark@rubrics")
455
+ >>> print(df.head())
456
+ >>> print(f"Shape: {df.shape}")
457
+
458
+ >>> # Download evaluation results
459
+ >>> results_df = download_hf_to_dataframe(
460
+ ... "username/hf-agent-benchmark@evaluations",
461
+ ... split="test"
462
+ ... )
463
+
464
+ Notes:
465
+ - Requires authentication for private datasets via `huggingface-cli login`
466
+ - Downloaded data will be in the same format as uploaded (preserves structure)
467
+ - Large datasets may take time to download and consume significant memory
468
+ - For very large datasets, consider using streaming or download_hf_to_jsonl
469
+ """
470
+ # Parse dataset specification
471
+ if "@" in dataset_spec:
472
+ dataset_name, config_name = dataset_spec.split("@", 1)
473
+ else:
474
+ dataset_name = dataset_spec
475
+ config_name = "default"
476
+
477
+ try:
478
+ print("\nDownloading from HuggingFace Hub...")
479
+ print(f" Dataset: {dataset_name}")
480
+ print(f" Config: {config_name}")
481
+ print(f" Split: {split}")
482
+
483
+ # Download dataset from HuggingFace Hub
484
+ dataset = load_dataset(
485
+ dataset_name,
486
+ name=config_name,
487
+ split=split,
488
+ )
489
+
490
+ print(f" Downloaded {len(dataset)} records")
491
+
492
+ # Convert to pandas DataFrame
493
+ df = dataset.to_pandas()
494
+
495
+ print("✓ Successfully loaded as DataFrame")
496
+ print(f" Shape: {df.shape}")
497
+ print(f" Columns: {list(df.columns)}")
498
+ return df
499
+
500
+ except Exception as e:
501
+ print(f"✗ Failed to download from HuggingFace: {type(e).__name__}: {str(e)}")
502
+ return None
503
+
504
+
505
+ if __name__ == "__main__":
506
+ # Example usage
507
+ print("HuggingFace Dataset I/O Utilities")
508
+ print("=" * 60)
509
+ print("\nExample: Upload rubrics")
510
+ print(' upload_jsonl_to_hf("qa_rubrics.jsonl", "username/dataset@rubrics")')
511
+ print("\nExample: Download evaluations")
512
+ print(' download_hf_to_jsonl("username/dataset@evaluations", "local.jsonl")')
513
+ print("\nExample: List configs")
514
+ print(' list_dataset_configs("username/dataset")')
515
+ print("\nExample: Get dataset info")
516
+ print(' get_dataset_info("username/dataset@rubrics")')
517
+ print("=" * 60)
pyproject.toml CHANGED
@@ -12,4 +12,6 @@ dependencies = [
12
  "tenacity>=8.0.0",
13
  "pandas>=2.3.3",
14
  "python-dotenv>=1.2.1",
 
 
15
  ]
 
12
  "tenacity>=8.0.0",
13
  "pandas>=2.3.3",
14
  "python-dotenv>=1.2.1",
15
+ "datasets>=4.3.0",
16
+ "huggingface-hub>=1.0.1",
17
  ]
uv.lock CHANGED
@@ -228,6 +228,40 @@ wheels = [
228
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
229
  ]
230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  [[package]]
232
  name = "distro"
233
  version = "1.9.0"
@@ -385,6 +419,11 @@ wheels = [
385
  { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" },
386
  ]
387
 
 
 
 
 
 
388
  [[package]]
389
  name = "h11"
390
  version = "0.16.0"
@@ -399,6 +438,8 @@ name = "hf-agent"
399
  version = "0.1.0"
400
  source = { virtual = "." }
401
  dependencies = [
 
 
402
  { name = "litellm" },
403
  { name = "numpy" },
404
  { name = "pandas" },
@@ -410,6 +451,8 @@ dependencies = [
410
 
411
  [package.metadata]
412
  requires-dist = [
 
 
413
  { name = "litellm", specifier = ">=1.0.0" },
414
  { name = "numpy", specifier = ">=1.24.0" },
415
  { name = "pandas", specifier = ">=2.3.3" },
@@ -810,6 +853,22 @@ wheels = [
810
  { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" },
811
  ]
812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
813
  [[package]]
814
  name = "numpy"
815
  version = "2.3.4"
@@ -1032,6 +1091,49 @@ wheels = [
1032
  { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
1033
  ]
1034
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1035
  [[package]]
1036
  name = "pydantic"
1037
  version = "2.12.3"
@@ -1550,6 +1652,89 @@ wheels = [
1550
  { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
1551
  ]
1552
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1553
  [[package]]
1554
  name = "yarl"
1555
  version = "1.22.0"
 
228
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
229
  ]
230
 
231
+ [[package]]
232
+ name = "datasets"
233
+ version = "4.3.0"
234
+ source = { registry = "https://pypi.org/simple" }
235
+ dependencies = [
236
+ { name = "dill" },
237
+ { name = "filelock" },
238
+ { name = "fsspec", extra = ["http"] },
239
+ { name = "httpx" },
240
+ { name = "huggingface-hub" },
241
+ { name = "multiprocess" },
242
+ { name = "numpy" },
243
+ { name = "packaging" },
244
+ { name = "pandas" },
245
+ { name = "pyarrow" },
246
+ { name = "pyyaml" },
247
+ { name = "requests" },
248
+ { name = "tqdm" },
249
+ { name = "xxhash" },
250
+ ]
251
+ sdist = { url = "https://files.pythonhosted.org/packages/2a/47/325206ac160f7699ed9f1798afa8f8f8d5189b03bf3815654859ac1d5cba/datasets-4.3.0.tar.gz", hash = "sha256:bc9118ed9afd92346c5be7ed3aaa00177eb907c25467f9d072a0d22777efbd2b", size = 582801, upload-time = "2025-10-23T16:31:51.547Z" }
252
+ wheels = [
253
+ { url = "https://files.pythonhosted.org/packages/ca/51/409a8184ed35453d9cbb3d6b20d524b1115c2c2d117b85d5e9b06cd70b45/datasets-4.3.0-py3-none-any.whl", hash = "sha256:0ea157e72138b3ca6c7d2415f19a164ecf7d4c4fa72da2a570da286882e96903", size = 506846, upload-time = "2025-10-23T16:31:49.965Z" },
254
+ ]
255
+
256
+ [[package]]
257
+ name = "dill"
258
+ version = "0.4.0"
259
+ source = { registry = "https://pypi.org/simple" }
260
+ sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" }
261
+ wheels = [
262
+ { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" },
263
+ ]
264
+
265
  [[package]]
266
  name = "distro"
267
  version = "1.9.0"
 
419
  { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" },
420
  ]
421
 
422
+ [package.optional-dependencies]
423
+ http = [
424
+ { name = "aiohttp" },
425
+ ]
426
+
427
  [[package]]
428
  name = "h11"
429
  version = "0.16.0"
 
438
  version = "0.1.0"
439
  source = { virtual = "." }
440
  dependencies = [
441
+ { name = "datasets" },
442
+ { name = "huggingface-hub" },
443
  { name = "litellm" },
444
  { name = "numpy" },
445
  { name = "pandas" },
 
451
 
452
  [package.metadata]
453
  requires-dist = [
454
+ { name = "datasets", specifier = ">=4.3.0" },
455
+ { name = "huggingface-hub", specifier = ">=1.0.1" },
456
  { name = "litellm", specifier = ">=1.0.0" },
457
  { name = "numpy", specifier = ">=1.24.0" },
458
  { name = "pandas", specifier = ">=2.3.3" },
 
853
  { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" },
854
  ]
855
 
856
+ [[package]]
857
+ name = "multiprocess"
858
+ version = "0.70.16"
859
+ source = { registry = "https://pypi.org/simple" }
860
+ dependencies = [
861
+ { name = "dill" },
862
+ ]
863
+ sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603, upload-time = "2024-01-28T18:52:34.85Z" }
864
+ wheels = [
865
+ { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824, upload-time = "2024-01-28T18:52:26.062Z" },
866
+ { url = "https://files.pythonhosted.org/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a", size = 143519, upload-time = "2024-01-28T18:52:28.115Z" },
867
+ { url = "https://files.pythonhosted.org/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e", size = 146741, upload-time = "2024-01-28T18:52:29.395Z" },
868
+ { url = "https://files.pythonhosted.org/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628, upload-time = "2024-01-28T18:52:30.853Z" },
869
+ { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351, upload-time = "2024-01-28T18:52:31.981Z" },
870
+ ]
871
+
872
  [[package]]
873
  name = "numpy"
874
  version = "2.3.4"
 
1091
  { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
1092
  ]
1093
 
1094
+ [[package]]
1095
+ name = "pyarrow"
1096
+ version = "22.0.0"
1097
+ source = { registry = "https://pypi.org/simple" }
1098
+ sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151, upload-time = "2025-10-24T12:30:00.762Z" }
1099
+ wheels = [
1100
+ { url = "https://files.pythonhosted.org/packages/af/63/ba23862d69652f85b615ca14ad14f3bcfc5bf1b99ef3f0cd04ff93fdad5a/pyarrow-22.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bea79263d55c24a32b0d79c00a1c58bb2ee5f0757ed95656b01c0fb310c5af3d", size = 34211578, upload-time = "2025-10-24T10:05:21.583Z" },
1101
+ { url = "https://files.pythonhosted.org/packages/b1/d0/f9ad86fe809efd2bcc8be32032fa72e8b0d112b01ae56a053006376c5930/pyarrow-22.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:12fe549c9b10ac98c91cf791d2945e878875d95508e1a5d14091a7aaa66d9cf8", size = 35989906, upload-time = "2025-10-24T10:05:29.485Z" },
1102
+ { url = "https://files.pythonhosted.org/packages/b4/a8/f910afcb14630e64d673f15904ec27dd31f1e009b77033c365c84e8c1e1d/pyarrow-22.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:334f900ff08ce0423407af97e6c26ad5d4e3b0763645559ece6fbf3747d6a8f5", size = 45021677, upload-time = "2025-10-24T10:05:38.274Z" },
1103
+ { url = "https://files.pythonhosted.org/packages/13/95/aec81f781c75cd10554dc17a25849c720d54feafb6f7847690478dcf5ef8/pyarrow-22.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c6c791b09c57ed76a18b03f2631753a4960eefbbca80f846da8baefc6491fcfe", size = 47726315, upload-time = "2025-10-24T10:05:47.314Z" },
1104
+ { url = "https://files.pythonhosted.org/packages/bb/d4/74ac9f7a54cfde12ee42734ea25d5a3c9a45db78f9def949307a92720d37/pyarrow-22.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c3200cb41cdbc65156e5f8c908d739b0dfed57e890329413da2748d1a2cd1a4e", size = 47990906, upload-time = "2025-10-24T10:05:58.254Z" },
1105
+ { url = "https://files.pythonhosted.org/packages/2e/71/fedf2499bf7a95062eafc989ace56572f3343432570e1c54e6599d5b88da/pyarrow-22.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ac93252226cf288753d8b46280f4edf3433bf9508b6977f8dd8526b521a1bbb9", size = 50306783, upload-time = "2025-10-24T10:06:08.08Z" },
1106
+ { url = "https://files.pythonhosted.org/packages/68/ed/b202abd5a5b78f519722f3d29063dda03c114711093c1995a33b8e2e0f4b/pyarrow-22.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:44729980b6c50a5f2bfcc2668d36c569ce17f8b17bccaf470c4313dcbbf13c9d", size = 27972883, upload-time = "2025-10-24T10:06:14.204Z" },
1107
+ { url = "https://files.pythonhosted.org/packages/a6/d6/d0fac16a2963002fc22c8fa75180a838737203d558f0ed3b564c4a54eef5/pyarrow-22.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e6e95176209257803a8b3d0394f21604e796dadb643d2f7ca21b66c9c0b30c9a", size = 34204629, upload-time = "2025-10-24T10:06:20.274Z" },
1108
+ { url = "https://files.pythonhosted.org/packages/c6/9c/1d6357347fbae062ad3f17082f9ebc29cc733321e892c0d2085f42a2212b/pyarrow-22.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:001ea83a58024818826a9e3f89bf9310a114f7e26dfe404a4c32686f97bd7901", size = 35985783, upload-time = "2025-10-24T10:06:27.301Z" },
1109
+ { url = "https://files.pythonhosted.org/packages/ff/c0/782344c2ce58afbea010150df07e3a2f5fdad299cd631697ae7bd3bac6e3/pyarrow-22.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ce20fe000754f477c8a9125543f1936ea5b8867c5406757c224d745ed033e691", size = 45020999, upload-time = "2025-10-24T10:06:35.387Z" },
1110
+ { url = "https://files.pythonhosted.org/packages/1b/8b/5362443737a5307a7b67c1017c42cd104213189b4970bf607e05faf9c525/pyarrow-22.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e0a15757fccb38c410947df156f9749ae4a3c89b2393741a50521f39a8cf202a", size = 47724601, upload-time = "2025-10-24T10:06:43.551Z" },
1111
+ { url = "https://files.pythonhosted.org/packages/69/4d/76e567a4fc2e190ee6072967cb4672b7d9249ac59ae65af2d7e3047afa3b/pyarrow-22.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cedb9dd9358e4ea1d9bce3665ce0797f6adf97ff142c8e25b46ba9cdd508e9b6", size = 48001050, upload-time = "2025-10-24T10:06:52.284Z" },
1112
+ { url = "https://files.pythonhosted.org/packages/01/5e/5653f0535d2a1aef8223cee9d92944cb6bccfee5cf1cd3f462d7cb022790/pyarrow-22.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:252be4a05f9d9185bb8c18e83764ebcfea7185076c07a7a662253af3a8c07941", size = 50307877, upload-time = "2025-10-24T10:07:02.405Z" },
1113
+ { url = "https://files.pythonhosted.org/packages/2d/f8/1d0bd75bf9328a3b826e24a16e5517cd7f9fbf8d34a3184a4566ef5a7f29/pyarrow-22.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:a4893d31e5ef780b6edcaf63122df0f8d321088bb0dee4c8c06eccb1ca28d145", size = 27977099, upload-time = "2025-10-24T10:08:07.259Z" },
1114
+ { url = "https://files.pythonhosted.org/packages/90/81/db56870c997805bf2b0f6eeeb2d68458bf4654652dccdcf1bf7a42d80903/pyarrow-22.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:f7fe3dbe871294ba70d789be16b6e7e52b418311e166e0e3cba9522f0f437fb1", size = 34336685, upload-time = "2025-10-24T10:07:11.47Z" },
1115
+ { url = "https://files.pythonhosted.org/packages/1c/98/0727947f199aba8a120f47dfc229eeb05df15bcd7a6f1b669e9f882afc58/pyarrow-22.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:ba95112d15fd4f1105fb2402c4eab9068f0554435e9b7085924bcfaac2cc306f", size = 36032158, upload-time = "2025-10-24T10:07:18.626Z" },
1116
+ { url = "https://files.pythonhosted.org/packages/96/b4/9babdef9c01720a0785945c7cf550e4acd0ebcd7bdd2e6f0aa7981fa85e2/pyarrow-22.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c064e28361c05d72eed8e744c9605cbd6d2bb7481a511c74071fd9b24bc65d7d", size = 44892060, upload-time = "2025-10-24T10:07:26.002Z" },
1117
+ { url = "https://files.pythonhosted.org/packages/f8/ca/2f8804edd6279f78a37062d813de3f16f29183874447ef6d1aadbb4efa0f/pyarrow-22.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6f9762274496c244d951c819348afbcf212714902742225f649cf02823a6a10f", size = 47504395, upload-time = "2025-10-24T10:07:34.09Z" },
1118
+ { url = "https://files.pythonhosted.org/packages/b9/f0/77aa5198fd3943682b2e4faaf179a674f0edea0d55d326d83cb2277d9363/pyarrow-22.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a9d9ffdc2ab696f6b15b4d1f7cec6658e1d788124418cb30030afbae31c64746", size = 48066216, upload-time = "2025-10-24T10:07:43.528Z" },
1119
+ { url = "https://files.pythonhosted.org/packages/79/87/a1937b6e78b2aff18b706d738c9e46ade5bfcf11b294e39c87706a0089ac/pyarrow-22.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ec1a15968a9d80da01e1d30349b2b0d7cc91e96588ee324ce1b5228175043e95", size = 50288552, upload-time = "2025-10-24T10:07:53.519Z" },
1120
+ { url = "https://files.pythonhosted.org/packages/60/ae/b5a5811e11f25788ccfdaa8f26b6791c9807119dffcf80514505527c384c/pyarrow-22.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bba208d9c7decf9961998edf5c65e3ea4355d5818dd6cd0f6809bec1afb951cc", size = 28262504, upload-time = "2025-10-24T10:08:00.932Z" },
1121
+ { url = "https://files.pythonhosted.org/packages/bd/b0/0fa4d28a8edb42b0a7144edd20befd04173ac79819547216f8a9f36f9e50/pyarrow-22.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:9bddc2cade6561f6820d4cd73f99a0243532ad506bc510a75a5a65a522b2d74d", size = 34224062, upload-time = "2025-10-24T10:08:14.101Z" },
1122
+ { url = "https://files.pythonhosted.org/packages/0f/a8/7a719076b3c1be0acef56a07220c586f25cd24de0e3f3102b438d18ae5df/pyarrow-22.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e70ff90c64419709d38c8932ea9fe1cc98415c4f87ea8da81719e43f02534bc9", size = 35990057, upload-time = "2025-10-24T10:08:21.842Z" },
1123
+ { url = "https://files.pythonhosted.org/packages/89/3c/359ed54c93b47fb6fe30ed16cdf50e3f0e8b9ccfb11b86218c3619ae50a8/pyarrow-22.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:92843c305330aa94a36e706c16209cd4df274693e777ca47112617db7d0ef3d7", size = 45068002, upload-time = "2025-10-24T10:08:29.034Z" },
1124
+ { url = "https://files.pythonhosted.org/packages/55/fc/4945896cc8638536ee787a3bd6ce7cec8ec9acf452d78ec39ab328efa0a1/pyarrow-22.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:6dda1ddac033d27421c20d7a7943eec60be44e0db4e079f33cc5af3b8280ccde", size = 47737765, upload-time = "2025-10-24T10:08:38.559Z" },
1125
+ { url = "https://files.pythonhosted.org/packages/cd/5e/7cb7edeb2abfaa1f79b5d5eb89432356155c8426f75d3753cbcb9592c0fd/pyarrow-22.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:84378110dd9a6c06323b41b56e129c504d157d1a983ce8f5443761eb5256bafc", size = 48048139, upload-time = "2025-10-24T10:08:46.784Z" },
1126
+ { url = "https://files.pythonhosted.org/packages/88/c6/546baa7c48185f5e9d6e59277c4b19f30f48c94d9dd938c2a80d4d6b067c/pyarrow-22.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:854794239111d2b88b40b6ef92aa478024d1e5074f364033e73e21e3f76b25e0", size = 50314244, upload-time = "2025-10-24T10:08:55.771Z" },
1127
+ { url = "https://files.pythonhosted.org/packages/3c/79/755ff2d145aafec8d347bf18f95e4e81c00127f06d080135dfc86aea417c/pyarrow-22.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:b883fe6fd85adad7932b3271c38ac289c65b7337c2c132e9569f9d3940620730", size = 28757501, upload-time = "2025-10-24T10:09:59.891Z" },
1128
+ { url = "https://files.pythonhosted.org/packages/0e/d2/237d75ac28ced3147912954e3c1a174df43a95f4f88e467809118a8165e0/pyarrow-22.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7a820d8ae11facf32585507c11f04e3f38343c1e784c9b5a8b1da5c930547fe2", size = 34355506, upload-time = "2025-10-24T10:09:02.953Z" },
1129
+ { url = "https://files.pythonhosted.org/packages/1e/2c/733dfffe6d3069740f98e57ff81007809067d68626c5faef293434d11bd6/pyarrow-22.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:c6ec3675d98915bf1ec8b3c7986422682f7232ea76cad276f4c8abd5b7319b70", size = 36047312, upload-time = "2025-10-24T10:09:10.334Z" },
1130
+ { url = "https://files.pythonhosted.org/packages/7c/2b/29d6e3782dc1f299727462c1543af357a0f2c1d3c160ce199950d9ca51eb/pyarrow-22.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:3e739edd001b04f654b166204fc7a9de896cf6007eaff33409ee9e50ceaff754", size = 45081609, upload-time = "2025-10-24T10:09:18.61Z" },
1131
+ { url = "https://files.pythonhosted.org/packages/8d/42/aa9355ecc05997915af1b7b947a7f66c02dcaa927f3203b87871c114ba10/pyarrow-22.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7388ac685cab5b279a41dfe0a6ccd99e4dbf322edfb63e02fc0443bf24134e91", size = 47703663, upload-time = "2025-10-24T10:09:27.369Z" },
1132
+ { url = "https://files.pythonhosted.org/packages/ee/62/45abedde480168e83a1de005b7b7043fd553321c1e8c5a9a114425f64842/pyarrow-22.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f633074f36dbc33d5c05b5dc75371e5660f1dbf9c8b1d95669def05e5425989c", size = 48066543, upload-time = "2025-10-24T10:09:34.908Z" },
1133
+ { url = "https://files.pythonhosted.org/packages/84/e9/7878940a5b072e4f3bf998770acafeae13b267f9893af5f6d4ab3904b67e/pyarrow-22.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4c19236ae2402a8663a2c8f21f1870a03cc57f0bef7e4b6eb3238cc82944de80", size = 50288838, upload-time = "2025-10-24T10:09:44.394Z" },
1134
+ { url = "https://files.pythonhosted.org/packages/7b/03/f335d6c52b4a4761bcc83499789a1e2e16d9d201a58c327a9b5cc9a41bd9/pyarrow-22.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0c34fe18094686194f204a3b1787a27456897d8a2d62caf84b61e8dfbc0252ae", size = 29185594, upload-time = "2025-10-24T10:09:53.111Z" },
1135
+ ]
1136
+
1137
  [[package]]
1138
  name = "pydantic"
1139
  version = "2.12.3"
 
1652
  { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
1653
  ]
1654
 
1655
+ [[package]]
1656
+ name = "xxhash"
1657
+ version = "3.6.0"
1658
+ source = { registry = "https://pypi.org/simple" }
1659
+ sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" }
1660
+ wheels = [
1661
+ { url = "https://files.pythonhosted.org/packages/9a/07/d9412f3d7d462347e4511181dea65e47e0d0e16e26fbee2ea86a2aefb657/xxhash-3.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:01362c4331775398e7bb34e3ab403bc9ee9f7c497bc7dee6272114055277dd3c", size = 32744, upload-time = "2025-10-02T14:34:34.622Z" },
1662
+ { url = "https://files.pythonhosted.org/packages/79/35/0429ee11d035fc33abe32dca1b2b69e8c18d236547b9a9b72c1929189b9a/xxhash-3.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b2df81a23f8cb99656378e72501b2cb41b1827c0f5a86f87d6b06b69f9f204", size = 30816, upload-time = "2025-10-02T14:34:36.043Z" },
1663
+ { url = "https://files.pythonhosted.org/packages/b7/f2/57eb99aa0f7d98624c0932c5b9a170e1806406cdbcdb510546634a1359e0/xxhash-3.6.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dc94790144e66b14f67b10ac8ed75b39ca47536bf8800eb7c24b50271ea0c490", size = 194035, upload-time = "2025-10-02T14:34:37.354Z" },
1664
+ { url = "https://files.pythonhosted.org/packages/4c/ed/6224ba353690d73af7a3f1c7cdb1fc1b002e38f783cb991ae338e1eb3d79/xxhash-3.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93f107c673bccf0d592cdba077dedaf52fe7f42dcd7676eba1f6d6f0c3efffd2", size = 212914, upload-time = "2025-10-02T14:34:38.6Z" },
1665
+ { url = "https://files.pythonhosted.org/packages/38/86/fb6b6130d8dd6b8942cc17ab4d90e223653a89aa32ad2776f8af7064ed13/xxhash-3.6.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aa5ee3444c25b69813663c9f8067dcfaa2e126dc55e8dddf40f4d1c25d7effa", size = 212163, upload-time = "2025-10-02T14:34:39.872Z" },
1666
+ { url = "https://files.pythonhosted.org/packages/ee/dc/e84875682b0593e884ad73b2d40767b5790d417bde603cceb6878901d647/xxhash-3.6.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7f99123f0e1194fa59cc69ad46dbae2e07becec5df50a0509a808f90a0f03f0", size = 445411, upload-time = "2025-10-02T14:34:41.569Z" },
1667
+ { url = "https://files.pythonhosted.org/packages/11/4f/426f91b96701ec2f37bb2b8cec664eff4f658a11f3fa9d94f0a887ea6d2b/xxhash-3.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49e03e6fe2cac4a1bc64952dd250cf0dbc5ef4ebb7b8d96bce82e2de163c82a2", size = 193883, upload-time = "2025-10-02T14:34:43.249Z" },
1668
+ { url = "https://files.pythonhosted.org/packages/53/5a/ddbb83eee8e28b778eacfc5a85c969673e4023cdeedcfcef61f36731610b/xxhash-3.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd17fede52a17a4f9a7bc4472a5867cb0b160deeb431795c0e4abe158bc784e9", size = 210392, upload-time = "2025-10-02T14:34:45.042Z" },
1669
+ { url = "https://files.pythonhosted.org/packages/1e/c2/ff69efd07c8c074ccdf0a4f36fcdd3d27363665bcdf4ba399abebe643465/xxhash-3.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6fb5f5476bef678f69db04f2bd1efbed3030d2aba305b0fc1773645f187d6a4e", size = 197898, upload-time = "2025-10-02T14:34:46.302Z" },
1670
+ { url = "https://files.pythonhosted.org/packages/58/ca/faa05ac19b3b622c7c9317ac3e23954187516298a091eb02c976d0d3dd45/xxhash-3.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:843b52f6d88071f87eba1631b684fcb4b2068cd2180a0224122fe4ef011a9374", size = 210655, upload-time = "2025-10-02T14:34:47.571Z" },
1671
+ { url = "https://files.pythonhosted.org/packages/d4/7a/06aa7482345480cc0cb597f5c875b11a82c3953f534394f620b0be2f700c/xxhash-3.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d14a6cfaf03b1b6f5f9790f76880601ccc7896aff7ab9cd8978a939c1eb7e0d", size = 414001, upload-time = "2025-10-02T14:34:49.273Z" },
1672
+ { url = "https://files.pythonhosted.org/packages/23/07/63ffb386cd47029aa2916b3d2f454e6cc5b9f5c5ada3790377d5430084e7/xxhash-3.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:418daf3db71e1413cfe211c2f9a528456936645c17f46b5204705581a45390ae", size = 191431, upload-time = "2025-10-02T14:34:50.798Z" },
1673
+ { url = "https://files.pythonhosted.org/packages/0f/93/14fde614cadb4ddf5e7cebf8918b7e8fac5ae7861c1875964f17e678205c/xxhash-3.6.0-cp312-cp312-win32.whl", hash = "sha256:50fc255f39428a27299c20e280d6193d8b63b8ef8028995323bf834a026b4fbb", size = 30617, upload-time = "2025-10-02T14:34:51.954Z" },
1674
+ { url = "https://files.pythonhosted.org/packages/13/5d/0d125536cbe7565a83d06e43783389ecae0c0f2ed037b48ede185de477c0/xxhash-3.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0f2ab8c715630565ab8991b536ecded9416d615538be8ecddce43ccf26cbc7c", size = 31534, upload-time = "2025-10-02T14:34:53.276Z" },
1675
+ { url = "https://files.pythonhosted.org/packages/54/85/6ec269b0952ec7e36ba019125982cf11d91256a778c7c3f98a4c5043d283/xxhash-3.6.0-cp312-cp312-win_arm64.whl", hash = "sha256:eae5c13f3bc455a3bbb68bdc513912dc7356de7e2280363ea235f71f54064829", size = 27876, upload-time = "2025-10-02T14:34:54.371Z" },
1676
+ { url = "https://files.pythonhosted.org/packages/33/76/35d05267ac82f53ae9b0e554da7c5e281ee61f3cad44c743f0fcd354f211/xxhash-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:599e64ba7f67472481ceb6ee80fa3bd828fd61ba59fb11475572cc5ee52b89ec", size = 32738, upload-time = "2025-10-02T14:34:55.839Z" },
1677
+ { url = "https://files.pythonhosted.org/packages/31/a8/3fbce1cd96534a95e35d5120637bf29b0d7f5d8fa2f6374e31b4156dd419/xxhash-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d8b8aaa30fca4f16f0c84a5c8d7ddee0e25250ec2796c973775373257dde8f1", size = 30821, upload-time = "2025-10-02T14:34:57.219Z" },
1678
+ { url = "https://files.pythonhosted.org/packages/0c/ea/d387530ca7ecfa183cb358027f1833297c6ac6098223fd14f9782cd0015c/xxhash-3.6.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d597acf8506d6e7101a4a44a5e428977a51c0fadbbfd3c39650cca9253f6e5a6", size = 194127, upload-time = "2025-10-02T14:34:59.21Z" },
1679
+ { url = "https://files.pythonhosted.org/packages/ba/0c/71435dcb99874b09a43b8d7c54071e600a7481e42b3e3ce1eb5226a5711a/xxhash-3.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:858dc935963a33bc33490128edc1c12b0c14d9c7ebaa4e387a7869ecc4f3e263", size = 212975, upload-time = "2025-10-02T14:35:00.816Z" },
1680
+ { url = "https://files.pythonhosted.org/packages/84/7a/c2b3d071e4bb4a90b7057228a99b10d51744878f4a8a6dd643c8bd897620/xxhash-3.6.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba284920194615cb8edf73bf52236ce2e1664ccd4a38fdb543506413529cc546", size = 212241, upload-time = "2025-10-02T14:35:02.207Z" },
1681
+ { url = "https://files.pythonhosted.org/packages/81/5f/640b6eac0128e215f177df99eadcd0f1b7c42c274ab6a394a05059694c5a/xxhash-3.6.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b54219177f6c6674d5378bd862c6aedf64725f70dd29c472eaae154df1a2e89", size = 445471, upload-time = "2025-10-02T14:35:03.61Z" },
1682
+ { url = "https://files.pythonhosted.org/packages/5e/1e/3c3d3ef071b051cc3abbe3721ffb8365033a172613c04af2da89d5548a87/xxhash-3.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c36dd7dbad2f5238950c377fcbf6811b1cdb1c444fab447960030cea60504d", size = 193936, upload-time = "2025-10-02T14:35:05.013Z" },
1683
+ { url = "https://files.pythonhosted.org/packages/2c/bd/4a5f68381939219abfe1c22a9e3a5854a4f6f6f3c4983a87d255f21f2e5d/xxhash-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f22927652cba98c44639ffdc7aaf35828dccf679b10b31c4ad72a5b530a18eb7", size = 210440, upload-time = "2025-10-02T14:35:06.239Z" },
1684
+ { url = "https://files.pythonhosted.org/packages/eb/37/b80fe3d5cfb9faff01a02121a0f4d565eb7237e9e5fc66e73017e74dcd36/xxhash-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b45fad44d9c5c119e9c6fbf2e1c656a46dc68e280275007bbfd3d572b21426db", size = 197990, upload-time = "2025-10-02T14:35:07.735Z" },
1685
+ { url = "https://files.pythonhosted.org/packages/d7/fd/2c0a00c97b9e18f72e1f240ad4e8f8a90fd9d408289ba9c7c495ed7dc05c/xxhash-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6f2580ffab1a8b68ef2b901cde7e55fa8da5e4be0977c68f78fc80f3c143de42", size = 210689, upload-time = "2025-10-02T14:35:09.438Z" },
1686
+ { url = "https://files.pythonhosted.org/packages/93/86/5dd8076a926b9a95db3206aba20d89a7fc14dd5aac16e5c4de4b56033140/xxhash-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40c391dd3cd041ebc3ffe6f2c862f402e306eb571422e0aa918d8070ba31da11", size = 414068, upload-time = "2025-10-02T14:35:11.162Z" },
1687
+ { url = "https://files.pythonhosted.org/packages/af/3c/0bb129170ee8f3650f08e993baee550a09593462a5cddd8e44d0011102b1/xxhash-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f205badabde7aafd1a31e8ca2a3e5a763107a71c397c4481d6a804eb5063d8bd", size = 191495, upload-time = "2025-10-02T14:35:12.971Z" },
1688
+ { url = "https://files.pythonhosted.org/packages/e9/3a/6797e0114c21d1725e2577508e24006fd7ff1d8c0c502d3b52e45c1771d8/xxhash-3.6.0-cp313-cp313-win32.whl", hash = "sha256:2577b276e060b73b73a53042ea5bd5203d3e6347ce0d09f98500f418a9fcf799", size = 30620, upload-time = "2025-10-02T14:35:14.129Z" },
1689
+ { url = "https://files.pythonhosted.org/packages/86/15/9bc32671e9a38b413a76d24722a2bf8784a132c043063a8f5152d390b0f9/xxhash-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:757320d45d2fbcce8f30c42a6b2f47862967aea7bf458b9625b4bbe7ee390392", size = 31542, upload-time = "2025-10-02T14:35:15.21Z" },
1690
+ { url = "https://files.pythonhosted.org/packages/39/c5/cc01e4f6188656e56112d6a8e0dfe298a16934b8c47a247236549a3f7695/xxhash-3.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:457b8f85dec5825eed7b69c11ae86834a018b8e3df5e77783c999663da2f96d6", size = 27880, upload-time = "2025-10-02T14:35:16.315Z" },
1691
+ { url = "https://files.pythonhosted.org/packages/f3/30/25e5321c8732759e930c555176d37e24ab84365482d257c3b16362235212/xxhash-3.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a42e633d75cdad6d625434e3468126c73f13f7584545a9cf34e883aa1710e702", size = 32956, upload-time = "2025-10-02T14:35:17.413Z" },
1692
+ { url = "https://files.pythonhosted.org/packages/9f/3c/0573299560d7d9f8ab1838f1efc021a280b5ae5ae2e849034ef3dee18810/xxhash-3.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:568a6d743219e717b07b4e03b0a828ce593833e498c3b64752e0f5df6bfe84db", size = 31072, upload-time = "2025-10-02T14:35:18.844Z" },
1693
+ { url = "https://files.pythonhosted.org/packages/7a/1c/52d83a06e417cd9d4137722693424885cc9878249beb3a7c829e74bf7ce9/xxhash-3.6.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bec91b562d8012dae276af8025a55811b875baace6af510412a5e58e3121bc54", size = 196409, upload-time = "2025-10-02T14:35:20.31Z" },
1694
+ { url = "https://files.pythonhosted.org/packages/e3/8e/c6d158d12a79bbd0b878f8355432075fc82759e356ab5a111463422a239b/xxhash-3.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78e7f2f4c521c30ad5e786fdd6bae89d47a32672a80195467b5de0480aa97b1f", size = 215736, upload-time = "2025-10-02T14:35:21.616Z" },
1695
+ { url = "https://files.pythonhosted.org/packages/bc/68/c4c80614716345d55071a396cf03d06e34b5f4917a467faf43083c995155/xxhash-3.6.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ed0df1b11a79856df5ffcab572cbd6b9627034c1c748c5566fa79df9048a7c5", size = 214833, upload-time = "2025-10-02T14:35:23.32Z" },
1696
+ { url = "https://files.pythonhosted.org/packages/7e/e9/ae27c8ffec8b953efa84c7c4a6c6802c263d587b9fc0d6e7cea64e08c3af/xxhash-3.6.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e4edbfc7d420925b0dd5e792478ed393d6e75ff8fc219a6546fb446b6a417b1", size = 448348, upload-time = "2025-10-02T14:35:25.111Z" },
1697
+ { url = "https://files.pythonhosted.org/packages/d7/6b/33e21afb1b5b3f46b74b6bd1913639066af218d704cc0941404ca717fc57/xxhash-3.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba27a198363a7ef87f8c0f6b171ec36b674fe9053742c58dd7e3201c1ab30ee", size = 196070, upload-time = "2025-10-02T14:35:26.586Z" },
1698
+ { url = "https://files.pythonhosted.org/packages/96/b6/fcabd337bc5fa624e7203aa0fa7d0c49eed22f72e93229431752bddc83d9/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:794fe9145fe60191c6532fa95063765529770edcdd67b3d537793e8004cabbfd", size = 212907, upload-time = "2025-10-02T14:35:28.087Z" },
1699
+ { url = "https://files.pythonhosted.org/packages/4b/d3/9ee6160e644d660fcf176c5825e61411c7f62648728f69c79ba237250143/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6105ef7e62b5ac73a837778efc331a591d8442f8ef5c7e102376506cb4ae2729", size = 200839, upload-time = "2025-10-02T14:35:29.857Z" },
1700
+ { url = "https://files.pythonhosted.org/packages/0d/98/e8de5baa5109394baf5118f5e72ab21a86387c4f89b0e77ef3e2f6b0327b/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f01375c0e55395b814a679b3eea205db7919ac2af213f4a6682e01220e5fe292", size = 213304, upload-time = "2025-10-02T14:35:31.222Z" },
1701
+ { url = "https://files.pythonhosted.org/packages/7b/1d/71056535dec5c3177eeb53e38e3d367dd1d16e024e63b1cee208d572a033/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d706dca2d24d834a4661619dcacf51a75c16d65985718d6a7d73c1eeeb903ddf", size = 416930, upload-time = "2025-10-02T14:35:32.517Z" },
1702
+ { url = "https://files.pythonhosted.org/packages/dc/6c/5cbde9de2cd967c322e651c65c543700b19e7ae3e0aae8ece3469bf9683d/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f059d9faeacd49c0215d66f4056e1326c80503f51a1532ca336a385edadd033", size = 193787, upload-time = "2025-10-02T14:35:33.827Z" },
1703
+ { url = "https://files.pythonhosted.org/packages/19/fa/0172e350361d61febcea941b0cc541d6e6c8d65d153e85f850a7b256ff8a/xxhash-3.6.0-cp313-cp313t-win32.whl", hash = "sha256:1244460adc3a9be84731d72b8e80625788e5815b68da3da8b83f78115a40a7ec", size = 30916, upload-time = "2025-10-02T14:35:35.107Z" },
1704
+ { url = "https://files.pythonhosted.org/packages/ad/e6/e8cf858a2b19d6d45820f072eff1bea413910592ff17157cabc5f1227a16/xxhash-3.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b1e420ef35c503869c4064f4a2f2b08ad6431ab7b229a05cce39d74268bca6b8", size = 31799, upload-time = "2025-10-02T14:35:36.165Z" },
1705
+ { url = "https://files.pythonhosted.org/packages/56/15/064b197e855bfb7b343210e82490ae672f8bc7cdf3ddb02e92f64304ee8a/xxhash-3.6.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ec44b73a4220623235f67a996c862049f375df3b1052d9899f40a6382c32d746", size = 28044, upload-time = "2025-10-02T14:35:37.195Z" },
1706
+ { url = "https://files.pythonhosted.org/packages/7e/5e/0138bc4484ea9b897864d59fce9be9086030825bc778b76cb5a33a906d37/xxhash-3.6.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a40a3d35b204b7cc7643cbcf8c9976d818cb47befcfac8bbefec8038ac363f3e", size = 32754, upload-time = "2025-10-02T14:35:38.245Z" },
1707
+ { url = "https://files.pythonhosted.org/packages/18/d7/5dac2eb2ec75fd771957a13e5dda560efb2176d5203f39502a5fc571f899/xxhash-3.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a54844be970d3fc22630b32d515e79a90d0a3ddb2644d8d7402e3c4c8da61405", size = 30846, upload-time = "2025-10-02T14:35:39.6Z" },
1708
+ { url = "https://files.pythonhosted.org/packages/fe/71/8bc5be2bb00deb5682e92e8da955ebe5fa982da13a69da5a40a4c8db12fb/xxhash-3.6.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:016e9190af8f0a4e3741343777710e3d5717427f175adfdc3e72508f59e2a7f3", size = 194343, upload-time = "2025-10-02T14:35:40.69Z" },
1709
+ { url = "https://files.pythonhosted.org/packages/e7/3b/52badfb2aecec2c377ddf1ae75f55db3ba2d321c5e164f14461c90837ef3/xxhash-3.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f6f72232f849eb9d0141e2ebe2677ece15adfd0fa599bc058aad83c714bb2c6", size = 213074, upload-time = "2025-10-02T14:35:42.29Z" },
1710
+ { url = "https://files.pythonhosted.org/packages/a2/2b/ae46b4e9b92e537fa30d03dbc19cdae57ed407e9c26d163895e968e3de85/xxhash-3.6.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63275a8aba7865e44b1813d2177e0f5ea7eadad3dd063a21f7cf9afdc7054063", size = 212388, upload-time = "2025-10-02T14:35:43.929Z" },
1711
+ { url = "https://files.pythonhosted.org/packages/f5/80/49f88d3afc724b4ac7fbd664c8452d6db51b49915be48c6982659e0e7942/xxhash-3.6.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cd01fa2aa00d8b017c97eb46b9a794fbdca53fc14f845f5a328c71254b0abb7", size = 445614, upload-time = "2025-10-02T14:35:45.216Z" },
1712
+ { url = "https://files.pythonhosted.org/packages/ed/ba/603ce3961e339413543d8cd44f21f2c80e2a7c5cfe692a7b1f2cccf58f3c/xxhash-3.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0226aa89035b62b6a86d3c68df4d7c1f47a342b8683da2b60cedcddb46c4d95b", size = 194024, upload-time = "2025-10-02T14:35:46.959Z" },
1713
+ { url = "https://files.pythonhosted.org/packages/78/d1/8e225ff7113bf81545cfdcd79eef124a7b7064a0bba53605ff39590b95c2/xxhash-3.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6e193e9f56e4ca4923c61238cdaced324f0feac782544eb4c6d55ad5cc99ddd", size = 210541, upload-time = "2025-10-02T14:35:48.301Z" },
1714
+ { url = "https://files.pythonhosted.org/packages/6f/58/0f89d149f0bad89def1a8dd38feb50ccdeb643d9797ec84707091d4cb494/xxhash-3.6.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9176dcaddf4ca963d4deb93866d739a343c01c969231dbe21680e13a5d1a5bf0", size = 198305, upload-time = "2025-10-02T14:35:49.584Z" },
1715
+ { url = "https://files.pythonhosted.org/packages/11/38/5eab81580703c4df93feb5f32ff8fa7fe1e2c51c1f183ee4e48d4bb9d3d7/xxhash-3.6.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c1ce4009c97a752e682b897aa99aef84191077a9433eb237774689f14f8ec152", size = 210848, upload-time = "2025-10-02T14:35:50.877Z" },
1716
+ { url = "https://files.pythonhosted.org/packages/5e/6b/953dc4b05c3ce678abca756416e4c130d2382f877a9c30a20d08ee6a77c0/xxhash-3.6.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:8cb2f4f679b01513b7adbb9b1b2f0f9cdc31b70007eaf9d59d0878809f385b11", size = 414142, upload-time = "2025-10-02T14:35:52.15Z" },
1717
+ { url = "https://files.pythonhosted.org/packages/08/a9/238ec0d4e81a10eb5026d4a6972677cbc898ba6c8b9dbaec12ae001b1b35/xxhash-3.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:653a91d7c2ab54a92c19ccf43508b6a555440b9be1bc8be553376778be7f20b5", size = 191547, upload-time = "2025-10-02T14:35:53.547Z" },
1718
+ { url = "https://files.pythonhosted.org/packages/f1/ee/3cf8589e06c2164ac77c3bf0aa127012801128f1feebf2a079272da5737c/xxhash-3.6.0-cp314-cp314-win32.whl", hash = "sha256:a756fe893389483ee8c394d06b5ab765d96e68fbbfe6fde7aa17e11f5720559f", size = 31214, upload-time = "2025-10-02T14:35:54.746Z" },
1719
+ { url = "https://files.pythonhosted.org/packages/02/5d/a19552fbc6ad4cb54ff953c3908bbc095f4a921bc569433d791f755186f1/xxhash-3.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:39be8e4e142550ef69629c9cd71b88c90e9a5db703fecbcf265546d9536ca4ad", size = 32290, upload-time = "2025-10-02T14:35:55.791Z" },
1720
+ { url = "https://files.pythonhosted.org/packages/b1/11/dafa0643bc30442c887b55baf8e73353a344ee89c1901b5a5c54a6c17d39/xxhash-3.6.0-cp314-cp314-win_arm64.whl", hash = "sha256:25915e6000338999236f1eb68a02a32c3275ac338628a7eaa5a269c401995679", size = 28795, upload-time = "2025-10-02T14:35:57.162Z" },
1721
+ { url = "https://files.pythonhosted.org/packages/2c/db/0e99732ed7f64182aef4a6fb145e1a295558deec2a746265dcdec12d191e/xxhash-3.6.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5294f596a9017ca5a3e3f8884c00b91ab2ad2933cf288f4923c3fd4346cf3d4", size = 32955, upload-time = "2025-10-02T14:35:58.267Z" },
1722
+ { url = "https://files.pythonhosted.org/packages/55/f4/2a7c3c68e564a099becfa44bb3d398810cc0ff6749b0d3cb8ccb93f23c14/xxhash-3.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1cf9dcc4ab9cff01dfbba78544297a3a01dafd60f3bde4e2bfd016cf7e4ddc67", size = 31072, upload-time = "2025-10-02T14:35:59.382Z" },
1723
+ { url = "https://files.pythonhosted.org/packages/c6/d9/72a29cddc7250e8a5819dad5d466facb5dc4c802ce120645630149127e73/xxhash-3.6.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01262da8798422d0685f7cef03b2bd3f4f46511b02830861df548d7def4402ad", size = 196579, upload-time = "2025-10-02T14:36:00.838Z" },
1724
+ { url = "https://files.pythonhosted.org/packages/63/93/b21590e1e381040e2ca305a884d89e1c345b347404f7780f07f2cdd47ef4/xxhash-3.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51a73fb7cb3a3ead9f7a8b583ffd9b8038e277cdb8cb87cf890e88b3456afa0b", size = 215854, upload-time = "2025-10-02T14:36:02.207Z" },
1725
+ { url = "https://files.pythonhosted.org/packages/ce/b8/edab8a7d4fa14e924b29be877d54155dcbd8b80be85ea00d2be3413a9ed4/xxhash-3.6.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b9c6df83594f7df8f7f708ce5ebeacfc69f72c9fbaaababf6cf4758eaada0c9b", size = 214965, upload-time = "2025-10-02T14:36:03.507Z" },
1726
+ { url = "https://files.pythonhosted.org/packages/27/67/dfa980ac7f0d509d54ea0d5a486d2bb4b80c3f1bb22b66e6a05d3efaf6c0/xxhash-3.6.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:627f0af069b0ea56f312fd5189001c24578868643203bca1abbc2c52d3a6f3ca", size = 448484, upload-time = "2025-10-02T14:36:04.828Z" },
1727
+ { url = "https://files.pythonhosted.org/packages/8c/63/8ffc2cc97e811c0ca5d00ab36604b3ea6f4254f20b7bc658ca825ce6c954/xxhash-3.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa912c62f842dfd013c5f21a642c9c10cd9f4c4e943e0af83618b4a404d9091a", size = 196162, upload-time = "2025-10-02T14:36:06.182Z" },
1728
+ { url = "https://files.pythonhosted.org/packages/4b/77/07f0e7a3edd11a6097e990f6e5b815b6592459cb16dae990d967693e6ea9/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b465afd7909db30168ab62afe40b2fcf79eedc0b89a6c0ab3123515dc0df8b99", size = 213007, upload-time = "2025-10-02T14:36:07.733Z" },
1729
+ { url = "https://files.pythonhosted.org/packages/ae/d8/bc5fa0d152837117eb0bef6f83f956c509332ce133c91c63ce07ee7c4873/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a881851cf38b0a70e7c4d3ce81fc7afd86fbc2a024f4cfb2a97cf49ce04b75d3", size = 200956, upload-time = "2025-10-02T14:36:09.106Z" },
1730
+ { url = "https://files.pythonhosted.org/packages/26/a5/d749334130de9411783873e9b98ecc46688dad5db64ca6e04b02acc8b473/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9b3222c686a919a0f3253cfc12bb118b8b103506612253b5baeaac10d8027cf6", size = 213401, upload-time = "2025-10-02T14:36:10.585Z" },
1731
+ { url = "https://files.pythonhosted.org/packages/89/72/abed959c956a4bfc72b58c0384bb7940663c678127538634d896b1195c10/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:c5aa639bc113e9286137cec8fadc20e9cd732b2cc385c0b7fa673b84fc1f2a93", size = 417083, upload-time = "2025-10-02T14:36:12.276Z" },
1732
+ { url = "https://files.pythonhosted.org/packages/0c/b3/62fd2b586283b7d7d665fb98e266decadf31f058f1cf6c478741f68af0cb/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c1343d49ac102799905e115aee590183c3921d475356cb24b4de29a4bc56518", size = 193913, upload-time = "2025-10-02T14:36:14.025Z" },
1733
+ { url = "https://files.pythonhosted.org/packages/9a/9a/c19c42c5b3f5a4aad748a6d5b4f23df3bed7ee5445accc65a0fb3ff03953/xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119", size = 31586, upload-time = "2025-10-02T14:36:15.603Z" },
1734
+ { url = "https://files.pythonhosted.org/packages/03/d6/4cc450345be9924fd5dc8c590ceda1db5b43a0a889587b0ae81a95511360/xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f", size = 32526, upload-time = "2025-10-02T14:36:16.708Z" },
1735
+ { url = "https://files.pythonhosted.org/packages/0f/c9/7243eb3f9eaabd1a88a5a5acadf06df2d83b100c62684b7425c6a11bcaa8/xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95", size = 28898, upload-time = "2025-10-02T14:36:17.843Z" },
1736
+ ]
1737
+
1738
  [[package]]
1739
  name = "yarl"
1740
  version = "1.22.0"