diff --git a/.gitignore b/.gitignore index 8d40064..98b7c0f 100644 --- a/.gitignore +++ b/.gitignore @@ -163,4 +163,6 @@ pyrightconfig.json # direnv files, used by load python venv .direnv/ -.envrc \ No newline at end of file +.envrc +.local.env +.envrc diff --git a/README.md b/README.md index 944fd70..ecf5ee6 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,7 @@ A `Makefile` is provided with the following targets: - `htmlcov` : run the unit tests and generate a full report in htmlcov/ Testing and coverage requires standing up a local testbed. For details, see [Design](docs/design.md). + + +## Adding `llm-redactor` branch +For the redactor feature. \ No newline at end of file diff --git a/data/custom_fields.json b/data/custom_fields.json index 127173f..26b8b41 100644 --- a/data/custom_fields.json +++ b/data/custom_fields.json @@ -1 +1,207 @@ -{"custom_fields":[{"id":2,"name":"Discord ID","description":"ID used to link user to their discord account, to enable integration","customized_type":"user","field_format":"string","regexp":"","min_length":null,"max_length":null,"is_required":false,"is_filter":false,"searchable":false,"multiple":false,"default_value":"","visible":true},{"id":4,"name":"syncdata","description":"Metadata used to sync the ticket with external sources.\r\n\r\nCurrent format is thread-id|zulu-timestamp.\r\n\r\nNot recommend to edit.","customized_type":"issue","field_format":"string","regexp":"","min_length":null,"max_length":null,"is_required":false,"is_filter":true,"searchable":false,"multiple":false,"default_value":"","visible":false,"trackers":[{"id":2,"name":"Infra-Field"},{"id":4,"name":"Software-Dev"},{"id":6,"name":"Infra-Config"},{"id":8,"name":"External-Comms-Intake"},{"id":9,"name":"Outreach-Partnerships"},{"id":10,"name":"Admin"},{"id":17,"name":"Research"},{"id":18,"name":"Mutual-Aid-Action"},{"id":19,"name":"SCN-Space"}],"roles":[{"id":3,"name":"Administrator"}]},{"id":5,"name":"To/CC","description":"Contains the To and Cc headers from the email that created the ticket.","customized_type":"issue","field_format":"string","regexp":"","min_length":null,"max_length":null,"is_required":false,"is_filter":false,"searchable":true,"multiple":false,"default_value":"","visible":true,"trackers":[{"id":2,"name":"Infra-Field"},{"id":4,"name":"Software-Dev"},{"id":6,"name":"Infra-Config"},{"id":8,"name":"External-Comms-Intake"},{"id":9,"name":"Outreach-Partnerships"},{"id":10,"name":"Admin"},{"id":13,"name":"Test-Reject"},{"id":17,"name":"Research"},{"id":18,"name":"Mutual-Aid-Action"},{"id":19,"name":"SCN-Space"}],"roles":[]}]} +{ + "custom_fields": [ + { + "id": 2, + "name": "Discord ID", + "description": "ID used to link user to their discord account, to enable integration", + "customized_type": "user", + "field_format": "string", + "regexp": "", + "min_length": null, + "max_length": null, + "is_required": false, + "is_filter": false, + "searchable": false, + "multiple": false, + "default_value": "", + "visible": true, + "editable": true + }, + { + "id": 4, + "name": "syncdata", + "description": "Metadata used to sync the ticket with external sources.\r\n\r\nCurrent format is thread-id|zulu-timestamp.\r\n\r\nNot recommend to edit.", + "customized_type": "issue", + "field_format": "string", + "regexp": "", + "min_length": null, + "max_length": null, + "is_required": false, + "is_filter": true, + "searchable": false, + "multiple": false, + "default_value": "", + "visible": false, + "editable": true, + "trackers": [ + { + "id": 2, + "name": "Infra-Field" + }, + { + "id": 4, + "name": "Software-Dev" + }, + { + "id": 6, + "name": "Infra-Config" + }, + { + "id": 8, + "name": "External-Comms-Intake" + }, + { + "id": 9, + "name": "Outreach-Partnerships" + }, + { + "id": 10, + "name": "Admin" + }, + { + "id": 17, + "name": "Research" + }, + { + "id": 18, + "name": "Mutual-Aid-Action" + }, + { + "id": 19, + "name": "SCN-Space" + } + ], + "roles": [ + { + "id": 3, + "name": "Administrator" + } + ] + }, + { + "id": 5, + "name": "To/CC", + "description": "Contains the To and Cc headers from the email that created the ticket.", + "customized_type": "issue", + "field_format": "string", + "regexp": "", + "min_length": null, + "max_length": null, + "is_required": false, + "is_filter": false, + "searchable": true, + "multiple": false, + "default_value": "", + "visible": true, + "editable": true, + "trackers": [ + { + "id": 2, + "name": "Infra-Field" + }, + { + "id": 4, + "name": "Software-Dev" + }, + { + "id": 6, + "name": "Infra-Config" + }, + { + "id": 8, + "name": "External-Comms-Intake" + }, + { + "id": 9, + "name": "Outreach-Partnerships" + }, + { + "id": 10, + "name": "Admin" + }, + { + "id": 13, + "name": "Test-Reject" + }, + { + "id": 17, + "name": "Research" + }, + { + "id": 18, + "name": "Mutual-Aid-Action" + }, + { + "id": 19, + "name": "SCN-Space" + } + ], + "roles": [] + }, + { + "id": 6, + "name": "redacted", + "description": "Keys and values for the redacted fields in the ticket.", + "customized_type": "issue", + "field_format": "string", + "regexp": "", + "min_length": null, + "max_length": null, + "is_required": false, + "is_filter": false, + "searchable": false, + "multiple": false, + "default_value": "", + "visible": false, + "editable": true, + "trackers": [ + { + "id": 2, + "name": "Infra-Field" + }, + { + "id": 4, + "name": "Software-Dev" + }, + { + "id": 6, + "name": "Infra-Config" + }, + { + "id": 8, + "name": "External-Comms-Intake" + }, + { + "id": 9, + "name": "Outreach-Partnerships" + }, + { + "id": 10, + "name": "Admin" + }, + { + "id": 13, + "name": "Test-Reject" + }, + { + "id": 17, + "name": "Research" + }, + { + "id": 18, + "name": "Mutual-Aid-Action" + }, + { + "id": 19, + "name": "SCN-Space" + } + ], + "roles": [ + { + "id": 3, + "name": "Administrator" + } + ] + } + ] +} \ No newline at end of file diff --git a/finetuning/.gitignore b/finetuning/.gitignore new file mode 100644 index 0000000..eb5a0c9 --- /dev/null +++ b/finetuning/.gitignore @@ -0,0 +1,15 @@ +venv/ +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +*.so +*.egg +*.egg-info/ +dist/ +build/ +pii-redactor-fused/ +pii-redactor-finetune.tar +data/ +.DS_Store diff --git a/finetuning/README.md b/finetuning/README.md new file mode 100644 index 0000000..d8e4225 --- /dev/null +++ b/finetuning/README.md @@ -0,0 +1,51 @@ +# PII Redactor - Setup & Usage + +## Installation + +# 1. Clone repository +git clone https://github.com/YOUR_USERNAME/pii-redactor.git +cd pii-redactor + +# 2. Create virtual environment (recommended) +python3 -m venv venv +source venv/bin/activate + +# 3. Install dependencies +pip install mlx mlx-lm + +## Usage + +# Basic usage +./redact.py "Contact John Doe at john@example.com or call 555-123-4567" + +# From stdin +echo "Server at 192.168.1.100" | ./redact.py + +# From file +cat ticket.txt | ./redact.py + +## Expected Output + +Input: +"Contact John Doe at john@example.com or call 555-123-4567" + +Output: +{ + "redacted_text": "Contact John [LastName1] at [Email2] or call [Phone3]", + "properties_redacted": { + "lastname1": "Doe", + "email2": "john@example.com", + "phone3": "555-123-4567" + } +} + +## Requirements + +- Mac with Apple Silicon (M1/M2/M3/M4) +- Python 3.9+ +- 8GB+ RAM + +## First Run + +- Downloads base model (~700MB) - takes 2-3 minutes +- Subsequent runs are instant \ No newline at end of file diff --git a/finetuning/adapters/pii-redactor-3b-varied/0000200_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0000200_adapters.safetensors new file mode 100644 index 0000000..cbe64dd Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0000200_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0000400_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0000400_adapters.safetensors new file mode 100644 index 0000000..1467818 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0000400_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0000600_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0000600_adapters.safetensors new file mode 100644 index 0000000..c930b72 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0000600_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0000800_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0000800_adapters.safetensors new file mode 100644 index 0000000..610ec9a Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0000800_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0001000_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0001000_adapters.safetensors new file mode 100644 index 0000000..6e380eb Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0001000_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0001200_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0001200_adapters.safetensors new file mode 100644 index 0000000..4a5ffe7 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0001200_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0001400_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0001400_adapters.safetensors new file mode 100644 index 0000000..955a67c Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0001400_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0001600_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0001600_adapters.safetensors new file mode 100644 index 0000000..38d1553 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0001600_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0001800_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0001800_adapters.safetensors new file mode 100644 index 0000000..e4ffd9b Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0001800_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/0002000_adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/0002000_adapters.safetensors new file mode 100644 index 0000000..05ccec1 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/0002000_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b-varied/adapter_config.json b/finetuning/adapters/pii-redactor-3b-varied/adapter_config.json new file mode 100644 index 0000000..e8198f6 --- /dev/null +++ b/finetuning/adapters/pii-redactor-3b-varied/adapter_config.json @@ -0,0 +1,40 @@ +{ + "adapter_path": "adapters/pii-redactor-3b-varied", + "batch_size": 2, + "config": null, + "data": "data", + "fine_tune_type": "lora", + "grad_accumulation_steps": 1, + "grad_checkpoint": false, + "iters": 2000, + "learning_rate": 1e-05, + "lora_parameters": { + "rank": 8, + "dropout": 0.0, + "scale": 20.0 + }, + "lr_schedule": null, + "mask_prompt": false, + "max_seq_length": 2048, + "model": "mlx-community/Llama-3.2-3B-Instruct-4bit", + "num_layers": 16, + "optimizer": "adam", + "optimizer_config": { + "adam": {}, + "adamw": {}, + "muon": {}, + "sgd": {}, + "adafactor": {} + }, + "project_name": null, + "report_to": null, + "resume_adapter_file": null, + "save_every": 200, + "seed": 0, + "steps_per_eval": 100, + "steps_per_report": 10, + "test": false, + "test_batches": 500, + "train": true, + "val_batches": 25 +} \ No newline at end of file diff --git a/finetuning/adapters/pii-redactor-3b-varied/adapters.safetensors b/finetuning/adapters/pii-redactor-3b-varied/adapters.safetensors new file mode 100644 index 0000000..05ccec1 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b-varied/adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000100_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000100_adapters.safetensors new file mode 100644 index 0000000..c5da0de Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000100_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000200_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000200_adapters.safetensors new file mode 100644 index 0000000..ad2c21c Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000200_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000300_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000300_adapters.safetensors new file mode 100644 index 0000000..f707b96 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000300_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000400_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000400_adapters.safetensors new file mode 100644 index 0000000..2c7ff74 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000400_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000500_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000500_adapters.safetensors new file mode 100644 index 0000000..fdd396b Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000500_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000600_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000600_adapters.safetensors new file mode 100644 index 0000000..5c58917 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000600_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000700_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000700_adapters.safetensors new file mode 100644 index 0000000..80ab48a Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000700_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000800_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000800_adapters.safetensors new file mode 100644 index 0000000..8a3cbd1 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000800_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0000900_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0000900_adapters.safetensors new file mode 100644 index 0000000..89b06af Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0000900_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/0001000_adapters.safetensors b/finetuning/adapters/pii-redactor-3b/0001000_adapters.safetensors new file mode 100644 index 0000000..f980d55 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/0001000_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor-3b/adapter_config.json b/finetuning/adapters/pii-redactor-3b/adapter_config.json new file mode 100644 index 0000000..c47e2c1 --- /dev/null +++ b/finetuning/adapters/pii-redactor-3b/adapter_config.json @@ -0,0 +1,40 @@ +{ + "adapter_path": "adapters/pii-redactor-3b", + "batch_size": 2, + "config": null, + "data": "data", + "fine_tune_type": "lora", + "grad_accumulation_steps": 1, + "grad_checkpoint": false, + "iters": 1000, + "learning_rate": 1e-05, + "lora_parameters": { + "rank": 8, + "dropout": 0.0, + "scale": 20.0 + }, + "lr_schedule": null, + "mask_prompt": false, + "max_seq_length": 2048, + "model": "mlx-community/Llama-3.2-3B-Instruct-4bit", + "num_layers": 8, + "optimizer": "adam", + "optimizer_config": { + "adam": {}, + "adamw": {}, + "muon": {}, + "sgd": {}, + "adafactor": {} + }, + "project_name": null, + "report_to": null, + "resume_adapter_file": null, + "save_every": 100, + "seed": 0, + "steps_per_eval": 100, + "steps_per_report": 10, + "test": false, + "test_batches": 500, + "train": true, + "val_batches": 25 +} \ No newline at end of file diff --git a/finetuning/adapters/pii-redactor-3b/adapters.safetensors b/finetuning/adapters/pii-redactor-3b/adapters.safetensors new file mode 100644 index 0000000..f980d55 Binary files /dev/null and b/finetuning/adapters/pii-redactor-3b/adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000040_adapters.safetensors b/finetuning/adapters/pii-redactor/0000040_adapters.safetensors new file mode 100644 index 0000000..397f8b5 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000040_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000080_adapters.safetensors b/finetuning/adapters/pii-redactor/0000080_adapters.safetensors new file mode 100644 index 0000000..3ef5129 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000080_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000120_adapters.safetensors b/finetuning/adapters/pii-redactor/0000120_adapters.safetensors new file mode 100644 index 0000000..3aa6120 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000120_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000160_adapters.safetensors b/finetuning/adapters/pii-redactor/0000160_adapters.safetensors new file mode 100644 index 0000000..638a165 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000160_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000200_adapters.safetensors b/finetuning/adapters/pii-redactor/0000200_adapters.safetensors new file mode 100644 index 0000000..416275c Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000200_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000240_adapters.safetensors b/finetuning/adapters/pii-redactor/0000240_adapters.safetensors new file mode 100644 index 0000000..ef2d5ca Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000240_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000280_adapters.safetensors b/finetuning/adapters/pii-redactor/0000280_adapters.safetensors new file mode 100644 index 0000000..a15d62b Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000280_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000320_adapters.safetensors b/finetuning/adapters/pii-redactor/0000320_adapters.safetensors new file mode 100644 index 0000000..ac770fe Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000320_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000360_adapters.safetensors b/finetuning/adapters/pii-redactor/0000360_adapters.safetensors new file mode 100644 index 0000000..7c3854a Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000360_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000400_adapters.safetensors b/finetuning/adapters/pii-redactor/0000400_adapters.safetensors new file mode 100644 index 0000000..935eb9a Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000400_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000440_adapters.safetensors b/finetuning/adapters/pii-redactor/0000440_adapters.safetensors new file mode 100644 index 0000000..d986bc6 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000440_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000480_adapters.safetensors b/finetuning/adapters/pii-redactor/0000480_adapters.safetensors new file mode 100644 index 0000000..ac6f638 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000480_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000520_adapters.safetensors b/finetuning/adapters/pii-redactor/0000520_adapters.safetensors new file mode 100644 index 0000000..c136e5b Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000520_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000560_adapters.safetensors b/finetuning/adapters/pii-redactor/0000560_adapters.safetensors new file mode 100644 index 0000000..313270d Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000560_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000600_adapters.safetensors b/finetuning/adapters/pii-redactor/0000600_adapters.safetensors new file mode 100644 index 0000000..3635b39 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000600_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000640_adapters.safetensors b/finetuning/adapters/pii-redactor/0000640_adapters.safetensors new file mode 100644 index 0000000..c148f81 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000640_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000680_adapters.safetensors b/finetuning/adapters/pii-redactor/0000680_adapters.safetensors new file mode 100644 index 0000000..97066fe Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000680_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000720_adapters.safetensors b/finetuning/adapters/pii-redactor/0000720_adapters.safetensors new file mode 100644 index 0000000..a7b39e2 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000720_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000760_adapters.safetensors b/finetuning/adapters/pii-redactor/0000760_adapters.safetensors new file mode 100644 index 0000000..16d37cb Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000760_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000800_adapters.safetensors b/finetuning/adapters/pii-redactor/0000800_adapters.safetensors new file mode 100644 index 0000000..b36e67c Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000800_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000840_adapters.safetensors b/finetuning/adapters/pii-redactor/0000840_adapters.safetensors new file mode 100644 index 0000000..98170b8 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000840_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000880_adapters.safetensors b/finetuning/adapters/pii-redactor/0000880_adapters.safetensors new file mode 100644 index 0000000..d428787 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000880_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000920_adapters.safetensors b/finetuning/adapters/pii-redactor/0000920_adapters.safetensors new file mode 100644 index 0000000..919d073 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000920_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0000960_adapters.safetensors b/finetuning/adapters/pii-redactor/0000960_adapters.safetensors new file mode 100644 index 0000000..8c9eeb9 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0000960_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/0001000_adapters.safetensors b/finetuning/adapters/pii-redactor/0001000_adapters.safetensors new file mode 100644 index 0000000..91d2519 Binary files /dev/null and b/finetuning/adapters/pii-redactor/0001000_adapters.safetensors differ diff --git a/finetuning/adapters/pii-redactor/adapter_config.json b/finetuning/adapters/pii-redactor/adapter_config.json new file mode 100644 index 0000000..ef949dc --- /dev/null +++ b/finetuning/adapters/pii-redactor/adapter_config.json @@ -0,0 +1,40 @@ +{ + "adapter_path": "adapters/pii-redactor", + "batch_size": 1, + "config": null, + "data": "data", + "fine_tune_type": "lora", + "grad_accumulation_steps": 1, + "grad_checkpoint": false, + "iters": 1000, + "learning_rate": 1e-05, + "lora_parameters": { + "rank": 8, + "dropout": 0.0, + "scale": 20.0 + }, + "lr_schedule": null, + "mask_prompt": false, + "max_seq_length": 2048, + "model": "mlx-community/Llama-3.2-3B-Instruct-4bit", + "num_layers": 8, + "optimizer": "adam", + "optimizer_config": { + "adam": {}, + "adamw": {}, + "muon": {}, + "sgd": {}, + "adafactor": {} + }, + "project_name": null, + "report_to": null, + "resume_adapter_file": null, + "save_every": 40, + "seed": 0, + "steps_per_eval": 40, + "steps_per_report": 10, + "test": false, + "test_batches": 500, + "train": true, + "val_batches": 25 +} \ No newline at end of file diff --git a/finetuning/adapters/pii-redactor/adapters.safetensors b/finetuning/adapters/pii-redactor/adapters.safetensors new file mode 100644 index 0000000..91d2519 Binary files /dev/null and b/finetuning/adapters/pii-redactor/adapters.safetensors differ diff --git a/finetuning/redact.py b/finetuning/redact.py new file mode 100755 index 0000000..6a5fddc --- /dev/null +++ b/finetuning/redact.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +"""Simple PII redactor""" +import sys +from mlx_lm import load, generate + +print("Loading model...", file=sys.stderr) +model, tokenizer = load( + "mlx-community/Llama-3.2-3B-Instruct-4bit", + adapter_path="adapters/pii-redactor-3b-varied" +) + +# Get input +if len(sys.argv) > 1: + text = " ".join(sys.argv[1:]) +else: + text = sys.stdin.read().strip() + +system_prompt = """You are a **privacy compliance officer** responsible for redacting **personally identifiable information (PII)** from **Redmine support tickets** before they are shared on public platforms like Discord. + + Your primary goal is to **remove PII while preserving ticket clarity and structure** so that it remains **useful for troubleshooting and public discussion.** + ALWAYS REMEMBER: never summarize a ticket. redact per instructions but never summarize or shorten excessively + + --- + + ## **Strict Redaction Rules:** + Apply these redaction rules carefully: + + ### **1. Names** + Replace all personal names with placeholders + Note: If there's only a first name, leave as is. If both first and last names are present, redact the last name only. + using **original firstname [LastNameN]**, where **N** is a unique number per name within the ticket. + + - Example: + - **Original:** *Chris Caputo reported an issue with network latency.* + - **Redacted:** *Chris [LastName1] reported an issue with network latency.* + + - Example: + - **Original:** *John reported an issue with network latency.* + - **Redacted:** *John reported an issue with network latency.* + + - Example (Multiple people): + - **Original:** *Esther Jang contacted Alice about the issue.* + - **Redacted:** *Esther [LastName1] contacted Alice about the issue.* + + - Example (Multiple people): + - **Original:** *Esther Jang contacted Alice Beatrice about the issue.* + - **Redacted:** *Esther [LastName1] contacted Alice [LastName2] about the issue.* + + ### **2. Emails** + Replace all email addresses with **[EmailN]**, ensuring correct numbering within the ticket. + + - Example: + - **Original:** *Please contact john.doe@example.com for assistance.* + - **Redacted:** *Please contact [Email1] for assistance.* + + - **Original:** *infrared@cs.washington.edu requested access to the document.* + - **Redacted:** *[Email2] requested access to the document.* + + ### **3. Phone Numbers** + Replace all phone numbers with **[PhoneN]**. + + - Example: + - **Original:** *Call us at +1 (555) 123-4567 for support.* + - **Redacted:** *Call us at [Phone1] for support.* + + ### **4. Physical Addresses** + Replace all physical addresses (including partial ones) with **[AddressN]**. + + - Example: + - **Original:** *The router is located at 1234 Elm St, Seattle, WA 98101.* + - **Redacted:** *The router is located at [Address1].* + + ### **5. IP & MAC Addresses** + Replace all IP addresses (both IPv4 and IPv6) and MAC addresses with **[IPN]** and **[MACN]**, respectively. + + - Example: + - **Original:** *Router IP: 192.168.1.100, IPv6: 2001:db8::ff00:42:8329.* + - **Redacted:** *Router IP: [IP1], IPv6: [IP2].* + + - **Original:** *Device MAC: AA:BB:CC:DD:EE:FF.* + - **Redacted:** *Device MAC: [MAC1].* + + ### **6. Public Keys & Login Credentials** + Replace cryptographic keys (such as SSH, PGP, and API keys) with **[PublicKeyN]**. + + - Example: + - **Original:** *PGP Key: 1280 AFA4 DD14 589B.* + - **Redacted:** *PGP Key: [PublicKey1].* + + Replace **any login credentials** (usernames and passwords) with **[UsernameN]** and **[PasswordN]**. + + - Example: + - **Original:** *Username: admin, Password: pass123!* + - **Redacted:** *Username: [Username1], Password: [Password1].* + + ### **7. Links & URLs** + - Replace **personal document-sharing links** with **[Document Link]**. + - Keep **institutional URLs** **(such as Seattle Community Network pages and PeeringDB links)** intact. + + - Example: + - **Original:** *Google Doc: https://docs.google.com/document/d/xyz123.* + - **Redacted:** *Google Doc: [Document Link].* + + - **Original:** *Seattle IX route servers: https://www.seattleix.net/route-servers.* + - **Redacted:** *Seattle IX route servers: https://www.seattleix.net/route-servers.* (Kept because it is public knowledge) + + ### **8. Message Context Preservation** + - **DO NOT** modify technical details, ticket metadata, or non-PII network-related content. + - **DO NOT** over-redact information that is **public knowledge or operationally relevant**. + + --- + + ## **Handling Multi-User Interactions** + When a ticket has **multiple users**, assign **unique placeholders per person** to maintain readability: + + - **Original:** +``` + Chris Caputo: Please fix this issue. + Esther Jang: I have escalated this to IT. + Chris Caputo: Thank you! +``` + - **Redacted:** +``` + [FirstName1] [LastName1]: Please fix this issue. + [FirstName2] [LastName2]: I have escalated this to IT. + [FirstName1] [LastName1]: Thank you! +``` + + --- + + ALWAYS REMEMBER: never summarize a ticket. redact per instructions but never summarize or shorten excessively + + You must output in JSON format with: + { + "redacted_text": "the fully redacted text", + "properties_redacted": { + "lastname1": "original last name", + "email1": "original email", + "ip1": "original IP", + ... + } + } + Do not include empty fields. Only include properties that were actually found and redacted. + +""" + +messages = [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": f"Redact all PII from this text and output in JSON format:\n\n{text}"} +] + +# Generate +prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) +result = generate(model, tokenizer, prompt=prompt, max_tokens=1024) + +# Extract JSON only +if '{' in result: + json_start = result.find('{') + json_end = result.rfind('}') + 1 + print(result[json_start:json_end]) +else: + print(result) diff --git a/netbot/cog_scn.py b/netbot/cog_scn.py index 9c3e2a5..410bce4 100644 --- a/netbot/cog_scn.py +++ b/netbot/cog_scn.py @@ -394,3 +394,31 @@ async def approve(self, ctx:discord.ApplicationContext): else: await ctx.respond("Must be authorized admin to approve Redmine users.") + + + # -------- + + # @scn.command(description="Redact a ticket") + # async def redact(self, ctx:discord.ApplicationContext, ticket_id: int): + # if self.is_admin(ctx.user): + # ticket = self.redmine.ticket_mgr.get(ticket_id) + # if ticket: + # ticket = self.redmine.redact_ticket(ticket) + # await self.bot.formatter.print_ticket(ticket, ctx) + # else: + # # TODO user error + # await ctx.respond(f"Ticket {ticket_id} not found.", ephemeral=True) + + + @scn.command(description="Display ticket details with redacted info displayed") + async def unredact(self, ctx:discord.ApplicationContext, ticket_id: int): + if self.is_admin(ctx.user): + ticket = self.redmine.ticket_mgr.get(ticket_id) + if ticket: + ticket = self.redmine.unredact_ticket(ticket) + #await self.bot.formatter.print_ticket(ticket, ctx) + await ctx.respond(embed=self.formatter.ticket_embed(ctx, ticket), ephemeral=True) + + else: + # TODO user error + await ctx.respond(f"Ticket {ticket_id} not found.") \ No newline at end of file diff --git a/netbot/formatting.py b/netbot/formatting.py index 4f8dc17..47362fd 100644 --- a/netbot/formatting.py +++ b/netbot/formatting.py @@ -349,7 +349,7 @@ def lookup_discord_user(self, ctx: discord.ApplicationContext, name:str) -> disc def get_user_id(self, ctx: discord.ApplicationContext, ticket:Ticket) -> str: if ticket is None or ticket.assigned_to is None: - return "" + return None user_str = self.format_discord_member(ctx, ticket.assigned_to.id) if not user_str: @@ -359,12 +359,12 @@ def get_user_id(self, ctx: discord.ApplicationContext, ticket:Ticket) -> str: def format_discord_member(self, ctx: discord.ApplicationContext, user_id:int) -> str: - user = ctx.bot.redmine.user_mgr.get(user_id) # call to cache + user = ctx.bot.redmine.user_mgr.cache.get(user_id) # call to cache directly if user and user.discord_id: return f"<@!{user.discord_id.id}>" if user: return user.name - return "" + return None def format_collaborators(self, ctx: discord.ApplicationContext, ticket:Ticket) -> str: diff --git a/pyproject.toml b/pyproject.toml index f5b6b8d..91264f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ dependencies = [ "dateparser>=1.2.2", "humanize>=4.12.0", "imapclient>=3.0.1", + "mlx-lm>=0.28.4", "py-cord>=2.6.1", "python-dotenv>=1.0.1", "requests>=2.32.3", diff --git a/redactor/__init__.py b/redactor/__init__.py new file mode 100644 index 0000000..a1d5447 --- /dev/null +++ b/redactor/__init__.py @@ -0,0 +1 @@ +"""tests module""" diff --git a/redactor/redactor.py b/redactor/redactor.py new file mode 100755 index 0000000..8ea51d2 --- /dev/null +++ b/redactor/redactor.py @@ -0,0 +1,459 @@ +"""Simple PII redactor""" + +import re +import json +import logging +import sys +from mlx_lm import load, generate + +log = logging.getLogger(__name__) + +default_model_file = "finetuning/adapters/pii-redactor" +model_name = "mlx-community/Llama-3.2-3B-Instruct-4bit" +system_prompt = """ + You are a **privacy compliance officer** responsible for redacting **personally identifiable information (PII)** from **Redmine support tickets** before they are shared on public platforms like Discord. + + Your primary goal is to **remove PII while preserving ticket clarity and structure** so that it remains **useful for troubleshooting and public discussion.** + NEVER invent or guess original values for properties_redacted. If the original value does not appear verbatim in the input text, do NOT create a placeholder or property. + ALWAYS REMEMBER: never summarize a ticket. redact per instructions but never summarize or shorten excessively + - If a value appears in properties_redacted, the original text MUST be replaced with the corresponding placeholder in redacted_text. + HashedCode redaction takes precedence over Emails, Public Keys, Phone Numbers, and Addresses. + A hashed code may only be classified once. + + --- + Rules: + - In properties_redacted, the value MUST always be the original unredacted text. NEVER use placeholders (e.g., [Phone1], [Email1]) as values. + - All placeholders use PascalCase with numeric suffixes consistently for all keys, inside both "redacted_text" and "properties_redacted" + Do not include empty fields. + In "properties_redacted", include only properties that were actually found and redacted. + Do not add any text outside the JSON object. + Only the following placeholder prefixes are allowed: + LastName, Email, Phone, Address, HashedCode, IP, MAC, PublicKey, Username, Password. + If a value appears in properties_redacted, the original text MUST be replaced with the corresponding placeholder in redacted_text. + + ## **Strict Redaction Rules:** + Apply these redaction rules carefully: + + For Phone and Address redaction: + - The model MUST read the entire number or address before emitting a placeholder. + - Emitting a placeholder before the full span is detected is INVALID. + - Partial replacement (e.g., replacing only an area code) is strictly forbidden. + - If uncertain, continue reading tokens until the full span is complete. + CRITICAL EMAIL RULE: + + The word "email", "emails", or "email address" by itself is NEVER PII. + If the input text does NOT contain a literal '@' character, you MUST NOT create an Email placeholder or Email property. + Violating this rule is INVALID OUTPUT. + TITLE DISAMBIGUATION RULE: + + Words following a first name that form a job title, department, or organization + (e.g., "Shelter Operations", "Network Engineer", "IT Support") + MUST NOT be treated as last names. + Only redact a last name if it is a personal surname. + + ### 1. Names + + If a name consists of a single word and does not appear alongside another name, it is a first name. + First names must NEVER be redacted, must NEVER be replaced with placeholders, and must NEVER appear in properties_redacted. + + Only redact last names when a full name (first + last) appears. + Use the format: original first name [LastNameN]. + + - Example: + - **Original:** *Chris Caputo reported an issue with network latency.* + - **Redacted:** *Chris [LastName1] reported an issue with network latency.* + + - Example: + - **Original:** *John reported an issue with network latency.* + - **Redacted:** *John reported an issue with network latency.* + + - Example (Multiple people): + - **Original:** *Eyoel Jin contacted Alice about the issue.* + - **Redacted:** *Eyoel [LastName1] contacted Alice about the issue.* + + - Example (Multiple people): + - **Original:** *Eyoel Jin contacted Alice Beatrice about the issue.* + - **Redacted:** *Eyoel [LastName1] contacted Alice [LastName2] about the issue.* + + - Example (Multiple people): + - **Original:** *Alert: Taylor Brown and Drew Thomas detected outage near 3672 Oak Ave, Tacoma, MT 96267. Phone 584-657-5661.* + - **Redacted:** *Alert: Taylor [LastName1] and Drew [LastName2] detected outage near [Address1], [City1], [State1] [Zip1]. Phone [Phone1].* + + ## 2. HASHED OR SYSTEM-GENERATED CODES + + When a ticket contains machine-generated identifiers (e.g., hexadecimal strings, reference codes, fingerprints, tracking IDs), redact them using `[HashedCodeN]`. + + - Treat grouped, uppercase, or hex-like alphanumeric strings as hashed codes. + - Assign incrementing numbers for distinct codes. + - Preserve spacing and punctuation outside the code. + - Do NOT classify these as emails, addresses, phone numbers, or personal IDs. + + Example: + + Original: + Thanks so much, + -Eyoel + 1210 N1A4 DM14 519B + + Redacted: + Thanks so much, + -Eyoel + [HashedCode1] + + Original: + Thanks so much, + -Eyoel 1210 N1A4 DM14 519B + + Redacted: + Thanks so much, + -Eyoel [HashedCode1] + + If multiple codes appear: + + Original: + Reference: 1210 N1A4 DM14 519B + Backup ID: 519B 88A1 N1A4 + + Redacted: + Reference: [HashedCode1] + Backup ID: [HashedCode2] + + --- + + ### Emails + + Redact ONLY literal email address strings that appear verbatim in the input text. + + A valid email address MUST: + - Contain the @ character + - Contain a domain after @ (e.g., example.com) + - Appear exactly as a substring in the input text + + STRICT PROHIBITIONS: + - NEVER infer or guess an email address + - NEVER fabricate an email address + - NEVER reuse an Email placeholder for non-email text + - NEVER redact or replace the words "email", "emails", or "email address" + - NEVER create an Email placeholder unless an actual email string appears in the input + + If no valid email address appears verbatim in the input text: + - DO NOT create an Email placeholder + - DO NOT add an Email entry to properties_redacted + Email addresses are ATOMIC spans. + + If a substring is identified as an email address: + - Redact the ENTIRE email address as a single unit + - DO NOT apply any other redaction rules inside the email + - DO NOT redact names, words, or subcomponents inside an email + - DO NOT partially redact an email address + + An email address must be replaced exactly once with [EmailN]. + + - Example: + - **Original:** *Please contact john.doe@example.com for assistance.* + - **Redacted:** *Please contact [Email1] for assistance.* + + - **Original:** *mira@cs.washington.edu requested access to the document.* + - **Redacted:** *[Email2] requested access to the document.* + + Original: Please contact Jamie Lee for assistance. + Redacted: Please contact Jamie Lee for assistance. + + Original: Please contact jamie.lee@example.com for assistance. + Redacted: Please contact [Email1] for assistance. + + Original: Hello, the resident wrote their contact as john dot doe at gmail dot com, so we don’t have a valid email. Please advise. Thanks, Liam + Redacted: Hello, the resident wrote their contact as [Email1], so we don’t have a valid email. Please advise. Thanks, Liam + + Original: Please contact Jamie Lee (jamie.lee@example.com) for assistance. + Redacted: Please contact Jamie Lee ([Email1]) for assistance. + + ### 3. Phone Numbers + + Replace all phone numbers with **[PhoneN]**, regardless of formatting, spacing, or punctuation. + + Phone numbers may appear in any of the following forms and **must always be redacted**: + + Original: 555-123-4567 + Redacted: [Phone1] + + Original: (555) 123-4567 + Redacted: [Phone1] + + Original: (555)-123-4567 + Redacted: [Phone1] + + Original: 555 123 4567 + Redacted: [Phone1] + + Original: 555.123.4567 + Redacted: [Phone1] + + Original: 5551234567 + Redacted: [Phone1] + + Original: +1 555 123 4567 + Redacted: [Phone1] + + Original: +1 (555) 123-4567 + Redacted: [Phone1] + + Original: 1-555-123-4567 + Redacted: [Phone1] + + Original: 1 (555) 123-4567 + Redacted: [Phone1] + + Original: call 555-123-4567 + Redacted: call [Phone1] + + Original: phone: (555)123-4567 + Redacted: phone: [Phone1] + + Original: Call us at +1 (555) 123-4567 for support. + Redacted: Call us at [Phone1] for support. + + EMAIL SIGN-OFF / SIGNATURE BLOCKS + Structured address parsing rules apply inside signature blocks exactly as in the body. + + When an email contains a sign-off such as: + - best regards + - thanks + - sincerely + - warm regards + - signature separators like `--` + + Apply redaction normally: + - Redact last names (keep first name) + - Redact phone numbers + - Redact addresses (using structured parsing when applicable) + + Example1: + Best, + Aman Habtai (He/Him) + 8531 Lake City Way NE + Seattle WA 98115 + (206)-702-6551 + + Redacted: + Aman [LastName1] (He/Him) + [StreetAddress1] + [CityName1] [StateName1] [ZipCode1] + [PhoneNumber1] + + Example2: + Best, + Eyoel + 8531 Lake City Way NE + (206)-702-6551 + + Redacted: + Eyoel + [StreetAddress1] + [PhoneNumber1] + + Example3: + Thanks, + Omar + + Redacted: + Thanks, + Omar + + Example4: + Warm Regards, + Betsie Sue + + Redacted: + Warm Regards, + Betsie [LastName1] + + Example5: + Thanks so much, + Rachel Kim 1280 AFA4 DD14 5898 + + Redacted: + Thanks so much, + Rachel [LastName1] [HashCode1] + + ### **4. Physical Addresses** + Replace all physical addresses (including partial ones) with **[AddressN]**. + + - Example: + - **Original:** *The router is located at 1234 Elm St, Seattle, WA 98101.* + - **Redacted:** *The router is located at [Address1].* + + ### **5. IP & MAC Addresses** + Replace all IP addresses (both IPv4 and IPv6) and MAC addresses with **[IPN]** and **[MACN]**, respectively. + + - Example: + - **Original:** *Router IP: 192.168.1.100, IPv6: 2001:db8::ff00:42:8329.* + - **Redacted:** *Router IP: [IP1], IPv6: [IP2].* + + - **Original:** *Device MAC: AA:BB:CC:DD:EE:FF.* + - **Redacted:** *Device MAC: [MAC1].* + + ### **6. Public Keys & Login Credentials** + Replace cryptographic keys (such as SSH, PGP, and API keys) with **[PublicKeyN]**. + + - Example: + - **Original:** *PGP Key: 1280 AFA4 DD14 589B.* + - **Redacted:** *PGP Key: [PublicKey1].* + + Usernames should ONLY be redacted if explicitly labeled (e.g., "username: admin"). Do NOT treat names, signatures, or sign-offs as usernames. + Replace **any login credentials** (usernames and passwords) with **[UsernameN]** and **[PasswordN]**. + + - Example: + - **Original:** *Username: admin, Password: pass123!* + - **Redacted:** *Username: [Username1], Password: [Password1].* + + ### **7. Links & URLs** + - Replace **personal document-sharing links** with **[Document Link]**. + - Keep **institutional URLs** **(such as Seattle Community Network pages and PeeringDB links)** intact. + + - Example: + - **Original:** *Google Doc: https://docs.google.com/document/d/xyz123.* + - **Redacted:** *Google Doc: [Document Link].* + + - **Original:** *Seattle IX route servers: https://www.seattleix.net/route-servers.* + - **Redacted:** *Seattle IX route servers: https://www.seattleix.net/route-servers.* (Kept because it is public knowledge) + + ### **8. Message Context Preservation** + - **DO NOT** modify technical details, ticket metadata, or non-PII network-related content. + - **DO NOT** over-redact information that is **public knowledge or operationally relevant**. + + --- + + ## **Handling Multi-User Interactions** + When a ticket has **multiple users**, assign **unique placeholders per person** to maintain readability: + + - **Original:** + ``` + Chris Caputo: Please fix this issue. + Eyoel Jin: I have escalated this to IT. + Chris Caputo: Thank you! + ``` + - **Redacted:** + ``` + Chris [LastName1]: Please fix this issue. + Eyoel [LastName2]: I have escalated this to IT. + Chris [LastName1]: Thank you! + ``` + + --- + + ALWAYS REMEMBER: never summarize a ticket. redact per instructions but never summarize or shorten excessively + + You must output in JSON format with: + { + "redacted_text": "the fully redacted text", + "properties_redacted": { + "LastName1": "original last name", + "Email1": "original email", + "Ip1": "original IP", + ... + } + } + Rules: + - In properties_redacted, the value MUST always be the original unredacted text. NEVER use placeholders (e.g., [Phone1], [Email1]) as values. + - All placeholders use PascalCase with numeric suffixes consistently for all keys, inside both "redacted_text" and "properties_redacted" + Do not include empty fields. + In "properties_redacted", include only properties that were actually found and redacted. + Do not add any text outside the JSON object. + Only the following placeholder prefixes are allowed: + LastName, Email, Phone, Address, HashedCode, IP, MAC, PublicKey, Username, Password. +""" + + +class RedactedText: + def __init__(self, text: str, fields: dict[str,str]): + self.text = text + self.fields = fields + + + @classmethod + def from_json(cls, json_str: str): + result = json.loads(json_str) + return cls(text=result['redacted_text'], fields=result['properties_redacted']) + + + def __str__(self): + return self.text + + + def unredact(self) -> str: + # retacted text format: Some text [aValue] more text [anotherValue] + # match all [], iterate over matches + pattern = re.compile(r"(\[\w+\])") + + restored_text = self.text + + # this is all about managing UpperCase vs lowercase keys + for match in pattern.finditer(self.text): + key = match.group(1) + lookup_key = key + #if lookup_key not in self.fields: + # lookup_key = lookup_key.lower() + if lookup_key not in self.fields: + log.error(f"Expected field, {key}, not provided.") + continue + value = self.fields[lookup_key] + restored_text = restored_text.replace(key, value) + + return restored_text + + +class Redactor: + def __init__(self, path: str = default_model_file): # relative to current module + log.info("Loading model from {path}") + self.model, self.tokenizer = load(model_name, adapter_path=path) + + + def redact_text(self, text: str) -> RedactedText: + text = text.strip() + messages = [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": f""" + You MUST output ONLY valid JSON. + Do NOT explain. + Do NOT include analysis. + Do NOT include notes. + Do NOT include text outside the JSON object. + Redact all PII from the following text according to the rules and output the result strictly in JSON format. + + TEXT: + {text} + """} + ] + prompt = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) + result = generate(self.model, self.tokenizer, prompt=prompt, max_tokens=1024) + + # Extract JSON only + if '{' in result: + json_start = result.find('{') + json_end = result.rfind('}') + 1 + result = result[json_start:json_end] + + return RedactedText.from_json(result.strip()) + + +def main(): + # Get input + if len(sys.argv) > 1: + text = " ".join(sys.argv[1:]) + else: + text = sys.stdin.read().strip() + + redactor = Redactor() + redacted = redactor.redact_text(text) + print("Input:", text) + print("Redacted:", redacted) + print("Fields:", redacted.fields) + print("Unredacted:", redacted.unredact()) + + + + +# Run the IMAP sync process +if __name__ == '__main__': + main() diff --git a/redmine/model.py b/redmine/model.py index bd5c7c1..6ad0f4a 100644 --- a/redmine/model.py +++ b/redmine/model.py @@ -334,6 +334,7 @@ class ParentTicket: SYNC_FIELD_NAME = "syncdata" TO_CC_FIELD_NAME = "To/CC" +REDACTOR_FIELD_NAME = "redacted" @dataclass @@ -484,6 +485,20 @@ def age_str(self) -> str: return synctime.age_str(self.updated_on) + @property + def redacted_fields(self) -> dict[str,str]: + val = self.get_custom_field(REDACTOR_FIELD_NAME) + if val: + # assume is json str + fields = json.loads(val) + return fields + + + @property + def is_redacted(self) -> bool: + return self.redacted_fields is not None + + def __str__(self): return f"#{self.id:04d} {self.status.name:<11} {self.priority.name:<6} {self.assigned:<20} {self.subject}" @@ -532,6 +547,7 @@ def get_notes(self, since:dt.datetime|None=None) -> list[TicketNote]: return notes + def get_field(self, fieldname:str): val = getattr(self, fieldname) return val diff --git a/redmine/redmine.py b/redmine/redmine.py index 7c93434..1cbe9ab 100644 --- a/redmine/redmine.py +++ b/redmine/redmine.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 """redmine client""" +import json import os import re import logging +from redactor.redactor import RedactedText, Redactor from redmine.session import RedmineSession -from redmine.model import Message, Ticket, User, NamedId +from redmine.model import REDACTOR_FIELD_NAME, Message, Ticket, User, NamedId from redmine.users import UserManager from redmine.tickets import TicketManager, SCN_PROJECT_ID @@ -39,6 +41,9 @@ def __init__(self, session:RedmineSession, user_mgr:UserManager, ticket_mgr:Tick self.user_mgr = user_mgr self.ticket_mgr = ticket_mgr + # Redactor - initialize, with default model location + self.redactor = Redactor() + # sanity check self.validate_sanity() # FATAL if not @@ -120,6 +125,10 @@ def create_ticket(self, user:User, message:Message) -> Ticket: log.info(f"Creating ticket with project={project_id} and tracker={tracker.id}") ticket = self.ticket_mgr.create(user, message, project_id=project_id, tracker_id=tracker.id) + # Redactor - this is where redaction happens + if self.redactor: + ticket = self.redactor.redact_ticket(ticket) + # check user status, reject the ticket if blocked if self.user_mgr.is_blocked(user): log.debug(f"Rejecting ticket #{ticket.id} based on blocked user {user.login}") @@ -128,6 +137,39 @@ def create_ticket(self, user:User, message:Message) -> Ticket: return ticket + # Redact a ticket. + # This will store the ticket as redacted. + def redact_ticket(self, ticket: Ticket) -> Ticket: + log.debug(f"Redacting : {ticket}") + redacted_desc = self.redactor.redact_text(ticket.description) + if len(redacted_desc.fields) > 0: + log.info(f"Redacted fields: {redacted_desc.fields.keys()}") + redaction_cf = self.ticket_mgr.get_custom_field(REDACTOR_FIELD_NAME) + redacted_fields_json = json.dumps(redacted_desc.fields) + + fields = { + "custom_fields": [ + { "id": redaction_cf.id, "value": redacted_fields_json } + ], + "description": str(redacted_desc) + } + return self.ticket_mgr.update(ticket.id, fields) + else: + log.debug(f"Nothing to redact on {ticket}") + return ticket + + + # returns an in-memory un-redacted copy of the ticket + # that works with the display info + def unredact_ticket(self, ticket:Ticket) -> Ticket: + field_str = ticket.get_custom_field(REDACTOR_FIELD_NAME) + if field_str: + fields = json.loads(field_str) + redacted = RedactedText(ticket.description, fields) + ticket.description = redacted.unredact() + return ticket + + def find_ticket_from_str(self, string:str) -> Ticket: """parse a ticket number from a string and get the associated ticket""" # for now, this is a trivial REGEX to match '#nnn' in a string, and return ticket #nnn diff --git a/redmine/users.py b/redmine/users.py index 0a05d39..1b7a81a 100644 --- a/redmine/users.py +++ b/redmine/users.py @@ -50,6 +50,7 @@ def cache_user(self, user: User) -> None: def cache_team(self, team: Team) -> None: """add the team to the cache""" self.teams[team.name] = team + self.user_ids[team.id] = team # hack to make teams visible by ID def get(self, user_id:int): @@ -73,6 +74,8 @@ def find(self, name): return self.get(self.discord_ids[name]) if name in self.teams: return self.teams[name] #ugly. put groups in user collection? + if name in self.user_ids: + return self.user_ids[name] # hack to support user-id in find() return None diff --git a/tests/test_redaction.py b/tests/test_redaction.py new file mode 100644 index 0000000..b6612c9 --- /dev/null +++ b/tests/test_redaction.py @@ -0,0 +1,48 @@ + +import unittest + +from redactor.redactor import Redactor, RedactedText + +#@unittest.skip("disabled due to model load times") +class RedactionTestCase(unittest.TestCase): + + def test_redacted_text(self): + redacted_text = "Alert: Chris [LastName1] and Elena [LastName2] detected outage near [Address1]. Phone [Phone1]." + expected_text = "Alert: Chris Garcia and Elena Lopez detected outage near 44 Beacon St, Boston, MA 02108. Phone 206-555-9988." + props = { + "[LastName1]": "Garcia", + "[LastName2]": "Lopez", + "[Address1]": "44 Beacon St, Boston, MA 02108", + "[Phone1]": "206-555-9988" + } + + redacted = RedactedText(redacted_text, props) + self.assertEqual(expected_text, redacted.unredact()) + + + def test_redactor(self): + text = "Alert: Chris Garcia and Elena Lopez detected outage near 3039 Beacon St, Boston, MA 02108. Phone 206-555-9988." + expected_props = { + "[LastName1]": "Garcia", + "[LastName2]": "Lopez", + "[Address1]": "3039 Beacon St", + "[City1]": "Boston", + "[State1]": "MA", + "[Zip1]": "02108", + "[Phone1]": "206-555-9988" + } + + redactor = Redactor() + redacted = redactor.redact_text(text) + + #print("Input:", text) + print("Redacted:", redacted) + print("Fields:", redacted.fields) + # print("Unredacted:", redacted.unredact()) + + for key, value in expected_props.items(): + self.assertTrue(key in redacted.fields, f"Missing expected key: {key}, not in redacted fields: {redacted.fields} ") + self.assertEqual(expected_props[key], redacted.fields[key]) + + unredacted = redacted.unredact() + self.assertEqual(unredacted, text) diff --git a/uv.lock b/uv.lock index 843fc89..be9190f 100644 --- a/uv.lock +++ b/uv.lock @@ -137,6 +137,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] +[[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, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + [[package]] name = "dateparser" version = "1.2.2" @@ -152,6 +161,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/87/22/f020c047ae1346613db9322638186468238bcfa8849b4668a22b97faad65/dateparser-1.2.2-py3-none-any.whl", hash = "sha256:5a5d7211a09013499867547023a2a0c91d5a27d15dd4dbcea676ea9fe66f2482", size = 315453, upload-time = "2025-06-26T09:29:21.412Z" }, ] +[[package]] +name = "filelock" +version = "3.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, +] + [[package]] name = "frozenlist" version = "1.7.0" @@ -195,6 +213,63 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" }, ] +[[package]] +name = "fsspec" +version = "2025.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/27/954057b0d1f53f086f681755207dda6de6c660ce133c829158e8e8fe7895/fsspec-2025.12.0.tar.gz", hash = "sha256:c505de011584597b1060ff778bb664c1bc022e87921b0e4f10cc9c44f9635973", size = 309748, upload-time = "2025-12-03T15:23:42.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/c7/b64cae5dba3a1b138d7123ec36bb5ccd39d39939f18454407e5468f4763f/fsspec-2025.12.0-py3-none-any.whl", hash = "sha256:8bf1fe301b7d8acfa6e8571e3b1c3d158f909666642431cc78a1b7b4dbc5ec5b", size = 201422, upload-time = "2025-12-03T15:23:41.434Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870, upload-time = "2025-10-24T19:04:11.422Z" }, + { url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584, upload-time = "2025-10-24T19:04:09.586Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004, upload-time = "2025-10-24T19:04:00.314Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636, upload-time = "2025-10-24T19:03:58.111Z" }, + { url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448, upload-time = "2025-10-24T19:04:20.951Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401, upload-time = "2025-10-24T19:04:22.549Z" }, + { url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866, upload-time = "2025-10-24T19:04:33.461Z" }, + { url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861, upload-time = "2025-10-24T19:04:19.01Z" }, + { url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699, upload-time = "2025-10-24T19:04:17.306Z" }, + { url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885, upload-time = "2025-10-24T19:04:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550, upload-time = "2025-10-24T19:04:05.55Z" }, + { url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010, upload-time = "2025-10-24T19:04:28.598Z" }, + { url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264, upload-time = "2025-10-24T19:04:30.397Z" }, + { url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071, upload-time = "2025-10-24T19:04:37.463Z" }, + { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, + { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, + { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, + { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, + { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "0.36.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358, upload-time = "2025-10-23T12:12:01.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094, upload-time = "2025-10-23T12:11:59.557Z" }, +] + [[package]] name = "humanize" version = "4.12.3" @@ -222,6 +297,114 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/8a/d1364c1c6d8f53ea390e8f1c6da220a4f9ee478ac8a473ae0669a2fb6f51/IMAPClient-3.0.1-py2.py3-none-any.whl", hash = "sha256:d77d77caa4123e0233b5cf2b9c54a078522e63270b88d3f48653a28637fd8828", size = 182490, upload-time = "2023-12-02T08:24:11.854Z" }, ] +[[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, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mlx" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mlx-metal", marker = "sys_platform == 'darwin'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/a5/e171b2caa69b346bc1abc1bfd0b139f631f68a0ff602862dd255e7dd95ec/mlx-0.30.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f46aaa6c562ba183e2a64a0e6ba15ed54f9027d9b7b1822e9eec7f59b13d610c", size = 554595, upload-time = "2025-11-20T16:45:13.919Z" }, + { url = "https://files.pythonhosted.org/packages/3e/f4/aeb8980bbef08fc031ab1a2d043a1d76e60d49bf46728bef66cf25b26dfa/mlx-0.30.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:5edee9f1452703c0804e067f212c61d488b3ad7419b9fcacf337ffd35842f576", size = 554593, upload-time = "2025-11-20T01:16:45.033Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b3/904235e11610c7cf2ba39eb39f32587d34048f7293d386f19d42d1e3dabc/mlx-0.30.0-cp313-cp313-macosx_26_0_arm64.whl", hash = "sha256:b8ae92b61353d756bbd0668b065a4ee5ee35867b03d63c0c61134d656ed236fe", size = 554330, upload-time = "2025-11-20T05:33:26.653Z" }, + { url = "https://files.pythonhosted.org/packages/43/25/6fa174632f5beb583eda80902af80dc39a63e5b4b6e66c7831301751d82e/mlx-0.30.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:384819dccfd551aa1444acacedb9ed2619724b0e67fc361ab80d73b8a8a7618f", size = 558834, upload-time = "2025-11-20T16:45:15.303Z" }, + { url = "https://files.pythonhosted.org/packages/b9/00/8c93f6ba5dc37a459b378bd22d2303824aa341595ed6c4958c6a48870677/mlx-0.30.0-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:8982a7d7fd078988c12c3fc53da1670aa910dd8a7df1fc0b9fee7303394ac8ff", size = 558833, upload-time = "2025-11-20T01:16:48.748Z" }, + { url = "https://files.pythonhosted.org/packages/58/65/0ecb3a858142d7b250b46d1e5c2bb4ebff9f57b735643626b7d4653b0f11/mlx-0.30.0-cp314-cp314-macosx_26_0_arm64.whl", hash = "sha256:392af721cb0bcf600162d7258923c15d39e197a64e23b6abd171e1aea79771c5", size = 558510, upload-time = "2025-11-20T05:33:28.286Z" }, +] + +[[package]] +name = "mlx-lm" +version = "0.28.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "mlx", marker = "sys_platform == 'darwin'" }, + { name = "numpy" }, + { name = "protobuf" }, + { name = "pyyaml" }, + { name = "sentencepiece" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/7f/94b3f7e00c4681a4fe2d47b519458245bd8a8f0506b1ce018d1850bbcf79/mlx_lm-0.28.4.tar.gz", hash = "sha256:3661d8ef5f0e2695d52993e0df1ed2c1f93ca1d094258146c18d9cec0c50514e", size = 232455, upload-time = "2025-12-03T22:39:59.122Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/35/5767098993834582e8a34a0317d666ed53498f58940fad8bffa0a4662eb1/mlx_lm-0.28.4-py3-none-any.whl", hash = "sha256:64ff7bff02e902f1df7daafdbf27ffc836b9a7c9b332b9fc3ea40c19e31147ca", size = 323307, upload-time = "2025-12-03T22:39:57.711Z" }, +] + +[[package]] +name = "mlx-metal" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/9f/47ebb6e9b2c33371c6ca3733e70324ed064f49e790ee4e194b713d6d7d84/mlx_metal-0.30.0-py3-none-macosx_14_0_arm64.whl", hash = "sha256:f48543b10d13bf0591b3f99c6eb585dd2c2e5db379edae5df0f19a728cb41742", size = 36819863, upload-time = "2025-11-20T06:42:23.927Z" }, + { url = "https://files.pythonhosted.org/packages/7f/91/c04e420326390c37d4c13f58960956d698cd34c7432ae3860bc5c6be71a0/mlx_metal-0.30.0-py3-none-macosx_15_0_arm64.whl", hash = "sha256:74bb5d10e0f24e21973d39430557bbd5d733c2a6599c3f1b87f9a0ff73fed2c8", size = 36817633, upload-time = "2025-11-20T01:16:27.77Z" }, + { url = "https://files.pythonhosted.org/packages/86/18/7af11eac0f488b68c436d249ffcf4f76003326e691994db3f720d21f21bb/mlx_metal-0.30.0-py3-none-macosx_26_0_arm64.whl", hash = "sha256:310fda8b2f7345865f3dd75a9b478e974f28d22e8ebf05f26af5adc0e8979cee", size = 44878409, upload-time = "2025-11-20T05:33:20.221Z" }, +] + [[package]] name = "multidict" version = "6.6.3" @@ -277,6 +460,7 @@ dependencies = [ { name = "dateparser" }, { name = "humanize" }, { name = "imapclient" }, + { name = "mlx-lm" }, { name = "py-cord" }, { name = "python-dotenv" }, { name = "requests" }, @@ -289,11 +473,73 @@ requires-dist = [ { name = "dateparser", specifier = ">=1.2.2" }, { name = "humanize", specifier = ">=4.12.0" }, { name = "imapclient", specifier = ">=3.0.1" }, + { name = "mlx-lm", specifier = ">=0.28.4" }, { name = "py-cord", specifier = ">=2.6.1" }, { name = "python-dotenv", specifier = ">=1.0.1" }, { name = "requests", specifier = ">=2.32.3" }, ] +[[package]] +name = "numpy" +version = "2.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" }, + { url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" }, + { url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" }, + { url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" }, + { url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" }, + { url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" }, + { url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" }, + { url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" }, + { url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" }, + { url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" }, + { url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" }, + { url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" }, + { url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" }, + { url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" }, + { url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" }, + { url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" }, + { url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" }, + { url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" }, + { url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" }, + { url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" }, + { url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" }, + { url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" }, + { url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" }, + { url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" }, + { url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" }, + { url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" }, + { url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" }, + { url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + [[package]] name = "propcache" version = "0.3.2" @@ -335,6 +581,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, ] +[[package]] +name = "protobuf" +version = "6.33.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296, upload-time = "2025-12-06T00:17:53.311Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603, upload-time = "2025-12-06T00:17:41.114Z" }, + { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930, upload-time = "2025-12-06T00:17:43.278Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621, upload-time = "2025-12-06T00:17:44.445Z" }, + { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460, upload-time = "2025-12-06T00:17:45.678Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168, upload-time = "2025-12-06T00:17:46.813Z" }, + { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270, upload-time = "2025-12-06T00:17:48.253Z" }, + { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, +] + [[package]] name = "py-cord" version = "2.6.1" @@ -377,6 +638,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, ] +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + [[package]] name = "regex" version = "2024.11.6" @@ -415,6 +712,68 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, ] +[[package]] +name = "safetensors" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, +] + +[[package]] +name = "sentencepiece" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/15/2e7a025fc62d764b151ae6d0f2a92f8081755ebe8d4a64099accc6f77ba6/sentencepiece-0.2.1.tar.gz", hash = "sha256:8138cec27c2f2282f4a34d9a016e3374cd40e5c6e9cb335063db66a0a3b71fad", size = 3228515, upload-time = "2025-08-12T07:00:51.718Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/4a/85fbe1706d4d04a7e826b53f327c4b80f849cf1c7b7c5e31a20a97d8f28b/sentencepiece-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dcd8161eee7b41aae57ded06272905dbd680a0a04b91edd0f64790c796b2f706", size = 1943150, upload-time = "2025-08-12T06:59:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/c2/83/4cfb393e287509fc2155480b9d184706ef8d9fa8cbf5505d02a5792bf220/sentencepiece-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c6c8f42949f419ff8c7e9960dbadcfbc982d7b5efc2f6748210d3dd53a7de062", size = 1325651, upload-time = "2025-08-12T06:59:55.073Z" }, + { url = "https://files.pythonhosted.org/packages/8d/de/5a007fb53b1ab0aafc69d11a5a3dd72a289d5a3e78dcf2c3a3d9b14ffe93/sentencepiece-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:097f3394e99456e9e4efba1737c3749d7e23563dd1588ce71a3d007f25475fff", size = 1253641, upload-time = "2025-08-12T06:59:56.562Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d2/f552be5928105588f4f4d66ee37dd4c61460d8097e62d0e2e0eec41bc61d/sentencepiece-0.2.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7b670879c370d350557edabadbad1f6561a9e6968126e6debca4029e5547820", size = 1316271, upload-time = "2025-08-12T06:59:58.109Z" }, + { url = "https://files.pythonhosted.org/packages/96/df/0cfe748ace5485be740fed9476dee7877f109da32ed0d280312c94ec259f/sentencepiece-0.2.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7f0fd2f2693309e6628aeeb2e2faf6edd221134dfccac3308ca0de01f8dab47", size = 1387882, upload-time = "2025-08-12T07:00:00.701Z" }, + { url = "https://files.pythonhosted.org/packages/ac/dd/f7774d42a881ced8e1739f393ab1e82ece39fc9abd4779e28050c2e975b5/sentencepiece-0.2.1-cp313-cp313-win32.whl", hash = "sha256:92b3816aa2339355fda2c8c4e021a5de92180b00aaccaf5e2808972e77a4b22f", size = 999541, upload-time = "2025-08-12T07:00:02.709Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e9/932b9eae6fd7019548321eee1ab8d5e3b3d1294df9d9a0c9ac517c7b636d/sentencepiece-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:10ed3dab2044c47f7a2e7b4969b0c430420cdd45735d78c8f853191fa0e3148b", size = 1054669, upload-time = "2025-08-12T07:00:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/c9/3a/76488a00ea7d6931689cda28726a1447d66bf1a4837943489314593d5596/sentencepiece-0.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac650534e2251083c5f75dde4ff28896ce7c8904133dc8fef42780f4d5588fcd", size = 1033922, upload-time = "2025-08-12T07:00:06.496Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b6/08fe2ce819e02ccb0296f4843e3f195764ce9829cbda61b7513f29b95718/sentencepiece-0.2.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:8dd4b477a7b069648d19363aad0cab9bad2f4e83b2d179be668efa672500dc94", size = 1946052, upload-time = "2025-08-12T07:00:08.136Z" }, + { url = "https://files.pythonhosted.org/packages/ab/d9/1ea0e740591ff4c6fc2b6eb1d7510d02f3fb885093f19b2f3abd1363b402/sentencepiece-0.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c0f672da370cc490e4c59d89e12289778310a0e71d176c541e4834759e1ae07", size = 1327408, upload-time = "2025-08-12T07:00:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/99/7e/1fb26e8a21613f6200e1ab88824d5d203714162cf2883248b517deb500b7/sentencepiece-0.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad8493bea8432dae8d6830365352350f3b4144415a1d09c4c8cb8d30cf3b6c3c", size = 1254857, upload-time = "2025-08-12T07:00:11.021Z" }, + { url = "https://files.pythonhosted.org/packages/bc/85/c72fd1f3c7a6010544d6ae07f8ddb38b5e2a7e33bd4318f87266c0bbafbf/sentencepiece-0.2.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b81a24733726e3678d2db63619acc5a8dccd074f7aa7a54ecd5ca33ca6d2d596", size = 1315722, upload-time = "2025-08-12T07:00:12.989Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e8/661e5bd82a8aa641fd6c1020bd0e890ef73230a2b7215ddf9c8cd8e941c2/sentencepiece-0.2.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0a81799d0a68d618e89063fb423c3001a034c893069135ffe51fee439ae474d6", size = 1387452, upload-time = "2025-08-12T07:00:15.088Z" }, + { url = "https://files.pythonhosted.org/packages/99/5e/ae66c361023a470afcbc1fbb8da722c72ea678a2fcd9a18f1a12598c7501/sentencepiece-0.2.1-cp313-cp313t-win32.whl", hash = "sha256:89a3ea015517c42c0341d0d962f3e6aaf2cf10d71b1932d475c44ba48d00aa2b", size = 1002501, upload-time = "2025-08-12T07:00:16.966Z" }, + { url = "https://files.pythonhosted.org/packages/c1/03/d332828c4ff764e16c1b56c2c8f9a33488bbe796b53fb6b9c4205ddbf167/sentencepiece-0.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:33f068c9382dc2e7c228eedfd8163b52baa86bb92f50d0488bf2b7da7032e484", size = 1057555, upload-time = "2025-08-12T07:00:18.573Z" }, + { url = "https://files.pythonhosted.org/packages/88/14/5aee0bf0864df9bd82bd59e7711362908e4935e3f9cdc1f57246b5d5c9b9/sentencepiece-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:b3616ad246f360e52c85781e47682d31abfb6554c779e42b65333d4b5f44ecc0", size = 1036042, upload-time = "2025-08-12T07:00:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/24/9c/89eb8b2052f720a612478baf11c8227dcf1dc28cd4ea4c0c19506b5af2a2/sentencepiece-0.2.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5d0350b686c320068702116276cfb26c066dc7e65cfef173980b11bb4d606719", size = 1943147, upload-time = "2025-08-12T07:00:21.809Z" }, + { url = "https://files.pythonhosted.org/packages/82/0b/a1432bc87f97c2ace36386ca23e8bd3b91fb40581b5e6148d24b24186419/sentencepiece-0.2.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c7f54a31cde6fa5cb030370566f68152a742f433f8d2be458463d06c208aef33", size = 1325624, upload-time = "2025-08-12T07:00:23.289Z" }, + { url = "https://files.pythonhosted.org/packages/ea/99/bbe054ebb5a5039457c590e0a4156ed073fb0fe9ce4f7523404dd5b37463/sentencepiece-0.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c83b85ab2d6576607f31df77ff86f28182be4a8de6d175d2c33ca609925f5da1", size = 1253670, upload-time = "2025-08-12T07:00:24.69Z" }, + { url = "https://files.pythonhosted.org/packages/19/ad/d5c7075f701bd97971d7c2ac2904f227566f51ef0838dfbdfdccb58cd212/sentencepiece-0.2.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1855f57db07b51fb51ed6c9c452f570624d2b169b36f0f79ef71a6e6c618cd8b", size = 1316247, upload-time = "2025-08-12T07:00:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/fb/03/35fbe5f3d9a7435eebd0b473e09584bd3cc354ce118b960445b060d33781/sentencepiece-0.2.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01e6912125cb45d3792f530a4d38f8e21bf884d6b4d4ade1b2de5cf7a8d2a52b", size = 1387894, upload-time = "2025-08-12T07:00:28.339Z" }, + { url = "https://files.pythonhosted.org/packages/dc/aa/956ef729aafb6c8f9c443104c9636489093bb5c61d6b90fc27aa1a865574/sentencepiece-0.2.1-cp314-cp314-win32.whl", hash = "sha256:c415c9de1447e0a74ae3fdb2e52f967cb544113a3a5ce3a194df185cbc1f962f", size = 1096698, upload-time = "2025-08-12T07:00:29.764Z" }, + { url = "https://files.pythonhosted.org/packages/b8/cb/fe400d8836952cc535c81a0ce47dc6875160e5fedb71d2d9ff0e9894c2a6/sentencepiece-0.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:881b2e44b14fc19feade3cbed314be37de639fc415375cefaa5bc81a4be137fd", size = 1155115, upload-time = "2025-08-12T07:00:32.865Z" }, + { url = "https://files.pythonhosted.org/packages/32/89/047921cf70f36c7b6b6390876b2399b3633ab73b8d0cb857e5a964238941/sentencepiece-0.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:2005242a16d2dc3ac5fe18aa7667549134d37854823df4c4db244752453b78a8", size = 1133890, upload-time = "2025-08-12T07:00:34.763Z" }, + { url = "https://files.pythonhosted.org/packages/a1/11/5b414b9fae6255b5fb1e22e2ed3dc3a72d3a694e5703910e640ac78346bb/sentencepiece-0.2.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a19adcec27c524cb7069a1c741060add95f942d1cbf7ad0d104dffa0a7d28a2b", size = 1946081, upload-time = "2025-08-12T07:00:36.97Z" }, + { url = "https://files.pythonhosted.org/packages/77/eb/7a5682bb25824db8545f8e5662e7f3e32d72a508fdce086029d89695106b/sentencepiece-0.2.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e37e4b4c4a11662b5db521def4e44d4d30ae69a1743241412a93ae40fdcab4bb", size = 1327406, upload-time = "2025-08-12T07:00:38.669Z" }, + { url = "https://files.pythonhosted.org/packages/03/b0/811dae8fb9f2784e138785d481469788f2e0d0c109c5737372454415f55f/sentencepiece-0.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:477c81505db072b3ab627e7eab972ea1025331bd3a92bacbf798df2b75ea86ec", size = 1254846, upload-time = "2025-08-12T07:00:40.611Z" }, + { url = "https://files.pythonhosted.org/packages/ef/23/195b2e7ec85ebb6a547969f60b723c7aca5a75800ece6cc3f41da872d14e/sentencepiece-0.2.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:010f025a544ef770bb395091d57cb94deb9652d8972e0d09f71d85d5a0816c8c", size = 1315721, upload-time = "2025-08-12T07:00:42.914Z" }, + { url = "https://files.pythonhosted.org/packages/7e/aa/553dbe4178b5f23eb28e59393dddd64186178b56b81d9b8d5c3ff1c28395/sentencepiece-0.2.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:733e59ff1794d26db706cd41fc2d7ca5f6c64a820709cb801dc0ea31780d64ab", size = 1387458, upload-time = "2025-08-12T07:00:44.56Z" }, + { url = "https://files.pythonhosted.org/packages/66/7c/08ff0012507297a4dd74a5420fdc0eb9e3e80f4e88cab1538d7f28db303d/sentencepiece-0.2.1-cp314-cp314t-win32.whl", hash = "sha256:d3233770f78e637dc8b1fda2cd7c3b99ec77e7505041934188a4e7fe751de3b0", size = 1099765, upload-time = "2025-08-12T07:00:46.058Z" }, + { url = "https://files.pythonhosted.org/packages/91/d5/2a69e1ce15881beb9ddfc7e3f998322f5cedcd5e4d244cb74dade9441663/sentencepiece-0.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e4366c97b68218fd30ea72d70c525e6e78a6c0a88650f57ac4c43c63b234a9d", size = 1157807, upload-time = "2025-08-12T07:00:47.673Z" }, + { url = "https://files.pythonhosted.org/packages/f3/16/54f611fcfc2d1c46cbe3ec4169780b2cfa7cf63708ef2b71611136db7513/sentencepiece-0.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:105e36e75cbac1292642045458e8da677b2342dcd33df503e640f0b457cb6751", size = 1136264, upload-time = "2025-08-12T07:00:49.485Z" }, +] + [[package]] name = "six" version = "1.17.0" @@ -424,6 +783,73 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "tokenizers" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9", size = 363123, upload-time = "2025-09-19T09:49:23.424Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73", size = 3069318, upload-time = "2025-09-19T09:49:11.848Z" }, + { url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc", size = 2926478, upload-time = "2025-09-19T09:49:09.759Z" }, + { url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a", size = 3256994, upload-time = "2025-09-19T09:48:56.701Z" }, + { url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7", size = 3153141, upload-time = "2025-09-19T09:48:59.749Z" }, + { url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21", size = 3508049, upload-time = "2025-09-19T09:49:05.868Z" }, + { url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214", size = 3710730, upload-time = "2025-09-19T09:49:01.832Z" }, + { url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f", size = 3412560, upload-time = "2025-09-19T09:49:03.867Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4", size = 3250221, upload-time = "2025-09-19T09:49:07.664Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879", size = 9345569, upload-time = "2025-09-19T09:49:14.214Z" }, + { url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446", size = 9271599, upload-time = "2025-09-19T09:49:16.639Z" }, + { url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a", size = 9533862, upload-time = "2025-09-19T09:49:19.146Z" }, + { url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390", size = 9681250, upload-time = "2025-09-19T09:49:21.501Z" }, + { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003, upload-time = "2025-09-19T09:49:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684, upload-time = "2025-09-19T09:49:24.953Z" }, +] + +[[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, upload-time = "2024-11-24T20:12:22.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, +] + +[[package]] +name = "transformers" +version = "4.57.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/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680, upload-time = "2025-11-25T15:51:30.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463, upload-time = "2025-11-25T15:51:26.493Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + [[package]] name = "tzdata" version = "2025.2"