From 6c1a69af0d3a89baa7f47ed53a43fc1b12dbbb0a Mon Sep 17 00:00:00 2001 From: xiaopeng <1509442308@qq.com> Date: Wed, 17 Jun 2026 10:13:39 +0800 Subject: [PATCH] Initial: integrated 2025 LawGraph (graphrag_pipeline) + 2026 kg_project Co-Authored-By: Claude Opus 4.7 --- .claude/settings.local.json | 12 + .gitignore | 50 + dofile/graphrag_pipeline/.gitignore | 62 + dofile/graphrag_pipeline/CHANGELOG.md | 38 + .../CONFIGURATION_COMPLETE.md | 83 + dofile/graphrag_pipeline/ENV_SETUP.md | 73 + dofile/graphrag_pipeline/MODEL_CONFIG.md | 43 + dofile/graphrag_pipeline/PROJECT_STATUS.md | 147 + dofile/graphrag_pipeline/PROJECT_SUMMARY.md | 161 ++ dofile/graphrag_pipeline/README.md | 220 ++ dofile/graphrag_pipeline/TEST_RESULTS.md | 111 + dofile/graphrag_pipeline/USAGE_EXAMPLES.md | 114 + dofile/graphrag_pipeline/config/ontology.yaml | 108 + dofile/graphrag_pipeline/docs/ARCHITECTURE.md | 199 ++ .../examples/use_siliconflow.py | 109 + dofile/graphrag_pipeline/pyproject.toml | 63 + dofile/graphrag_pipeline/scripts/build_kg.py | 126 + .../scripts/complete_pipeline.py | 137 + dofile/graphrag_pipeline/scripts/extract.py | 96 + dofile/graphrag_pipeline/scripts/query_kg.py | 100 + dofile/graphrag_pipeline/src/__init__.py | 10 + .../src/analysis/__init__.py | 12 + .../src/analysis/evolution.py | 54 + .../src/analysis/legal_structure.py | 98 + .../src/analysis/reasoning.py | 59 + .../src/extraction/__init__.py | 18 + .../src/extraction/classifier.py | 325 +++ .../src/extraction/evaluator.py | 322 +++ .../graphrag_pipeline/src/extraction/ner.py | 178 ++ dofile/graphrag_pipeline/src/extraction/re.py | 236 ++ .../src/kg_builder/__init__.py | 11 + .../graphrag_pipeline/src/kg_builder/graph.py | 129 + .../src/kg_builder/indexer.py | 361 +++ .../src/ontology/__init__.py | 18 + .../src/ontology/entities.py | 82 + .../src/ontology/relations.py | 73 + .../graphrag_pipeline/src/ontology/schema.py | 126 + .../src/preprocessing/__init__.py | 11 + .../src/preprocessing/document_parser.py | 242 ++ .../src/preprocessing/text_processor.py | 271 ++ .../graphrag_pipeline/src/prompts/__init__.py | 17 + .../src/prompts/ner_prompts.py | 95 + .../src/prompts/re_prompts.py | 129 + .../graphrag_pipeline/src/prompts/template.py | 227 ++ .../graphrag_pipeline/src/query/__init__.py | 12 + .../src/query/drift_search.py | 187 ++ .../src/query/global_search.py | 194 ++ .../src/query/local_search.py | 162 ++ .../graphrag_pipeline/src/utils/__init__.py | 17 + dofile/graphrag_pipeline/src/utils/config.py | 52 + .../graphrag_pipeline/src/utils/file_utils.py | 58 + .../graphrag_pipeline/src/utils/llm_client.py | 219 ++ dofile/graphrag_pipeline/tests/README.md | 75 + dofile/graphrag_pipeline/tests/test_basic.py | 72 + .../graphrag_pipeline/tests/test_documents.py | 100 + dofile/graphrag_pipeline/tests/test_model.py | 73 + .../graphrag_pipeline/tests/test_my_models.py | 61 + dofile/graphrag_pipeline/tests/test_quick.py | 57 + .../tests/test_siliconflow.py | 81 + dofile/graphrag_pipeline/uv.lock | 2379 +++++++++++++++++ dofile/kg_project/.gitignore | 10 + .../config/deep_extraction_config.yaml | 122 + dofile/kg_project/config/legal_config.yaml | 86 + dofile/kg_project/config/legal_ontology.yaml | 246 ++ dofile/kg_project/neo4j/import_data.py | 139 + dofile/kg_project/neo4j/schema.cypher | 62 + .../kg_project/ontology/legal_ontology.json | 213 ++ ...wal_ontology_21_entities_32_relations.json | 1973 ++++++++++++++ dofile/kg_project/requirements.txt | 29 + dofile/kg_project/scripts/docx_reader.py | 221 ++ .../kg_project/scripts/extract_legal_csv.py | 314 +++ .../scripts/legal_metadata_extractor.py | 354 +++ .../kg_project/scripts/visualize_legal_kg.py | 239 ++ dofile/kg_project/src/__init__.py | 0 .../src/data_processing/__init__.py | 0 .../src/data_processing/entity_normalizer.py | 176 ++ .../data_processing/relationship_builder.py | 189 ++ .../src/deep_extraction_pipeline.py | 335 +++ .../src/knowledge_extraction/__init__.py | 0 .../knowledge_extraction/citation_resolver.py | 134 + .../llm_legal_extractor.py | 303 +++ dofile/kg_project/src/main.py | 166 ++ dofile/kg_project/start.bat | 29 + 83 files changed, 14295 insertions(+) create mode 100644 .claude/settings.local.json create mode 100644 .gitignore create mode 100644 dofile/graphrag_pipeline/.gitignore create mode 100644 dofile/graphrag_pipeline/CHANGELOG.md create mode 100644 dofile/graphrag_pipeline/CONFIGURATION_COMPLETE.md create mode 100644 dofile/graphrag_pipeline/ENV_SETUP.md create mode 100644 dofile/graphrag_pipeline/MODEL_CONFIG.md create mode 100644 dofile/graphrag_pipeline/PROJECT_STATUS.md create mode 100644 dofile/graphrag_pipeline/PROJECT_SUMMARY.md create mode 100644 dofile/graphrag_pipeline/README.md create mode 100644 dofile/graphrag_pipeline/TEST_RESULTS.md create mode 100644 dofile/graphrag_pipeline/USAGE_EXAMPLES.md create mode 100644 dofile/graphrag_pipeline/config/ontology.yaml create mode 100644 dofile/graphrag_pipeline/docs/ARCHITECTURE.md create mode 100644 dofile/graphrag_pipeline/examples/use_siliconflow.py create mode 100644 dofile/graphrag_pipeline/pyproject.toml create mode 100644 dofile/graphrag_pipeline/scripts/build_kg.py create mode 100644 dofile/graphrag_pipeline/scripts/complete_pipeline.py create mode 100644 dofile/graphrag_pipeline/scripts/extract.py create mode 100644 dofile/graphrag_pipeline/scripts/query_kg.py create mode 100644 dofile/graphrag_pipeline/src/__init__.py create mode 100644 dofile/graphrag_pipeline/src/analysis/__init__.py create mode 100644 dofile/graphrag_pipeline/src/analysis/evolution.py create mode 100644 dofile/graphrag_pipeline/src/analysis/legal_structure.py create mode 100644 dofile/graphrag_pipeline/src/analysis/reasoning.py create mode 100644 dofile/graphrag_pipeline/src/extraction/__init__.py create mode 100644 dofile/graphrag_pipeline/src/extraction/classifier.py create mode 100644 dofile/graphrag_pipeline/src/extraction/evaluator.py create mode 100644 dofile/graphrag_pipeline/src/extraction/ner.py create mode 100644 dofile/graphrag_pipeline/src/extraction/re.py create mode 100644 dofile/graphrag_pipeline/src/kg_builder/__init__.py create mode 100644 dofile/graphrag_pipeline/src/kg_builder/graph.py create mode 100644 dofile/graphrag_pipeline/src/kg_builder/indexer.py create mode 100644 dofile/graphrag_pipeline/src/ontology/__init__.py create mode 100644 dofile/graphrag_pipeline/src/ontology/entities.py create mode 100644 dofile/graphrag_pipeline/src/ontology/relations.py create mode 100644 dofile/graphrag_pipeline/src/ontology/schema.py create mode 100644 dofile/graphrag_pipeline/src/preprocessing/__init__.py create mode 100644 dofile/graphrag_pipeline/src/preprocessing/document_parser.py create mode 100644 dofile/graphrag_pipeline/src/preprocessing/text_processor.py create mode 100644 dofile/graphrag_pipeline/src/prompts/__init__.py create mode 100644 dofile/graphrag_pipeline/src/prompts/ner_prompts.py create mode 100644 dofile/graphrag_pipeline/src/prompts/re_prompts.py create mode 100644 dofile/graphrag_pipeline/src/prompts/template.py create mode 100644 dofile/graphrag_pipeline/src/query/__init__.py create mode 100644 dofile/graphrag_pipeline/src/query/drift_search.py create mode 100644 dofile/graphrag_pipeline/src/query/global_search.py create mode 100644 dofile/graphrag_pipeline/src/query/local_search.py create mode 100644 dofile/graphrag_pipeline/src/utils/__init__.py create mode 100644 dofile/graphrag_pipeline/src/utils/config.py create mode 100644 dofile/graphrag_pipeline/src/utils/file_utils.py create mode 100644 dofile/graphrag_pipeline/src/utils/llm_client.py create mode 100644 dofile/graphrag_pipeline/tests/README.md create mode 100644 dofile/graphrag_pipeline/tests/test_basic.py create mode 100644 dofile/graphrag_pipeline/tests/test_documents.py create mode 100644 dofile/graphrag_pipeline/tests/test_model.py create mode 100644 dofile/graphrag_pipeline/tests/test_my_models.py create mode 100644 dofile/graphrag_pipeline/tests/test_quick.py create mode 100644 dofile/graphrag_pipeline/tests/test_siliconflow.py create mode 100644 dofile/graphrag_pipeline/uv.lock create mode 100644 dofile/kg_project/.gitignore create mode 100644 dofile/kg_project/config/deep_extraction_config.yaml create mode 100644 dofile/kg_project/config/legal_config.yaml create mode 100644 dofile/kg_project/config/legal_ontology.yaml create mode 100644 dofile/kg_project/neo4j/import_data.py create mode 100644 dofile/kg_project/neo4j/schema.cypher create mode 100644 dofile/kg_project/ontology/legal_ontology.json create mode 100644 dofile/kg_project/ontology/urban_renewal_ontology_21_entities_32_relations.json create mode 100644 dofile/kg_project/requirements.txt create mode 100644 dofile/kg_project/scripts/docx_reader.py create mode 100644 dofile/kg_project/scripts/extract_legal_csv.py create mode 100644 dofile/kg_project/scripts/legal_metadata_extractor.py create mode 100644 dofile/kg_project/scripts/visualize_legal_kg.py create mode 100644 dofile/kg_project/src/__init__.py create mode 100644 dofile/kg_project/src/data_processing/__init__.py create mode 100644 dofile/kg_project/src/data_processing/entity_normalizer.py create mode 100644 dofile/kg_project/src/data_processing/relationship_builder.py create mode 100644 dofile/kg_project/src/deep_extraction_pipeline.py create mode 100644 dofile/kg_project/src/knowledge_extraction/__init__.py create mode 100644 dofile/kg_project/src/knowledge_extraction/citation_resolver.py create mode 100644 dofile/kg_project/src/knowledge_extraction/llm_legal_extractor.py create mode 100644 dofile/kg_project/src/main.py create mode 100644 dofile/kg_project/start.bat diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..542c1d3 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,12 @@ +{ + "permissions": { + "allow": [ + "Bash(where WinRAR.exe)", + "Read(//c/Program Files/WinRAR/**)", + "Bash(pip install *)", + "Bash(pip show *)", + "Bash(where winget *)", + "Bash(python -c \"import rarfile; print\\('rarfile OK'\\)\")" + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b886b90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Python +__pycache__/ +*.pyc +*.pyo +.venv/ +venv/ +.python-version + +# Secrets +.env +dofile/kg_project/config/api_keys.yaml + +# LaTeX build artifacts +officefile/latex/*.aux +officefile/latex/*.log +officefile/latex/*.out +officefile/latex/*.synctex.gz +officefile/latex/*.fls +officefile/latex/*.fdb_latexmk +officefile/latex/*.toc +officefile/latex/*.lof +officefile/latex/*.lot +officefile/latex/*.bbl +officefile/latex/*.blg + +# LaTeX paper (independent Overleaf git repo — do NOT track in root) +officefile/latex/ + +# Data files (large, tracked separately via archive/) +data/ + +# Archive (frozen snapshots, not version-controlled) +archive/ + +# Large archives +*.rar +*.zip +dofile/**/output-*.zip + +# Logs and runtime outputs +dofile/*/logs/ +output/ + +# OS +.DS_Store +Thumbs.db + +# IDE +.vscode/ +.idea/ diff --git a/dofile/graphrag_pipeline/.gitignore b/dofile/graphrag_pipeline/.gitignore new file mode 100644 index 0000000..285d2dd --- /dev/null +++ b/dofile/graphrag_pipeline/.gitignore @@ -0,0 +1,62 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual Environment +.venv/ +venv/ +ENV/ +env/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Environment variables +.env + +# Output files +output/ +*.json +*.csv +*.xlsx + +# Logs +*.log +logs/ + +# OS +.DS_Store +Thumbs.db + +# Project specific +data/ +models/ +*.db +*.sqlite + + + + + diff --git a/dofile/graphrag_pipeline/CHANGELOG.md b/dofile/graphrag_pipeline/CHANGELOG.md new file mode 100644 index 0000000..d1f4e22 --- /dev/null +++ b/dofile/graphrag_pipeline/CHANGELOG.md @@ -0,0 +1,38 @@ +# 更新日志 + +## v0.1.0 (2025-01-XX) + +### 新增功能 + +- ✅ 项目基础架构(uv项目配置、目录结构) +- ✅ 文本预处理模块(HanLP集成、文档解析) +- ✅ 本体模型定义(9种实体类型、8种关系类型) +- ✅ Prompt模板系统(五个模块、NER/RE构建器) +- ✅ LLM客户端封装(多提供商支持) +- ✅ 知识抽取模块: + - NER提取器(两阶段对话) + - RE提取器(多模型并行、二次对话验证) + - 三元组评估器(成对排序、多维度评分) + - 实体关系分类器(RAG-based + Prompt-based) +- ✅ 知识图谱构建(索引器、图谱管理、社区检测、摘要生成) +- ✅ 分析和推理模块(法理结构提取、逻辑推理、演化分析框架) +- ✅ 查询系统(全局搜索、局部搜索、DRIFT搜索) +- ✅ 执行脚本(extract.py, build_kg.py, query_kg.py, complete_pipeline.py) + +### 技术特性 + +- 支持多个LLM提供商(OpenAI, Anthropic, Qwen, Doubao, GLM) +- 参考GraphRAG实现社区检测和摘要生成 +- 实现论文中描述的五个模块Prompt模板 +- 支持两阶段对话验证机制 +- LLM-as-a-Judge评估方法 + +### 已知限制 + +- 大规模数据处理需要进一步优化 +- 向量数据库使用简化实现,生产环境建议使用专业向量数据库 +- 社区检测在igraph未安装时使用替代方法 + + + + diff --git a/dofile/graphrag_pipeline/CONFIGURATION_COMPLETE.md b/dofile/graphrag_pipeline/CONFIGURATION_COMPLETE.md new file mode 100644 index 0000000..a9dccaa --- /dev/null +++ b/dofile/graphrag_pipeline/CONFIGURATION_COMPLETE.md @@ -0,0 +1,83 @@ +# 配置完成总结 + +## ✅ 已完成的配置 + +### 1. 硅基流动API配置 +- ✅ API密钥已配置:`sk-pvvtosiglncktlucwarxilvsypqcttqizgpcfdvodgcuaezn` +- ✅ API端点:`https://api.siliconflow.cn/v1` +- ✅ 环境变量已设置(`.env`文件) + +### 2. 模型配置 +项目已配置为使用以下两个硅基流动模型: + +#### NER(命名实体识别) +- **模型**: `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` +- **配置文件**: `src/extraction/ner.py` +- **状态**: ✅ 已测试,可用 + +#### RE(关系抽取)- 多模型并行 +- **模型1**: `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` +- **模型2**: `Qwen/Qwen2.5-7B-Instruct` +- **配置文件**: `src/extraction/re.py` +- **状态**: ✅ 已测试,两个模型都可用 + +#### 社区摘要生成 +- **模型**: `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` +- **配置文件**: `src/kg_builder/indexer.py` +- **状态**: ✅ 已配置 + +### 3. 环境修复 +- ✅ 虚拟环境已重新创建 +- ✅ 所有依赖包已安装(92个包) +- ✅ 修复了conda环境路径干扰问题 + +## 📊 快速测试结果 + +测试了2个TextUnit的完整流程: + +### 成功指标 +- ✅ 文档解析:成功 +- ✅ 实体识别(NER):成功(使用deepseek-ai/DeepSeek-R1-Distill-Qwen-7B) +- ✅ 关系抽取(RE):成功(两个模型并行运行) +- ✅ 知识图谱构建:成功 + - 节点数:67个 + - 边数:63条 + - 三元组数:63个 +- ✅ 社区检测:成功(11个社区) +- ⚠️ 社区摘要生成:部分成功(需要更长时间,但已修复配置) + +## 🚀 运行完整流程 + +完整流程正在后台运行,处理3个文档: +```bash +cd E:\Project\2025_LawGraph\dofile +.venv\Scripts\python.exe scripts\complete_pipeline.py --data-dir ../data/1法律 --output-dir ./output --max-docs 3 +``` + +预计运行时间:由于需要处理62个TextUnit,每个都需要调用LLM API,可能需要10-30分钟。 + +## 📁 输出文件 + +流程完成后,将生成: +- `output/knowledge_graph.json`: 知识图谱文件 +- `output/knowledge_graph_stats.json`: 统计信息 + +## 🔍 验证模型 + +运行以下命令验证模型配置: +```bash +.venv\Scripts\python.exe tests\test_my_models.py +``` + +## 📝 下一步 + +1. 等待完整流程完成 +2. 查看生成的知识图谱文件 +3. 可以尝试查询功能: + ```bash + .venv\Scripts\python.exe scripts/query_kg.py --kg output/knowledge_graph.json --query "乡村振兴" --mode global + ``` + +配置已完成,项目已就绪!🎉 + + diff --git a/dofile/graphrag_pipeline/ENV_SETUP.md b/dofile/graphrag_pipeline/ENV_SETUP.md new file mode 100644 index 0000000..18be248 --- /dev/null +++ b/dofile/graphrag_pipeline/ENV_SETUP.md @@ -0,0 +1,73 @@ +# 环境变量配置说明 + +## 创建 .env 文件 + +在 `dofile` 目录下创建 `.env` 文件,添加以下配置: + +```bash +# LLM API Keys +OPENAI_API_KEY=your_openai_api_key_here +ANTHROPIC_API_KEY=your_anthropic_api_key_here +DASHSCOPE_API_KEY=your_dashscope_api_key_here +VOLCENGINE_ACCESS_KEY=your_volcengine_access_key_here +VOLCENGINE_SECRET_KEY=your_volcengine_secret_key_here +ZHIPUAI_API_KEY=your_zhipuai_api_key_here + +# SiliconFlow API配置(硅基流动) +SILICONFLOW_API_KEY=sk-pvvtosiglncktlucwarxilvsypqcttqizgpcfdvodgcuaezn +SILICONFLOW_API_BASE=https://api.siliconflow.cn/v1 + +# 路径配置 +DATA_DIR=../data +OUTPUT_DIR=./output + +# 日志配置 +LOG_LEVEL=INFO + +# HanLP配置(可选) +HANLP_MODEL_PATH= + +# LLM配置 +TEMPERATURE=0.4 +FREQUENCY_PENALTY=0.6 +PRESENCE_PENALTY=0.6 + +# 文本处理配置 +MAX_TEXTUNIT_LENGTH=500 +``` + +## 硅基流动配置 + +硅基流动的API已经配置完成,您可以使用以下方式调用: + +```python +from src.utils.llm_client import LLMClient, LLMProvider +from src.utils.config import load_config + +config = load_config() +client = LLMClient( + provider=LLMProvider.SILICONFLOW, + model="你的模型名称", # 例如:Qwen/Qwen2.5-72B-Instruct + config=config +) + +messages = [ + {"role": "user", "content": "你好"} +] +response = client.chat(messages) +print(response) +``` + +## 支持的模型 + +硅基流动支持多种模型,常用的包括: +- `Qwen/Qwen2.5-72B-Instruct` +- `meta-llama/Llama-3.1-70B-Instruct` +- `01-ai/Yi-1.5-34B-Chat` +- 等等... + +具体可用模型请查看硅基流动官网:https://siliconflow.cn/ + + + + diff --git a/dofile/graphrag_pipeline/MODEL_CONFIG.md b/dofile/graphrag_pipeline/MODEL_CONFIG.md new file mode 100644 index 0000000..163be3e --- /dev/null +++ b/dofile/graphrag_pipeline/MODEL_CONFIG.md @@ -0,0 +1,43 @@ +# 模型配置说明 + +## 当前使用的模型 + +项目已配置为使用以下两个硅基流动模型: + +### 1. NER(命名实体识别) +- **模型**: `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` +- **用途**: 从法规文本中提取实体(目标要素、原则要素、空间要素等) +- **特点**: DeepSeek R1系列,具有推理能力 + +### 2. RE(关系抽取) +- **模型1**: `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` +- **模型2**: `Qwen/Qwen2.5-7B-Instruct` +- **用途**: 从文本中提取实体之间的关系 +- **特点**: 多模型并行,提高准确性和覆盖率 + +### 3. 社区摘要生成 +- **模型**: `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` +- **用途**: 为知识图谱的社区生成摘要 + +## 配置位置 + +模型配置在以下文件中: +- `src/extraction/ner.py`: NER模型配置 +- `src/extraction/re.py`: RE模型配置(多模型) +- `src/kg_builder/indexer.py`: 社区摘要生成模型配置 + +## 如何修改模型 + +如果要修改使用的模型,请编辑上述文件中的模型名称字符串。 + +## 测试模型可用性 + +运行以下命令测试模型: +```bash +uv run python tests/test_model.py + +# 测试指定的两个模型 +uv run python tests/test_my_models.py +``` + + diff --git a/dofile/graphrag_pipeline/PROJECT_STATUS.md b/dofile/graphrag_pipeline/PROJECT_STATUS.md new file mode 100644 index 0000000..71866d1 --- /dev/null +++ b/dofile/graphrag_pipeline/PROJECT_STATUS.md @@ -0,0 +1,147 @@ +# 项目实现状态 + +## 已完成工作 ✅ + +### 1. 项目基础架构 +- ✅ 使用uv进行项目管理和依赖管理 +- ✅ 完整的项目目录结构 +- ✅ 配置文件(pyproject.toml, .env.example) +- ✅ README和项目文档 + +### 2. 文本预处理模块 (`src/preprocessing/`) +- ✅ **TextProcessor**: HanLP集成,支持分词、依存句法、句法成分分析 +- ✅ **DocumentParser**: Word文档解析(.docx),支持章节结构识别 +- ✅ TextUnit切分功能(参考GraphRAG) + +### 3. 本体模型定义 (`src/ontology/`) +- ✅ **实体类型定义**: 9种实体类型(对应论文Table 1) +- ✅ **关系类型定义**: 8种关系类型(对应论文Table 2) +- ✅ **三维本体结构**: 法规层级—要素类型—关联关系 +- ✅ **四要素架构**: 目标—原则—要素—管控 +- ✅ **OntologySchema**: 本体模式管理和验证 + +### 4. Prompt模板系统 (`src/prompts/`) +- ✅ **PromptTemplate**: 统一模板框架,实现五个模块: + - 任务描述(系统消息+用户消息) + - 候选目标 + - 任务示例(少样本学习) + - 任务强调 + - 二次对话 +- ✅ **NERPromptBuilder**: NER任务Prompt构建 +- ✅ **REPromptBuilder**: RE任务Prompt构建 + +### 5. LLM客户端封装 (`src/utils/llm_client.py`) +- ✅ 支持多个LLM提供商: + - OpenAI (GPT-4o) + - Anthropic (Claude) + - 阿里云Qwen + - 字节跳动Doubao + - 智谱GLM +- ✅ 统一API接口 +- ✅ 异步/同步支持 +- ✅ 重试机制(tenacity) + +### 6. 知识抽取模块 (`src/extraction/`) +- ✅ **NERExtractor**: 使用Qwen-Max进行实体识别,支持两阶段对话验证 +- ✅ **REExtractor**: 多模型并行关系抽取框架 +- ✅ **TripletEvaluator**: LLM-as-a-Judge评估框架 +- ✅ **EntityRelationClassifier**: RAG-based分类框架 + +### 7. 知识图谱构建 (`src/kg_builder/`) +- ✅ **GraphIndexer**: 索引器框架(参考GraphRAG) + - 文本切分 + - 实体提取 + - 图构建 + - 社区检测(待完善) + - 摘要生成(待完善) +- ✅ **KnowledgeGraph**: 图谱结构管理(NetworkX) + +### 8. 分析和推理模块 (`src/analysis/`) +- ✅ **LegalStructureExtractor**: 法理结构提取(四要素识别、跨法规引用) +- ✅ **LegalReasoner**: 逻辑推理(路径查询、中心性分析) +- ✅ **EvolutionAnalyzer**: 演化规律分析框架 + +### 9. 查询系统 (`src/query/`) +- ✅ **GlobalSearcher**: 全局搜索框架(参考GraphRAG) +- ✅ **LocalSearcher**: 局部搜索框架 +- ✅ **DriftSearcher**: DRIFT搜索框架 + +### 10. 工具脚本 +- ✅ `scripts/extract.py`: 知识抽取示例脚本 + +## 待完善功能 ⚠️ + +### 高优先级 +1. **三元组评估算法实现** (`src/extraction/evaluator.py`) + - 成对排序逻辑 + - 五维度评分(语义准确性、一致性、事实性、准确性、可理解性) + - 评分聚合和筛选 + +2. **RAG-based分类实现** (`src/extraction/classifier.py`) + - 向量嵌入(使用Text-embedding-3-large) + - 向量数据库(相似度检索) + - 分类逻辑完善 + +3. **知识图谱构建完整实现** (`src/kg_builder/indexer.py`) + - 完整的三元组提取流程 + - 社区检测(Leiden算法,需要igraph) + - 社区摘要生成(使用LLM) + +4. **RE提取器完善** (`src/extraction/re.py`) + - 句法信息集成 + - 上下文信息利用 + - 省略成分补全逻辑 + +### 中优先级 +5. **查询系统实现** (`src/query/`) + - 全局搜索:利用社区摘要 + - 局部搜索:邻居扩展算法 + - DRIFT搜索:社区上下文整合 + +6. **演化规律分析** (`src/analysis/evolution.py`) + - 时间序列分析 + - 关系演化追踪 + - 网络演化分析 + +### 低优先级 +7. **共指消解完善** (`src/preprocessing/text_processor.py`) +8. **性能优化和缓存机制** +9. **可视化工具** +10. **单元测试和集成测试** + +## 使用说明 + +### 环境设置 +```bash +cd dofile +uv sync +source .venv/bin/activate # 或 .venv\Scripts\activate (Windows) +``` + +### 配置API密钥 +复制`.env.example`到`.env`,填入相应的API密钥。 + +### 运行示例 +```bash +# 实体识别 +uv run python scripts/extract.py --input ../data/1法律/3-中华人民共和国城乡规划法.docx --output ./output/entities.json +``` + +## 参考资源 + +- GraphRAG文档: https://msdocs.cn/graphrag/ +- 论文: `officefile/paper.tex` +- 数据目录: `../data/` + +## 技术债务 + +1. 错误处理和日志记录需要更完善 +2. 需要添加配置文件验证 +3. 异步处理可以进一步优化 +4. 需要添加单元测试 +5. 文档需要补充更多示例 + + + + + diff --git a/dofile/graphrag_pipeline/PROJECT_SUMMARY.md b/dofile/graphrag_pipeline/PROJECT_SUMMARY.md new file mode 100644 index 0000000..1719756 --- /dev/null +++ b/dofile/graphrag_pipeline/PROJECT_SUMMARY.md @@ -0,0 +1,161 @@ +# 项目完善总结 + +## 已完成的核心功能 + +### ✅ 1. 三元组评估器完整实现 +- **成对排序**: 比较不同模型生成的三元组组,确定最佳结果 +- **多维度评分**: 五维度评估(语义准确性、一致性、事实性、准确性、可理解性) +- **Peer Examination**: 避免自我增强偏差,支持多评估者 +- **低质量筛选**: 基于评分阈值筛选三元组 + +### ✅ 2. RE提取器完善 +- **二次对话验证**: 完整实现两阶段对话优化 +- **实体格式化**: 改进实体列表的格式化处理 +- **句法信息集成**: 更好地利用依存句法和句法成分信息 +- **错误处理**: 完善的异常处理和结果解析 + +### ✅ 3. 知识图谱索引器完整实现 +- **完整索引流程**: 从TextUnit到知识图谱的完整流程 +- **图构建**: 实现三元组到图结构的转换 +- **社区检测**: Leiden算法实现(igraph)或连通分量替代 +- **社区摘要生成**: 使用LLM生成社区摘要 +- **元数据管理**: 存储图谱构建的元信息 + +### ✅ 4. RAG-based分类器实现 +- **向量嵌入**: 使用OpenAI text-embedding-3-large +- **相似度计算**: 余弦相似度计算 +- **知识库构建**: 支持从已标注样本构建知识库 +- **Prompt-based分类**: 备选分类方法 + +### ✅ 5. 查询系统完整实现 +- **全局搜索**: + - 社区相关性评估 + - LLM生成综合答案 + - 支持关键词匹配备选 +- **局部搜索**: + - 实体邻居扩展 + - 深度控制 + - 中心性分析 + - 模糊匹配 +- **DRIFT搜索**: + - 结合局部和全局信息 + - 社区上下文整合 + - 综合答案生成 + +### ✅ 6. 图谱管理功能 +- **保存/加载**: JSON和GraphML格式支持 +- **元数据保存**: 保存社区信息和统计信息 + +### ✅ 7. 执行脚本完善 +- `extract.py`: 实体识别脚本 +- `build_kg.py`: 知识图谱构建脚本 +- `query_kg.py`: 图谱查询脚本(支持三种模式) +- `complete_pipeline.py`: 完整流程脚本 + +### ✅ 8. 文档和测试 +- 完整的README +- 使用示例文档 +- 架构文档 +- 基础测试 +- 项目状态文档 + +## 项目文件结构 + +``` +dofile/ +├── pyproject.toml # uv项目配置 ✅ +├── README.md # 项目文档 ✅ +├── PROJECT_STATUS.md # 项目状态 ✅ +├── CHANGELOG.md # 更新日志 ✅ +├── USAGE_EXAMPLES.md # 使用示例 ✅ +├── .env.example # 环境变量模板 ✅ +├── .gitignore # Git忽略文件 ✅ +├── src/ # 源代码 ✅ +│ ├── preprocessing/ # 文本预处理 ✅ +│ ├── ontology/ # 本体模型 ✅ +│ ├── prompts/ # Prompt模板 ✅ +│ ├── extraction/ # 知识抽取 ✅ +│ ├── kg_builder/ # 图谱构建 ✅ +│ ├── analysis/ # 分析推理 ✅ +│ ├── query/ # 查询系统 ✅ +│ └── utils/ # 工具函数 ✅ +├── scripts/ # 执行脚本 ✅ +│ ├── extract.py +│ ├── build_kg.py +│ ├── query_kg.py +│ └── complete_pipeline.py +├── config/ # 配置文件 ✅ +│ ├── ontology.yaml +│ └── prompts/ +├── docs/ # 文档 ✅ +│ └── ARCHITECTURE.md +└── tests/ # 测试 ✅ + └── test_basic.py +``` + +## 核心特性 + +1. **模块化设计**: 清晰的模块划分,易于扩展和维护 +2. **多LLM支持**: 支持5个主流LLM提供商 +3. **GraphRAG参考**: 参考GraphRAG实现社区检测和查询 +4. **论文方法实现**: 完整实现论文中描述的方法论 +5. **错误处理**: 完善的异常处理和日志记录 +6. **可配置性**: 通过配置文件和环境变量灵活配置 + +## 使用方法 + +### 快速开始 +```bash +cd dofile +uv sync +cp .env.example .env +# 编辑.env填入API密钥 + +# 完整流程 +uv run python scripts/complete_pipeline.py --data-dir ../data/1法律 --output-dir ./output --max-docs 5 +``` + +### 分步执行 +```bash +# 1. 实体识别 +uv run python scripts/extract.py --input ../data/1法律/3-中华人民共和国城乡规划法.docx + +# 2. 构建图谱 +uv run python scripts/build_kg.py --data-dir ../data/1法律 --output ./output/kg.json + +# 3. 查询图谱 +uv run python scripts/query_kg.py --kg ./output/kg.json --query "城乡规划" --mode global +``` + +## 技术亮点 + +1. **两阶段对话验证**: 提高知识抽取准确性 +2. **多模型并行**: 利用不同模型的优势 +3. **LLM-as-a-Judge**: 自动化质量评估 +4. **社区检测和摘要**: 参考GraphRAG的层次结构 +5. **RAG-based分类**: 利用向量检索提高分类准确率 +6. **DRIFT搜索**: 结合局部和全局信息的智能搜索 + +## 下一步建议 + +1. **性能优化**: + - 批量处理优化 + - 缓存机制 + - 异步处理改进 + +2. **功能增强**: + - 可视化工具 + - 更完善的演化分析 + - 增量更新支持 + +3. **生产化**: + - 单元测试完善 + - 集成测试 + - 性能监控 + - 错误恢复机制 + +项目已基本完成论文方法论的实现,可以直接用于法规知识图谱的构建和分析。 + + + + diff --git a/dofile/graphrag_pipeline/README.md b/dofile/graphrag_pipeline/README.md new file mode 100644 index 0000000..e9ae460 --- /dev/null +++ b/dofile/graphrag_pipeline/README.md @@ -0,0 +1,220 @@ +# 国土空间规划法规知识图谱构建与分析系统 + +基于大语言模型的国土空间规划法规知识图谱构建与原理提取研究项目。 + +## 项目简介 + +本项目实现了论文《基于大模型的国土空间规划法规知识图谱构建与原理提取研究》中提出的方法论,包括: + +- 法规文本预处理(HanLP) +- 基于大模型的知识抽取(NER、RE、评估、分类) +- 知识图谱构建(基于GraphRAG思路) +- 法理结构提取 +- 法规逻辑推理与演化规律识别 + +## 技术栈 + +- Python 3.12+ +- uv: 虚拟环境管理和包管理 +- HanLP: 中文NLP处理 +- LLM APIs: OpenAI, Anthropic, Qwen, Doubao, GLM +- NetworkX/iGraph: 图处理 +- PyYAML: 配置文件 + +## 快速开始 + +### 1. 环境设置 + +使用uv创建虚拟环境并安装依赖: + +```bash +# 进入项目目录 +cd dofile + +# 初始化uv项目并安装依赖 +uv sync + +# 激活虚拟环境 +source .venv/bin/activate # Linux/Mac +# 或 +.venv\Scripts\activate # Windows + +# 如果uv未安装,先安装uv: +# Windows: powershell -c "irm https://astral.sh/uv/install.ps1 | iex" +# Linux/Mac: curl -LsSf https://astral.sh/uv/install.sh | sh +``` + +### 2. 配置环境变量 + +复制 `.env.example` 到 `.env` 并填入API密钥: + +```bash +cp .env.example .env +``` + +编辑 `.env` 文件,填入所需的API密钥: +- OPENAI_API_KEY +- ANTHROPIC_API_KEY +- DASHSCOPE_API_KEY (Qwen) +- VOLCENGINE_ACCESS_KEY / VOLCENGINE_SECRET_KEY (Doubao) +- ZHIPUAI_API_KEY (GLM) +- SILICONFLOW_API_KEY (硅基流动,已在示例中配置) +- SILICONFLOW_API_BASE (可选,默认为 https://api.siliconflow.cn/v1) + +### 3. 运行项目 + +```bash +# 方式1: 完整流程(推荐) +# 从文档解析到知识图谱构建 +uv run python scripts/complete_pipeline.py --data-dir ../data/1法律 --output-dir ./output --max-docs 5 + +# 方式2: 分步骤执行 +# 步骤1: 实体识别 +uv run python scripts/extract.py --input ../data/1法律/3-中华人民共和国城乡规划法.docx --output ./output/entities.json + +# 步骤2: 构建知识图谱 +uv run python scripts/build_kg.py --data-dir ../data/1法律 --output ./output/kg.json --max-docs 5 + +# 步骤3: 查询知识图谱 +uv run python scripts/query_kg.py --kg ./output/kg.json --query "城乡规划" --mode global +uv run python scripts/query_kg.py --kg ./output/kg.json --query "城市更新" --mode local +uv run python scripts/query_kg.py --kg ./output/kg.json --query "土地管理" --mode drift +``` + +### 4. 运行测试 + +```bash +# 基础功能测试 +uv run python tests/test_basic.py + +# 测试硅基流动API配置 +uv run python tests/test_siliconflow.py + +# 测试文档解析 +uv run python tests/test_documents.py + +# 测试模型可用性 +uv run python tests/test_model.py + +# 测试指定模型 +uv run python tests/test_my_models.py + +# 快速测试(小规模知识图谱构建) +uv run python tests/test_quick.py +``` + +### 5. 使用硅基流动API + +硅基流动已经配置完成,您可以通过以下方式使用: + +```python +from src.utils.llm_client import LLMClient, LLMProvider +from src.utils.config import load_config + +config = load_config() +client = LLMClient( + provider=LLMProvider.SILICONFLOW, + model="Qwen/Qwen2.5-72B-Instruct", # 请根据实际可用模型修改 + config=config +) + +messages = [{"role": "user", "content": "你好"}] +response = client.chat(messages) +``` + +更多使用示例请参考: +- `examples/use_siliconflow.py` - 硅基流动使用示例 +- `ENV_SETUP.md` - 环境变量配置说明 + +## 项目结构 + +``` +dofile/ +├── src/ # 源代码 +│ ├── preprocessing/ # 文本预处理 +│ ├── ontology/ # 本体模型定义 +│ ├── prompts/ # Prompt模板 +│ ├── extraction/ # 知识抽取 +│ ├── kg_builder/ # 知识图谱构建 +│ ├── analysis/ # 分析和推理 +│ ├── query/ # 查询系统 +│ └── utils/ # 工具函数 +├── scripts/ # 执行脚本 +├── config/ # 配置文件 +└── tests/ # 测试文件 +``` + +## 项目状态 + +### 已完成模块 + +✅ **项目基础结构** +- uv项目配置(pyproject.toml) +- 项目文档(README.md) +- 目录结构 + +✅ **文本预处理模块** +- HanLP文本处理器(分词、依存句法、句法成分分析) +- 法规文档解析器(.docx文件解析) + +✅ **本体模型定义** +- 实体类型定义(9种类型) +- 关系类型定义(8种类型) +- 三维本体结构(法规层级—要素类型—关联关系) +- 四要素架构(目标—原则—要素—管控) + +✅ **Prompt模板系统** +- 统一Prompt模板框架(五个模块) +- NER Prompt构建器 +- RE Prompt构建器 + +✅ **LLM客户端封装** +- 多提供商支持(OpenAI, Anthropic, Qwen, Doubao, GLM) +- 统一API接口 +- 重试机制 + +✅ **知识抽取模块框架** +- NER提取器(两阶段对话) +- RE提取器(多模型并行) +- 三元组评估器(LLM-as-a-Judge) +- 实体关系分类器(RAG-based) + +✅ **知识图谱构建框架** +- 图谱索引器(参考GraphRAG) +- 图谱结构管理(NetworkX) + +✅ **分析和推理模块框架** +- 法理结构提取器 +- 逻辑推理器 +- 演化规律分析器 + +✅ **查询系统框架** +- 全局搜索(参考GraphRAG) +- 局部搜索 +- DRIFT搜索 + +### 待完善模块 + +⚠️ **部分优化空间**: +- 三元组评估的性能优化(批量评估) +- RAG-based分类的向量数据库优化(使用专业向量数据库) +- 大规模图谱的社区检测优化 +- 演化规律分析的时间序列处理完善 +- 可视化工具开发 +- 性能监控和日志分析 + +### 开发指南 + +各模块的详细文档: +- `src/preprocessing/`: 文本预处理 +- `src/ontology/`: 本体模型定义 +- `src/prompts/`: Prompt模板系统 +- `src/extraction/`: 知识抽取 +- `src/kg_builder/`: 知识图谱构建 +- `src/analysis/`: 分析和推理 +- `src/query/`: 查询系统 + +## 许可证 + +[待定] + diff --git a/dofile/graphrag_pipeline/TEST_RESULTS.md b/dofile/graphrag_pipeline/TEST_RESULTS.md new file mode 100644 index 0000000..22c830c --- /dev/null +++ b/dofile/graphrag_pipeline/TEST_RESULTS.md @@ -0,0 +1,111 @@ +# 测试结果报告 + +## 测试目标 +测试运行项目,处理3个法规文档文件,构建知识图谱。 + +## 测试环境 +- 操作系统: Windows +- Python版本: 3.12.7 (通过uv管理) +- 虚拟环境: `.venv` +- 测试时间: 2025-01-XX + +## 已完成的工作 + +### ✅ 1. 项目依赖安装 +- 使用 `uv sync` 成功安装了所有依赖包 +- 包括:hanlp, openai, networkx, python-docx 等92个包 + +### ✅ 2. 硅基流动API配置 +- 成功添加硅基流动API支持 +- 配置了API密钥和端点 +- 更新了相关文档 + +### ✅ 3. 代码修复 +- 修复了 `pyproject.toml` 中的包配置问题 +- 修复了 `TextProcessor` 使其能在HanLP不可用时降级运行 +- 所有代码通过语法检查 + +## 遇到的问题 + +### ⚠️ 问题1: lxml DLL加载失败 +**错误信息**: +``` +ImportError: DLL load failed while importing etree: 找不到指定的模块。 +``` + +**原因分析**: +- 虚拟环境的Python在导入lxml时,误用了系统conda环境的lxml包 +- conda环境的lxml可能缺少必要的DLL文件或版本不兼容 + +**解决方案**: +1. **方案A(推荐)**: 重新创建干净的虚拟环境 + ```bash + cd E:\Project\2025_LawGraph\dofile + rm -rf .venv # 或手动删除.venv目录 + uv sync + ``` + +2. **方案B**: 在虚拟环境中强制重新安装lxml + ```bash + .venv\Scripts\python.exe -m pip install --force-reinstall --no-deps lxml + ``` + +3. **方案C**: 使用conda环境(如果系统主要使用conda) + ```bash + conda install lxml + ``` + +## 测试脚本说明 + +已创建以下测试脚本: + +1. **`scripts/test_documents.py`** - 测试文档解析(不调用LLM) + - 解析3个文档 + - 切分为TextUnit + - 保存结果到JSON + +2. **`scripts/test_siliconflow.py`** - 测试硅基流动API配置 + +3. **`scripts/complete_pipeline.py`** - 完整流程(包含LLM调用) + +## 建议的测试步骤 + +### 步骤1: 修复环境问题 +```bash +# 重新创建虚拟环境 +cd E:\Project\2025_LawGraph\dofile +rm -rf .venv +uv sync +``` + +### 步骤2: 测试文档解析(不调用LLM) +```bash +uv run python scripts/test_documents.py +``` + +### 步骤3: 测试完整流程(调用LLM,需要API密钥) +```bash +# 确保.env文件已配置API密钥 +uv run python scripts/complete_pipeline.py --data-dir ../data/1法律 --output-dir ./output --max-docs 3 +``` + +## 项目状态 + +✅ **已完成**: +- 项目结构搭建 +- 所有核心模块实现 +- 硅基流动API集成 +- 文档完善 + +⚠️ **待解决**: +- 虚拟环境配置问题(lxml DLL) +- 完整的端到端测试 + +## 下一步 + +1. 修复虚拟环境问题后,可以成功运行测试 +2. 所有功能模块已实现,只需要解决环境配置即可正常使用 + + + + diff --git a/dofile/graphrag_pipeline/USAGE_EXAMPLES.md b/dofile/graphrag_pipeline/USAGE_EXAMPLES.md new file mode 100644 index 0000000..6e4a52c --- /dev/null +++ b/dofile/graphrag_pipeline/USAGE_EXAMPLES.md @@ -0,0 +1,114 @@ +# 使用示例 + +## 示例1: 从单个文档提取实体 + +```python +from src.preprocessing.document_parser import DocumentParser +from src.extraction.ner import NERExtractor +from src.utils.config import load_config + +# 加载配置 +config = load_config() + +# 解析文档 +parser = DocumentParser() +doc = parser.parse_docx("../data/1法律/3-中华人民共和国城乡规划法.docx") + +# 提取实体 +ner_extractor = NERExtractor(config=config) +entities = ner_extractor.extract(doc["text"]) + +print(f"识别到 {sum(len(v) for v in entities.values())} 个实体") +``` + +## 示例2: 构建知识图谱 + +```python +from src.preprocessing.document_parser import DocumentParser +from src.kg_builder.indexer import GraphIndexer +from src.utils.config import load_config + +# 加载配置 +config = load_config() + +# 解析文档 +parser = DocumentParser() +docs = parser.parse_directory("../data/1法律") + +# 切分为TextUnit +all_textunits = [] +for doc in docs[:3]: # 只处理前3个文档 + textunits = parser.split_into_textunits(doc, max_length=500) + all_textunits.extend(textunits) + +# 构建知识图谱 +indexer = GraphIndexer(config=config) +kg = indexer.index(all_textunits) + +print(f"图谱包含 {kg.number_of_nodes()} 个节点,{kg.number_of_edges()} 条边") +``` + +## 示例3: 查询知识图谱 + +```python +from src.kg_builder.graph import KnowledgeGraph +from src.query.global_search import GlobalSearcher +from src.query.local_search import LocalSearcher + +# 加载图谱 +kg = KnowledgeGraph() +kg.load("output/kg.json") + +# 全局搜索 +global_searcher = GlobalSearcher(kg.graph, communities={}) +result = global_searcher.search("城市更新的法规要求") +print(result["answer"]) + +# 局部搜索 +local_searcher = LocalSearcher(kg.graph) +result = local_searcher.search("城乡规划", depth=2) +print(f"找到 {result['subgraph']['num_nodes']} 个相关节点") +``` + +## 示例4: 法理结构提取 + +```python +from src.analysis.legal_structure import LegalStructureExtractor + +extractor = LegalStructureExtractor() + +# 提取四要素 +text = "为了促进城市可持续发展,应当坚持生态优先、绿色发展原则..." +elements = extractor.extract_four_elements(text) +print(elements) + +# 提取跨法规引用 +references = extractor.extract_cross_references(text) +print(references) +``` + +## 示例5: 三元组评估 + +```python +from src.extraction.evaluator import TripletEvaluator + +evaluator = TripletEvaluator() + +# 评估多个模型的结果 +peer_groups = { + "model_1": [{"head": "A", "relation": "管控", "tail": "B"}], + "model_2": [{"head": "A", "relation": "涉及", "tail": "B"}], +} + +result = evaluator.evaluate_peer_groups( + peer_groups, + source_text="原始文本..." +) + +print(f"最佳模型: {result['best_group']}") +print(f"评分: {result['scores']}") +``` + + + + diff --git a/dofile/graphrag_pipeline/config/ontology.yaml b/dofile/graphrag_pipeline/config/ontology.yaml new file mode 100644 index 0000000..217a2a0 --- /dev/null +++ b/dofile/graphrag_pipeline/config/ontology.yaml @@ -0,0 +1,108 @@ +# 法规知识本体定义文件 +# 对应论文中的三维本体结构:法规层级—要素类型—关联关系 + +# 法规层级 +legal_levels: + - 法律 + - 行政法规 + - 部门规章 + - 党中央国务院文件 + - 国家主管部门文件 + - 技术标准 + +# 实体类型(对应论文Table 1) +entity_types: + 法规条文: + definition: "法规文档中的具体条文,包括章、节、条、款、项等不同层级" + category: "structural" + 目标要素: + definition: "法规中明确表达的战略目标、发展愿景、总体目标等宏观性表述" + category: "four_elements" + element_type: "目标" + 原则要素: + definition: "法规遵循的基本准则、指导方针、基本原则等" + category: "four_elements" + element_type: "原则" + 空间要素: + definition: "法规涉及的地理空间实体,包括行政区划、功能区、保护区域等" + category: "four_elements" + element_type: "要素" + 功能要素: + definition: "法规涉及的功能性要素,如土地利用类型、产业类型、基础设施等" + category: "four_elements" + element_type: "要素" + 环境要素: + definition: "法规涉及的环境保护相关要素,如生态保护区、环境质量标准等" + category: "four_elements" + element_type: "要素" + 管控要素: + definition: "法规对各类要素的管控要求,包括禁止、限制、引导等管控措施" + category: "four_elements" + element_type: "管控" + 行政区域: + definition: "法规适用的行政区划,包括省、市、县、乡等不同层级" + category: "spatial" + 时间要素: + definition: "法规涉及的时间概念,如规划期限、实施阶段、有效期等" + category: "temporal" + +# 关系类型(对应论文Table 2) +relation_types: + 引用: + definition: "一条法规条文明确引用另一条法规条文" + category: "legal" + 层级: + definition: "上下级法规之间的隶属关系或法规条文内部的层级关系" + category: "structural" + 包含: + definition: "一个实体在逻辑上包含另一个实体" + category: "logical" + 管控: + definition: "管控要素对目标要素、原则要素或其他要素的约束关系" + category: "four_elements" + 遵循: + definition: "要素遵循某种原则或目标的关系" + category: "four_elements" + 涉及: + definition: "法规条文涉及某个空间、功能或环境要素" + category: "semantic" + 适用: + definition: "法规适用于某个行政区域" + category: "spatial" + 演化: + definition: "法规修订前后的变化关系或时间序列上的演化关系" + category: "temporal" + +# 四要素架构 +four_elements: + 目标: + entities: + - 目标要素 + 原则: + entities: + - 原则要素 + 要素: + entities: + - 空间要素 + - 功能要素 + - 环境要素 + 管控: + entities: + - 管控要素 + +# 层级关系 +hierarchical_relations: + - from: 目标 + to: 原则 + relation: 遵循 + - from: 原则 + to: 要素 + relation: 遵循 + - from: 要素 + to: 管控 + relation: 管控 + + + + + diff --git a/dofile/graphrag_pipeline/docs/ARCHITECTURE.md b/dofile/graphrag_pipeline/docs/ARCHITECTURE.md new file mode 100644 index 0000000..77d5988 --- /dev/null +++ b/dofile/graphrag_pipeline/docs/ARCHITECTURE.md @@ -0,0 +1,199 @@ +# 系统架构文档 + +## 整体架构 + +本系统采用模块化设计,参考GraphRAG的实现思路,实现法规知识图谱的构建、分析和查询。 + +## 核心模块说明 + +### 1. 文本预处理模块 (`src/preprocessing/`) + +**职责**:处理法规文档,为知识抽取做准备 + +- `TextProcessor`: 使用HanLP进行中文文本处理 + - 分词(粗粒度/细粒度) + - 依存句法分析 + - 句法成分分析 + - 词性标注 + - 省略检测 + - 共指消解候选提取 + +- `DocumentParser`: 解析Word文档 + - .docx文件解析 + - 章节结构识别 + - TextUnit切分(参考GraphRAG) + +### 2. 本体模型模块 (`src/ontology/`) + +**职责**:定义知识图谱的语义结构 + +- `EntityType`: 9种实体类型枚举 +- `RelationType`: 8种关系类型枚举 +- `OntologySchema`: 三维本体结构管理 + - 法规层级维度 + - 要素类型维度 + - 关联关系维度 + - 四要素架构(目标—原则—要素—管控) + +### 3. Prompt模板模块 (`src/prompts/`) + +**职责**:构建统一的Prompt模板系统 + +- `PromptTemplate`: 统一模板框架 + - 任务描述模块 + - 候选目标模块 + - 任务示例模块(少样本学习) + - 任务强调模块 + - 二次对话模块 + +- `NERPromptBuilder`: NER任务Prompt构建 +- `REPromptBuilder`: RE任务Prompt构建 + +### 4. 知识抽取模块 (`src/extraction/`) + +**职责**:从文本中提取结构化知识 + +- `NERExtractor`: 命名实体识别 + - 使用Qwen-Max模型 + - 两阶段对话验证 + - JSON格式输出 + +- `REExtractor`: 关系抽取 + - 多模型并行(GPT-4o, Doubao-pro, GLM-4) + - 二次对话验证 + - 句法信息集成 + - 上下文利用 + +- `TripletEvaluator`: 三元组评估 + - LLM-as-a-Judge方法 + - 成对排序 + - 五维度评分(语义准确性、一致性、事实性、准确性、可理解性) + - 低质量三元组筛选 + +- `EntityRelationClassifier`: 实体关系分类 + - RAG-based分类(使用向量检索) + - Prompt-based分类 + - 粗粒度和细粒度分类 + +### 5. 知识图谱构建模块 (`src/kg_builder/`) + +**职责**:构建和管理知识图谱(参考GraphRAG) + +- `GraphIndexer`: 图谱索引器 + - 文本切分为TextUnit + - 实体和关系提取 + - 图构建 + - 社区检测(Leiden算法) + - 社区摘要生成 + +- `KnowledgeGraph`: 图谱结构管理 + - 基于NetworkX + - 三元组添加 + - 路径查询 + - 子图提取 + - 保存/加载 + +### 6. 分析和推理模块 (`src/analysis/`) + +**职责**:分析法规体系和识别规律 + +- `LegalStructureExtractor`: 法理结构提取 + - 四要素提取(目标、原则、要素、管控) + - 跨法规引用关系提取 + - 例外条款识别 + +- `LegalReasoner`: 逻辑推理 + - 路径查询(多跳关系) + - 中心性分析 + - 子图分析 + +- `EvolutionAnalyzer`: 演化规律识别 + - 时间维度分析 + - 关系演化追踪 + - 网络演化分析 + +### 7. 查询系统模块 (`src/query/`) + +**职责**:知识图谱查询和问答(参考GraphRAG) + +- `GlobalSearcher`: 全局搜索 + - 利用社区摘要 + - 相关性评估 + - 综合答案生成 + +- `LocalSearcher`: 局部搜索 + - 实体邻居扩展 + - 深度控制 + - 子图提取 + +- `DriftSearcher`: DRIFT搜索 + - 结合局部和全局信息 + - 社区上下文整合 + - 综合答案生成 + +### 8. 工具模块 (`src/utils/`) + +**职责**:提供通用工具函数 + +- `Config`: 配置管理 + - 环境变量加载 + - API密钥管理 + - 参数配置 + +- `LLMClient`: LLM客户端封装 + - 多提供商支持 + - 统一API接口 + - 异步/同步支持 + - 重试机制 + +- `file_utils`: 文件操作工具 + +## 数据流 + +``` +法规文档(.docx) + ↓ +DocumentParser (解析) + ↓ +TextUnit列表 + ↓ +TextProcessor (预处理) + NERExtractor (实体识别) + ↓ +实体列表 + ↓ +REExtractor (关系抽取) + TextProcessor (句法信息) + ↓ +三元组列表 + ↓ +TripletEvaluator (评估筛选) + ↓ +高质量三元组 + ↓ +GraphIndexer (图构建) + ↓ +KnowledgeGraph + ↓ +社区检测 + 摘要生成 + ↓ +完整知识图谱 + 社区信息 + ↓ +Query系统 (查询和问答) +``` + +## 技术栈 + +- **语言**: Python 3.12+ +- **包管理**: uv +- **NLP**: HanLP +- **图处理**: NetworkX, python-igraph +- **LLM**: OpenAI, Anthropic, Qwen, Doubao, GLM +- **数据处理**: pandas, numpy + +## 参考实现 + +- GraphRAG: https://msdocs.cn/graphrag/ +- 论文方法: `officefile/paper.tex` + + + + diff --git a/dofile/graphrag_pipeline/examples/use_siliconflow.py b/dofile/graphrag_pipeline/examples/use_siliconflow.py new file mode 100644 index 0000000..68971d7 --- /dev/null +++ b/dofile/graphrag_pipeline/examples/use_siliconflow.py @@ -0,0 +1,109 @@ +""" +使用硅基流动API的示例 +""" + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.utils.llm_client import LLMClient, LLMProvider +from src.utils.config import load_config +from src.extraction.ner import NERExtractor + + +def example_basic_usage(): + """基础使用示例""" + print("示例1: 基础API调用") + print("-" * 60) + + config = load_config() + client = LLMClient( + provider=LLMProvider.SILICONFLOW, + model="Qwen/Qwen2.5-72B-Instruct", # 请根据实际情况修改 + config=config + ) + + messages = [ + {"role": "system", "content": "你是一个法规文本分析专家。"}, + {"role": "user", "content": "请分析以下文本中的实体:城市更新需要遵循国土空间规划的要求。"} + ] + + response = client.chat(messages) + print(f"响应: {response}\n") + + +def example_ner_with_siliconflow(): + """使用硅基流动进行实体识别""" + print("示例2: 使用硅基流动进行实体识别") + print("-" * 60) + + config = load_config() + + # 注意:NERExtractor默认使用Qwen,我们可以修改为使用硅基流动 + # 方法1: 直接创建NER提取器并替换LLM客户端 + ner_extractor = NERExtractor(model="Qwen/Qwen2.5-72B-Instruct", config=config) + + # 替换为硅基流动客户端 + ner_extractor.llm_client = LLMClient( + provider=LLMProvider.SILICONFLOW, + model="Qwen/Qwen2.5-72B-Instruct", # 请根据实际情况修改 + config=config + ) + + text = "城市更新应当遵循国土空间规划,坚持生态优先、绿色发展原则。" + entities = ner_extractor.extract(text) + + print(f"输入文本: {text}") + print(f"识别到的实体: {entities}\n") + + +def example_multi_provider_comparison(): + """多提供商对比示例""" + print("示例3: 对比不同LLM提供商的响应") + print("-" * 60) + + config = load_config() + messages = [ + {"role": "user", "content": "什么是知识图谱?用一句话回答。"} + ] + + providers = [ + (LLMProvider.SILICONFLOW, "Qwen/Qwen2.5-72B-Instruct"), + # 可以添加其他提供商进行对比 + ] + + for provider, model in providers: + try: + client = LLMClient(provider=provider, model=model, config=config) + response = client.chat(messages) + print(f"{provider.value}: {response[:100]}...") + except Exception as e: + print(f"{provider.value}: 调用失败 - {e}") + + +if __name__ == "__main__": + print("=" * 60) + print("硅基流动API使用示例") + print("=" * 60) + print() + + # 运行示例 + try: + example_basic_usage() + except Exception as e: + print(f"示例1执行失败: {e}\n") + + try: + example_ner_with_siliconflow() + except Exception as e: + print(f"示例2执行失败: {e}\n") + + try: + example_multi_provider_comparison() + except Exception as e: + print(f"示例3执行失败: {e}\n") + + + + diff --git a/dofile/graphrag_pipeline/pyproject.toml b/dofile/graphrag_pipeline/pyproject.toml new file mode 100644 index 0000000..cf12316 --- /dev/null +++ b/dofile/graphrag_pipeline/pyproject.toml @@ -0,0 +1,63 @@ +[project] +name = "law-graph-kg" +version = "0.1.0" +description = "国土空间规划法规知识图谱构建与分析系统" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "hanlp>=2.1.0", + "openai>=1.0.0", + "anthropic>=0.18.0", + "dashscope>=1.17.0", # 阿里云Qwen API + "volcengine-python-sdk>=1.0.0", # 字节跳动Doubao API + "zhipuai>=2.0.0", # 智谱GLM API + "networkx>=3.2.0", + "python-igraph>=0.11.0", + "pyyaml>=6.0", + "pandas>=2.0.0", + "python-dotenv>=1.0.0", + "numpy>=1.24.0", + "tqdm>=4.66.0", + "aiohttp>=3.9.0", + "tenacity>=8.2.0", # 重试机制 + "python-docx>=1.1.0", # Word文档解析 +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.4.0", + "pytest-asyncio>=0.21.0", + "black>=23.0.0", + "ruff>=0.1.0", + "mypy>=1.5.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src"] + +[tool.uv] +dev-dependencies = [ + "pytest>=7.4.0", + "pytest-asyncio>=0.21.0", + "black>=23.0.0", + "ruff>=0.1.0", + "mypy>=1.5.0", +] + +[tool.black] +line-length = 100 +target-version = ['py312'] + +[tool.ruff] +line-length = 100 +target-version = "py312" + +[tool.mypy] +python_version = "3.12" +warn_return_any = true +warn_unused_configs = true + diff --git a/dofile/graphrag_pipeline/scripts/build_kg.py b/dofile/graphrag_pipeline/scripts/build_kg.py new file mode 100644 index 0000000..d6d14a8 --- /dev/null +++ b/dofile/graphrag_pipeline/scripts/build_kg.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +""" +知识图谱构建脚本 +示例用法:python scripts/build_kg.py --data-dir ../data/1法律 --output ./output/kg.json +""" + +import argparse +import json +import logging +import sys +from pathlib import Path + +# 添加src到路径 +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.preprocessing.document_parser import DocumentParser +from src.kg_builder.indexer import GraphIndexer +from src.kg_builder.graph import KnowledgeGraph +from src.utils.config import Config, load_config + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +def main(): + parser = argparse.ArgumentParser(description="构建法规知识图谱") + parser.add_argument( + "--data-dir", + type=str, + required=True, + help="法规数据目录路径" + ) + parser.add_argument( + "--output", + type=str, + default="output/kg.json", + help="输出文件路径" + ) + parser.add_argument( + "--max-docs", + type=int, + default=None, + help="最大处理文档数(用于测试)" + ) + parser.add_argument( + "--no-verification", + action="store_true", + help="不使用二次对话验证(加快速度)" + ) + + args = parser.parse_args() + + # 加载配置 + config = load_config() + + # 确保输出目录存在 + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + + # 解析文档 + logger.info(f"解析目录: {args.data_dir}") + parser_obj = DocumentParser() + docs = parser_obj.parse_directory(args.data_dir) + + if args.max_docs: + docs = docs[:args.max_docs] + + logger.info(f"找到 {len(docs)} 个文档") + + # 切分为TextUnit + logger.info("切分文档为TextUnit...") + all_textunits = [] + for doc in docs: + textunits = parser_obj.split_into_textunits( + doc, + max_length=config.MAX_TEXTUNIT_LENGTH + ) + for tu in textunits: + tu["doc_id"] = doc.get("file_path", "") + tu["id"] = f"{doc.get('file_path', '')}_{tu.get('paragraph_index', 0)}" + all_textunits.extend(textunits) + + logger.info(f"共生成 {len(all_textunits)} 个TextUnit") + + # 构建知识图谱 + logger.info("开始构建知识图谱...") + indexer = GraphIndexer(config=config) + kg_graph = indexer.index( + all_textunits, + use_verification=not args.no_verification + ) + + # 保存图谱 + kg_obj = KnowledgeGraph() + kg_obj.graph = kg_graph + kg_obj.save(str(output_path), format="json") + + # 保存统计信息 + stats = { + "num_docs": len(docs), + "num_textunits": len(all_textunits), + "num_nodes": kg_graph.number_of_nodes(), + "num_edges": kg_graph.number_of_edges(), + "metadata": kg_graph.graph.get("metadata", {}) + } + + stats_path = output_path.parent / f"{output_path.stem}_stats.json" + with open(stats_path, "w", encoding="utf-8") as f: + json.dump(stats, f, ensure_ascii=False, indent=2) + + logger.info(f"知识图谱构建完成!") + logger.info(f" - 节点数: {stats['num_nodes']}") + logger.info(f" - 边数: {stats['num_edges']}") + logger.info(f" - 结果已保存到: {output_path}") + logger.info(f" - 统计信息已保存到: {stats_path}") + + +if __name__ == "__main__": + main() + + + + diff --git a/dofile/graphrag_pipeline/scripts/complete_pipeline.py b/dofile/graphrag_pipeline/scripts/complete_pipeline.py new file mode 100644 index 0000000..38a5cb0 --- /dev/null +++ b/dofile/graphrag_pipeline/scripts/complete_pipeline.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +""" +完整流程脚本:从文档到知识图谱构建和查询 +示例用法:python scripts/complete_pipeline.py --data-dir ../data/1法律 --output-dir ./output +""" + +import argparse +import logging +import sys +from pathlib import Path + +# 修复:移除conda环境的路径,确保使用虚拟环境的包 +sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()] +# 添加src到路径 +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.preprocessing.document_parser import DocumentParser +from src.kg_builder.indexer import GraphIndexer +from src.kg_builder.graph import KnowledgeGraph +from src.utils.config import Config, load_config + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +def main(): + parser = argparse.ArgumentParser(description="完整的知识图谱构建流程") + parser.add_argument( + "--data-dir", + type=str, + required=True, + help="法规数据目录路径" + ) + parser.add_argument( + "--output-dir", + type=str, + default="output", + help="输出目录" + ) + parser.add_argument( + "--max-docs", + type=int, + default=None, + help="最大处理文档数(用于测试)" + ) + parser.add_argument( + "--skip-index", + action="store_true", + help="跳过索引构建(使用已有索引)" + ) + + args = parser.parse_args() + + # 加载配置 + config = load_config() + + # 创建输出目录 + output_dir = Path(args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + kg_file = output_dir / "knowledge_graph.json" + + # 步骤1: 构建知识图谱索引 + if not args.skip_index: + logger.info("=" * 60) + logger.info("步骤1: 解析文档并构建知识图谱") + logger.info("=" * 60) + + # 解析文档 + logger.info(f"解析目录: {args.data_dir}") + parser_obj = DocumentParser() + docs = parser_obj.parse_directory(args.data_dir) + + if args.max_docs: + docs = docs[:args.max_docs] + + logger.info(f"找到 {len(docs)} 个文档") + + # 切分为TextUnit + logger.info("切分文档为TextUnit...") + all_textunits = [] + for doc in docs: + textunits = parser_obj.split_into_textunits( + doc, + max_length=config.MAX_TEXTUNIT_LENGTH + ) + for tu in textunits: + tu["doc_id"] = doc.get("file_path", "") + tu["id"] = f"{doc.get('file_path', '')}_{tu.get('paragraph_index', 0)}" + all_textunits.extend(textunits) + + logger.info(f"共生成 {len(all_textunits)} 个TextUnit") + + # 构建知识图谱 + logger.info("开始构建知识图谱...") + indexer = GraphIndexer(config=config) + kg_graph = indexer.index(all_textunits, use_verification=True) + + # 保存图谱 + kg_obj = KnowledgeGraph() + kg_obj.graph = kg_graph + kg_obj.save(str(kg_file), format="json") + + logger.info(f"知识图谱已保存到: {kg_file}") + else: + logger.info(f"跳过索引构建,从文件加载: {kg_file}") + kg_obj = KnowledgeGraph() + kg_obj.load(str(kg_file), format="json") + + # 步骤2: 展示图谱统计信息 + logger.info("=" * 60) + logger.info("步骤2: 知识图谱统计信息") + logger.info("=" * 60) + + if kg_obj.graph: + metadata = kg_obj.graph.graph.get("metadata", {}) + logger.info(f"节点数: {kg_obj.graph.number_of_nodes()}") + logger.info(f"边数: {kg_obj.graph.number_of_edges()}") + logger.info(f"三元组数: {metadata.get('num_triplets', 0)}") + logger.info(f"社区数: {metadata.get('num_communities', 0)}") + + # 展示一些社区摘要 + summaries = metadata.get("summaries", {}) + if summaries: + logger.info("\n前5个社区摘要:") + for i, (comm_id, comm_data) in enumerate(list(summaries.items())[:5], 1): + logger.info(f"{i}. {comm_id}: {comm_data.get('summary', '无摘要')[:100]}...") + + logger.info("\n知识图谱构建流程完成!") + + +if __name__ == "__main__": + main() + diff --git a/dofile/graphrag_pipeline/scripts/extract.py b/dofile/graphrag_pipeline/scripts/extract.py new file mode 100644 index 0000000..0542e8d --- /dev/null +++ b/dofile/graphrag_pipeline/scripts/extract.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +""" +知识抽取脚本 +示例用法:python scripts/extract.py --input data/sample.txt --output output/entities.json +""" + +import argparse +import json +import logging +import sys +from pathlib import Path + +# 添加src到路径 +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.preprocessing.document_parser import DocumentParser +from src.extraction.ner import NERExtractor +from src.utils.config import Config, load_config + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +def main(): + parser = argparse.ArgumentParser(description="法规知识抽取工具") + parser.add_argument( + "--input", + type=str, + required=True, + help="输入文件路径(.docx或.txt)" + ) + parser.add_argument( + "--output", + type=str, + default="output/entities.json", + help="输出文件路径" + ) + parser.add_argument( + "--use-verification", + action="store_true", + default=True, + help="使用二次对话验证" + ) + + args = parser.parse_args() + + # 加载配置 + config = load_config() + + # 确保输出目录存在 + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + + # 解析文档 + input_path = Path(args.input) + if input_path.suffix == ".docx": + parser = DocumentParser() + doc_data = parser.parse_docx(str(input_path)) + text = doc_data["text"] + else: + # 假设是纯文本文件 + with open(input_path, "r", encoding="utf-8") as f: + text = f.read() + + # 执行NER + logger.info("开始实体识别...") + ner_extractor = NERExtractor(config=config) + entities = ner_extractor.extract(text, use_verification=args.use_verification) + + # 保存结果 + result = { + "input_file": str(input_path), + "entities": entities, + "statistics": { + entity_type: len(entity_list) + for entity_type, entity_list in entities.items() + } + } + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(result, f, ensure_ascii=False, indent=2) + + logger.info(f"实体识别完成,结果已保存到: {output_path}") + logger.info(f"统计信息: {result['statistics']}") + + +if __name__ == "__main__": + main() + + + + + diff --git a/dofile/graphrag_pipeline/scripts/query_kg.py b/dofile/graphrag_pipeline/scripts/query_kg.py new file mode 100644 index 0000000..1ad9916 --- /dev/null +++ b/dofile/graphrag_pipeline/scripts/query_kg.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +""" +知识图谱查询脚本 +示例用法:python scripts/query_kg.py --kg output/kg.json --query "城市更新" +""" + +import argparse +import json +import logging +import sys +from pathlib import Path + +# 添加src到路径 +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.kg_builder.graph import KnowledgeGraph +from src.query.global_search import GlobalSearcher +from src.query.local_search import LocalSearcher +from src.utils.config import load_config + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +def main(): + parser = argparse.ArgumentParser(description="查询知识图谱") + parser.add_argument( + "--kg", + type=str, + required=True, + help="知识图谱文件路径(JSON格式)" + ) + parser.add_argument( + "--query", + type=str, + required=True, + help="查询文本" + ) + parser.add_argument( + "--mode", + type=str, + choices=["global", "local", "drift"], + default="global", + help="查询模式" + ) + parser.add_argument( + "--output", + type=str, + default=None, + help="输出结果文件路径" + ) + + args = parser.parse_args() + + # 加载知识图谱 + logger.info(f"加载知识图谱: {args.kg}") + kg = KnowledgeGraph() + kg.load(str(args.kg), format="json") + + # 获取社区信息(从元数据) + communities = {} + if hasattr(kg, 'graph') and kg.graph: + metadata = kg.graph.graph.get("metadata", {}) + communities = metadata.get("summaries", {}) + + # 执行查询 + logger.info(f"执行{args.mode}查询: {args.query}") + + kg_graph = kg.graph if hasattr(kg, 'graph') and kg.graph else None + + if args.mode == "global": + searcher = GlobalSearcher(kg_graph, communities) + result = searcher.search(args.query) + elif args.mode == "local": + searcher = LocalSearcher(kg_graph) + # 尝试从查询中提取实体 + entity = args.query # 简化处理,可以将查询作为实体 + result = searcher.search(entity, depth=2) + else: + # DRIFT search + from src.query.drift_search import DriftSearcher + searcher = DriftSearcher(kg_graph, communities) + result = searcher.search(args.query) + + # 输出结果 + print("\n查询结果:") + print(json.dumps(result, ensure_ascii=False, indent=2)) + + if args.output: + with open(args.output, "w", encoding="utf-8") as f: + json.dump(result, f, ensure_ascii=False, indent=2) + logger.info(f"结果已保存到: {args.output}") + + +if __name__ == "__main__": + main() + diff --git a/dofile/graphrag_pipeline/src/__init__.py b/dofile/graphrag_pipeline/src/__init__.py new file mode 100644 index 0000000..e61148f --- /dev/null +++ b/dofile/graphrag_pipeline/src/__init__.py @@ -0,0 +1,10 @@ +""" +国土空间规划法规知识图谱构建与分析系统 +""" + +__version__ = "0.1.0" + + + + + diff --git a/dofile/graphrag_pipeline/src/analysis/__init__.py b/dofile/graphrag_pipeline/src/analysis/__init__.py new file mode 100644 index 0000000..89b629d --- /dev/null +++ b/dofile/graphrag_pipeline/src/analysis/__init__.py @@ -0,0 +1,12 @@ +"""分析和推理模块""" + +from .legal_structure import LegalStructureExtractor +from .reasoning import LegalReasoner +from .evolution import EvolutionAnalyzer + +__all__ = ["LegalStructureExtractor", "LegalReasoner", "EvolutionAnalyzer"] + + + + + diff --git a/dofile/graphrag_pipeline/src/analysis/evolution.py b/dofile/graphrag_pipeline/src/analysis/evolution.py new file mode 100644 index 0000000..69f1c3c --- /dev/null +++ b/dofile/graphrag_pipeline/src/analysis/evolution.py @@ -0,0 +1,54 @@ +""" +演化规律识别模块 +""" + +import logging +from typing import List, Dict, Any +from datetime import datetime + +logger = logging.getLogger(__name__) + + +class EvolutionAnalyzer: + """演化规律分析器""" + + def analyze_temporal_changes( + self, + regulations: List[Dict[str, Any]], + ) -> Dict[str, Any]: + """ + 时间维度分析:识别不同时期法规内容的变化 + + Args: + regulations: 法规列表,每个包含时间和内容 + + Returns: + 演化分析结果 + """ + # TODO: 实现时间序列分析 + logger.info("执行时间维度演化分析...") + return {} + + def track_relation_evolution( + self, + entity: str, + time_points: List[datetime], + ) -> Dict[str, Any]: + """ + 关系演化追踪 + + Args: + entity: 实体名称 + time_points: 时间点列表 + + Returns: + 演化模式 + """ + # TODO: 实现关系演化追踪 + logger.info(f"追踪实体 {entity} 的关系演化...") + return {} + + + + + diff --git a/dofile/graphrag_pipeline/src/analysis/legal_structure.py b/dofile/graphrag_pipeline/src/analysis/legal_structure.py new file mode 100644 index 0000000..ab965df --- /dev/null +++ b/dofile/graphrag_pipeline/src/analysis/legal_structure.py @@ -0,0 +1,98 @@ +""" +法理结构提取模块 +""" + +import logging +from typing import List, Dict, Any +import re + +logger = logging.getLogger(__name__) + + +class LegalStructureExtractor: + """法理结构提取器""" + + def extract_four_elements(self, text: str) -> Dict[str, List[str]]: + """ + 提取四要素:目标、原则、要素、管控 + + Args: + text: 法规文本 + + Returns: + 四要素字典 + """ + result = { + "目标": [], + "原则": [], + "要素": [], + "管控": [] + } + + # 目标要素识别("为了...""应当..."等) + target_patterns = [ + r"为了[^,。]+", + r"应当[^,。]+", + r"旨在[^,。]+", + ] + for pattern in target_patterns: + matches = re.findall(pattern, text) + result["目标"].extend(matches) + + # 原则要素识别("遵循...""坚持..."等) + principle_patterns = [ + r"遵循[^,。]+", + r"坚持[^,。]+", + r"按照[^,。]+", + ] + for pattern in principle_patterns: + matches = re.findall(pattern, text) + result["原则"].extend(matches) + + # 管控要素识别("禁止...""限制...""引导..."等) + control_patterns = [ + r"禁止[^,。]+", + r"限制[^,。]+", + r"引导[^,。]+", + r"不得[^,。]+", + ] + for pattern in control_patterns: + matches = re.findall(pattern, text) + result["管控"].extend(matches) + + return result + + def extract_cross_references(self, text: str) -> List[Dict[str, str]]: + """ + 提取跨法规引用关系 + + Args: + text: 法规文本 + + Returns: + 引用关系列表 + """ + references = [] + + # 引用模式:"依据...""按照..."等 + patterns = [ + (r"依据《([^》]+)》", "依据"), + (r"按照《([^》]+)》", "按照"), + (r"参照《([^》]+)》", "参照"), + ] + + for pattern, relation_type in patterns: + matches = re.finditer(pattern, text) + for match in matches: + references.append({ + "cited_document": match.group(1), + "relation": relation_type, + "position": match.start() + }) + + return references + + + + + diff --git a/dofile/graphrag_pipeline/src/analysis/reasoning.py b/dofile/graphrag_pipeline/src/analysis/reasoning.py new file mode 100644 index 0000000..cb6b0f5 --- /dev/null +++ b/dofile/graphrag_pipeline/src/analysis/reasoning.py @@ -0,0 +1,59 @@ +""" +逻辑推理模块 +""" + +import logging +from typing import List, Dict, Any +import networkx as nx + +from ..kg_builder.graph import KnowledgeGraph + +logger = logging.getLogger(__name__) + + +class LegalReasoner: + """法规逻辑推理器""" + + def __init__(self, kg: KnowledgeGraph): + """ + 初始化推理器 + + Args: + kg: 知识图谱 + """ + self.kg = kg + + def path_query(self, source: str, target: str, max_hops: int = 3) -> List: + """ + 路径查询:发现实体间的多跳关系 + + Args: + source: 源实体 + target: 目标实体 + max_hops: 最大跳数 + + Returns: + 路径列表 + """ + return self.kg.query_path(source, target, max_hops) + + def centrality_analysis(self) -> Dict[str, Any]: + """ + 中心性分析:识别关键节点 + + Returns: + 中心性分析结果 + """ + graph = self.kg.graph + degree_centrality = nx.degree_centrality(graph) + betweenness_centrality = nx.betweenness_centrality(graph) + + return { + "degree": degree_centrality, + "betweenness": betweenness_centrality + } + + + + + diff --git a/dofile/graphrag_pipeline/src/extraction/__init__.py b/dofile/graphrag_pipeline/src/extraction/__init__.py new file mode 100644 index 0000000..90fdd24 --- /dev/null +++ b/dofile/graphrag_pipeline/src/extraction/__init__.py @@ -0,0 +1,18 @@ +"""知识抽取模块""" + +from .ner import NERExtractor +from .re import REExtractor +from .evaluator import TripletEvaluator +from .classifier import EntityRelationClassifier + +__all__ = [ + "NERExtractor", + "REExtractor", + "TripletEvaluator", + "EntityRelationClassifier", +] + + + + + diff --git a/dofile/graphrag_pipeline/src/extraction/classifier.py b/dofile/graphrag_pipeline/src/extraction/classifier.py new file mode 100644 index 0000000..f5a68f2 --- /dev/null +++ b/dofile/graphrag_pipeline/src/extraction/classifier.py @@ -0,0 +1,325 @@ +""" +实体关系分类模块 +使用RAG-based方法进行分类 +""" + +import logging +import numpy as np +from typing import List, Dict, Any, Optional +from collections import defaultdict + +from ..utils.config import Config +from ..utils.llm_client import LLMClient, LLMProvider +from ..ontology.entities import get_four_element_types + +logger = logging.getLogger(__name__) + + +class EntityRelationClassifier: + """实体关系分类器""" + + def __init__(self, config: Optional[Config] = None): + """ + 初始化分类器 + + Args: + config: 配置对象 + """ + self.config = config or Config() + self.embedding_model = None + self.knowledge_base = {} # 向量数据库(简化版) + self.four_elements = get_four_element_types() + + def _get_embedding(self, text: str) -> Optional[np.ndarray]: + """ + 获取文本嵌入向量 + + Args: + text: 输入文本 + + Returns: + 嵌入向量 + """ + try: + # 使用OpenAI的embedding模型 + import openai + client = openai.OpenAI(api_key=self.config.OPENAI_API_KEY) + response = client.embeddings.create( + model="text-embedding-3-large", + input=text + ) + return np.array(response.data[0].embedding) + except Exception as e: + logger.warning(f"获取嵌入向量失败: {e}") + return None + + def _cosine_similarity(self, vec_a: np.ndarray, vec_b: np.ndarray) -> float: + """计算余弦相似度""" + dot_product = np.dot(vec_a, vec_b) + norm_a = np.linalg.norm(vec_a) + norm_b = np.linalg.norm(vec_b) + if norm_a == 0 or norm_b == 0: + return 0.0 + return dot_product / (norm_a * norm_b) + + def build_knowledge_base( + self, + labeled_entities: List[Dict[str, Any]], + ): + """ + 构建RAG知识库(使用已标注的实体样本) + + Args: + labeled_entities: 已标注实体列表,格式:[{"text": "实体", "label": "标签", ...}, ...] + """ + logger.info(f"构建知识库,包含 {len(labeled_entities)} 个样本...") + + self.knowledge_base = defaultdict(list) + + for entity in labeled_entities: + text = entity.get("text", "") + label = entity.get("label", "") + + if not text or not label: + continue + + # 获取嵌入向量 + embedding = self._get_embedding(text) + if embedding is not None: + self.knowledge_base[label].append({ + "text": text, + "embedding": embedding, + "metadata": entity + }) + + logger.info(f"知识库构建完成,包含 {len(self.knowledge_base)} 个类别") + + def classify_entities_rag( + self, + entities: List[str], + top_k: int = 5, + ) -> List[Dict[str, str]]: + """ + 使用RAG方法对实体进行粗粒度分类 + + Args: + entities: 实体文本列表 + top_k: 检索的top-k相似样本数 + + Returns: + 分类结果列表,格式:[{"entity": "...", "label": "...", "confidence": ...}, ...] + """ + if not self.knowledge_base: + logger.warning("知识库为空,无法进行RAG分类") + return [{"entity": e, "label": "未知", "confidence": 0.0} for e in entities] + + results = [] + + for entity_text in entities: + # 获取实体嵌入 + entity_embedding = self._get_embedding(entity_text) + if entity_embedding is None: + results.append({ + "entity": entity_text, + "label": "未知", + "confidence": 0.0 + }) + continue + + # 在所有类别中查找最相似的样本 + best_match = None + best_similarity = -1.0 + + for label, samples in self.knowledge_base.items(): + for sample in samples: + similarity = self._cosine_similarity( + entity_embedding, + sample["embedding"] + ) + if similarity > best_similarity: + best_similarity = similarity + best_match = label + + results.append({ + "entity": entity_text, + "label": best_match or "未知", + "confidence": best_similarity + }) + + return results + + def classify_entities_prompt( + self, + entities: List[str], + categories: List[str] = None, + ) -> List[Dict[str, str]]: + """ + 使用Prompt方法对实体进行分类 + + Args: + entities: 实体文本列表 + categories: 分类类别列表,默认使用四要素类型 + + Returns: + 分类结果列表 + """ + if categories is None: + categories = list(self.four_elements.keys()) + + try: + llm_client = LLMClient( + provider=LLMProvider.QWEN, + model="qwen-max", + config=self.config + ) + except Exception as e: + logger.error(f"LLM客户端初始化失败: {e}") + return [] + + results = [] + + for entity_text in entities: + prompt = f"""请将以下实体分类到以下类别之一: + +类别: +{chr(10).join([f'- {cat}' for cat in categories])} + +实体:{entity_text} + +请只回答类别名称:""" + + messages = [ + {"role": "system", "content": "你是一个城市规划和法律文本分类专家。"}, + {"role": "user", "content": prompt} + ] + + try: + label = llm_client.chat(messages).strip() + # 验证标签是否在有效类别中 + if label not in categories: + label = "未知" + + results.append({ + "entity": entity_text, + "label": label, + "confidence": 1.0 + }) + except Exception as e: + logger.warning(f"实体分类失败 {entity_text}: {e}") + results.append({ + "entity": entity_text, + "label": "未知", + "confidence": 0.0 + }) + + return results + + def classify_entities( + self, + entities: List[str], + method: str = "rag", + ) -> List[Dict[str, str]]: + """ + 对实体进行粗粒度分类(目标、原则、要素、管控) + + Args: + entities: 实体文本列表 + method: 分类方法("rag"或"prompt") + + Returns: + 分类结果列表 + """ + logger.info(f"使用 {method} 方法对 {len(entities)} 个实体进行分类...") + + if method == "rag": + if not self.knowledge_base: + logger.warning("RAG知识库为空,切换到prompt方法") + method = "prompt" + else: + return self.classify_entities_rag(entities) + + if method == "prompt": + return self.classify_entities_prompt(entities) + + return [] + + def classify_relations( + self, + relations: List[str], + ) -> List[Dict[str, str]]: + """ + 对关系进行分类 + + Args: + relations: 关系文本列表 + + Returns: + 分类结果列表 + """ + logger.info(f"对 {len(relations)} 个关系进行分类...") + + # 关系分类类别(对应论文Table 5) + relation_categories = { + "引用": "一条法规条文明确引用另一条法规条文", + "层级": "上下级法规之间的隶属关系", + "包含": "一个实体在逻辑上包含另一个实体", + "管控": "管控要素对目标要素、原则要素或其他要素的约束关系", + "遵循": "要素遵循某种原则或目标的关系", + "涉及": "法规条文涉及某个空间、功能或环境要素", + "适用": "法规适用于某个行政区域", + "演化": "法规修订前后的变化关系", + } + + try: + llm_client = LLMClient( + provider=LLMProvider.QWEN, + model="qwen-max", + config=self.config + ) + except Exception as e: + logger.error(f"LLM客户端初始化失败: {e}") + return [] + + results = [] + + for relation_text in relations: + categories_desc = "\n".join([ + f"- {cat}: {desc}" for cat, desc in relation_categories.items() + ]) + + prompt = f"""请将以下关系分类到以下类别之一: + +类别定义: +{categories_desc} + +关系:{relation_text} + +请只回答类别名称:""" + + messages = [ + {"role": "system", "content": "你是一个法规关系分类专家。"}, + {"role": "user", "content": prompt} + ] + + try: + label = llm_client.chat(messages).strip() + # 验证标签 + if label not in relation_categories: + label = "未知" + + results.append({ + "relation": relation_text, + "label": label, + "confidence": 1.0 + }) + except Exception as e: + logger.warning(f"关系分类失败 {relation_text}: {e}") + results.append({ + "relation": relation_text, + "label": "未知", + "confidence": 0.0 + }) + + return results + + diff --git a/dofile/graphrag_pipeline/src/extraction/evaluator.py b/dofile/graphrag_pipeline/src/extraction/evaluator.py new file mode 100644 index 0000000..f62f2fa --- /dev/null +++ b/dofile/graphrag_pipeline/src/extraction/evaluator.py @@ -0,0 +1,322 @@ +""" +三元组评估模块 +使用LLM-as-a-Judge方法评估三元组质量 +实现成对排序和多维度评分(五维度:语义准确性、一致性、事实性、准确性、可理解性) +""" + +import json +import logging +import random +from typing import List, Dict, Any, Optional, Tuple + +from ..utils.llm_client import LLMClient, LLMProvider +from ..utils.config import Config + +logger = logging.getLogger(__name__) + +# 评估维度定义(对应论文Table 4) +EVALUATION_DIMENSIONS = { + "SEM": "语义准确性(Semantically): 三元组是否准确表示或涵盖了源文本的语义?", + "CON": "一致性(Consistency): 三元组提供的信息是否与源文本的逻辑一致?", + "FAC": "事实性(Factuality): 三元组是否保留了源文本的事实陈述?", + "ACC": "准确性(Accuracy): 三元组中是否有不准确、遗漏或错误的信息?", + "UND": "可理解性(Understandability): 三元组是否对非专业人员来说可理解和可解释?", +} + + +class TripletEvaluator: + """三元组评估器(LLM-as-a-Judge)""" + + def __init__( + self, + judge_models: Optional[List[Tuple[str, str]]] = None, + config: Optional[Config] = None, + ): + """ + 初始化三元组评估器 + + Args: + judge_models: 评估模型列表,格式[(provider, model), ...] + config: 配置对象 + """ + self.config = config or Config() + + # 默认使用GPT-4o作为评估者 + if judge_models is None: + judge_models = [ + ("openai", "gpt-4o"), + ] + + self.judge_clients = [] + for provider_str, model in judge_models: + provider = LLMProvider(provider_str) + self.judge_clients.append(LLMClient(provider, model, self.config)) + + def pairwise_ranking( + self, + triplet_group_a: List[Dict[str, str]], + triplet_group_b: List[Dict[str, str]], + source_text: str, + judge_index: int = 0, + ) -> str: + """ + 成对排序:比较两个三元组组,确定哪个更优 + + Args: + triplet_group_a: 三元组组A + triplet_group_b: 三元组组B + source_text: 源文本 + judge_index: 使用的评估者索引 + + Returns: + "A"或"B",表示哪个组更优 + """ + judge = self.judge_clients[judge_index] + + # 构建评估提示 + prompt = f"""请比较两个三元组组,确定哪个更准确地反映了源文本的语义。 + +源文本: +{source_text} + +三元组组A: +{json.dumps(triplet_group_a, ensure_ascii=False, indent=2)} + +三元组组B: +{json.dumps(triplet_group_b, ensure_ascii=False, indent=2)} + +请从以下维度进行比较: +1. 语义准确性:是否准确表示源文本的语义 +2. 完整性:是否涵盖了源文本中的关键信息 +3. 正确性:三元组之间的关系是否正确 + +请只回答"A"或"B",表示哪个组更优。如果质量相当,请回答"TIE"(平局)。""" + + messages = [ + {"role": "system", "content": "你是一个知识图谱质量评估专家,擅长评估三元组的质量。"}, + {"role": "user", "content": prompt} + ] + + try: + result = judge.chat(messages) + result = result.strip().upper() + if result in ["A", "B", "TIE"]: + return result + else: + # 尝试从结果中提取 + if "A" in result: + return "A" + elif "B" in result: + return "B" + else: + return "TIE" + except Exception as e: + logger.error(f"成对排序失败: {e}") + return "TIE" + + def multi_dimensional_scoring( + self, + triplets: List[Dict[str, str]], + source_text: str, + judge_index: int = 0, + ) -> Dict[str, List[int]]: + """ + 多维度评分:从五个维度对每个三元组进行评分(1-5分) + + Args: + triplets: 三元组列表 + source_text: 源文本 + judge_index: 使用的评估者索引 + + Returns: + 评分字典,格式:{维度: [分数列表]} + """ + judge = self.judge_clients[judge_index] + + # 为每个三元组构建评估 + scores = {dim: [] for dim in EVALUATION_DIMENSIONS.keys()} + + for triplet in triplets: + triplet_str = f"<{triplet.get('head', '')}, {triplet.get('relation', '')}, {triplet.get('tail', '')}>" + + prompt = f"""请从以下五个维度评估这个三元组的质量,每个维度给出1-5分的评分(5分最高,1分最低)。 + +源文本: +{source_text} + +三元组: +{triplet_str} + +评估维度: +{chr(10).join([f'{dim}: {desc}' for dim, desc in EVALUATION_DIMENSIONS.items()])} + +请以JSON格式输出评分: +{{ + "SEM": <分数1-5>, + "CON": <分数1-5>, + "FAC": <分数1-5>, + "ACC": <分数1-5>, + "UND": <分数1-5> +}}""" + + messages = [ + {"role": "system", "content": "你是一个知识图谱质量评估专家。"}, + {"role": "user", "content": prompt} + ] + + try: + result = judge.chat(messages) + # 尝试解析JSON + try: + triplet_scores = json.loads(result) + for dim in EVALUATION_DIMENSIONS.keys(): + score = triplet_scores.get(dim, 3) # 默认3分 + # 确保分数在1-5范围内 + score = max(1, min(5, int(score))) + scores[dim].append(score) + except json.JSONDecodeError: + # 如果解析失败,使用默认分数 + logger.warning(f"无法解析评分结果,使用默认分数: {result}") + for dim in EVALUATION_DIMENSIONS.keys(): + scores[dim].append(3) + except Exception as e: + logger.error(f"评分失败: {e}") + for dim in EVALUATION_DIMENSIONS.keys(): + scores[dim].append(3) + + return scores + + def evaluate_peer_groups( + self, + peer_groups: Dict[str, List[Dict[str, str]]], + source_text: str, + ) -> Dict[str, Any]: + """ + 评估多个模型生成的三元组组(Peer Examination) + + Args: + peer_groups: 多个模型的结果,格式:{"model_a": [triplets], "model_b": [triplets], ...} + source_text: 源文本 + + Returns: + 评估结果,包含排序和评分 + """ + model_names = list(peer_groups.keys()) + + # 随机打乱顺序以降低位置偏差 + shuffled_pairs = [] + for i, model_a in enumerate(model_names): + for model_b in model_names[i+1:]: + shuffled_pairs.append((model_a, model_b)) + random.shuffle(shuffled_pairs) + + # 成对排序 + rankings = {} + for model_a, model_b in shuffled_pairs: + # 每个评估者只评估其他模型的结果,避免自我增强偏差 + for judge_idx in range(len(self.judge_clients)): + result = self.pairwise_ranking( + peer_groups[model_a], + peer_groups[model_b], + source_text, + judge_idx + ) + key = f"{model_a}_vs_{model_b}" + if key not in rankings: + rankings[key] = [] + rankings[key].append(result) + + # 确定最佳组 + best_group = self._determine_best_group(rankings, model_names) + + # 对最佳组进行多维度评分 + if best_group: + scores = self.multi_dimensional_scoring( + peer_groups[best_group], + source_text + ) + else: + scores = {} + + return { + "rankings": rankings, + "best_group": best_group, + "scores": scores, + "selected_triplets": peer_groups.get(best_group, []) + } + + def _determine_best_group( + self, + rankings: Dict[str, List[str]], + model_names: List[str], + ) -> Optional[str]: + """ + 根据排序结果确定最佳组 + + Args: + rankings: 排序结果 + model_names: 模型名称列表 + + Returns: + 最佳模型名称 + """ + wins = {model: 0 for model in model_names} + + for pair_key, results in rankings.items(): + # 统计每个结果 + counts = {"A": 0, "B": 0, "TIE": 0} + for result in results: + if result in counts: + counts[result] += 1 + + # 解析模型名称 + parts = pair_key.split("_vs_") + if len(parts) == 2: + model_a, model_b = parts + if counts["A"] > counts["B"]: + wins[model_a] += 1 + elif counts["B"] > counts["A"]: + wins[model_b] += 1 + + # 返回获胜次数最多的模型 + if wins: + best_model = max(wins.items(), key=lambda x: x[1]) + return best_model[0] if best_model[1] > 0 else model_names[0] + return model_names[0] if model_names else None + + def filter_low_quality_triplets( + self, + triplets: List[Dict[str, str]], + scores: Dict[str, List[int]], + min_score: int = 2, + ) -> List[Dict[str, str]]: + """ + 根据评分筛选低质量三元组 + + Args: + triplets: 三元组列表 + scores: 评分字典 + min_score: 最低分数阈值 + + Returns: + 筛选后的三元组列表 + """ + filtered = [] + for i, triplet in enumerate(triplets): + # 计算平均分 + avg_score = 0 + count = 0 + for dim_scores in scores.values(): + if i < len(dim_scores): + avg_score += dim_scores[i] + count += 1 + if count > 0: + avg_score /= count + + # 只保留平均分 >= min_score的三元组 + if avg_score >= min_score: + filtered.append(triplet) + + return filtered + + diff --git a/dofile/graphrag_pipeline/src/extraction/ner.py b/dofile/graphrag_pipeline/src/extraction/ner.py new file mode 100644 index 0000000..2a99c05 --- /dev/null +++ b/dofile/graphrag_pipeline/src/extraction/ner.py @@ -0,0 +1,178 @@ +""" +命名实体识别模块 +使用Qwen-Max进行法规实体识别(两阶段对话) +""" + +import json +import logging +from typing import List, Dict, Any, Optional + +from ..prompts.ner_prompts import NERPromptBuilder +from ..utils.llm_client import LLMClient, LLMProvider +from ..utils.config import Config + +logger = logging.getLogger(__name__) + + +class NERExtractor: + """命名实体识别器""" + + def __init__( + self, + model: Optional[str] = None, + config: Optional[Config] = None, + ): + """ + 初始化NER提取器 + + Args: + model: 模型名称(如果为None,会根据配置自动选择) + config: 配置对象 + """ + self.config = config or Config() + + # 优先使用硅基流动,如果API密钥未配置则尝试Qwen + if self.config.SILICONFLOW_API_KEY: + provider = LLMProvider.SILICONFLOW + if model is None or model == "qwen-max": # 如果使用默认值,改为硅基流动模型 + # 使用指定的模型 + model = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B" + elif self.config.DASHSCOPE_API_KEY: + provider = LLMProvider.QWEN + if model is None: + model = "qwen-max" + else: + raise ValueError("请配置SILICONFLOW_API_KEY或DASHSCOPE_API_KEY") + + self.llm_client = LLMClient( + provider=provider, + model=model, + config=self.config + ) + self.prompt_builder = NERPromptBuilder() + + def extract( + self, + text: str, + use_verification: bool = True, + ) -> Dict[str, List[str]]: + """ + 从文本中提取实体(两阶段对话) + + Args: + text: 输入文本 + use_verification: 是否使用二次对话验证 + + Returns: + 实体字典,格式:{实体类型: [实体列表]} + """ + # 获取Prompt模板 + template = self.prompt_builder.build_template() + + # 构建第一阶段对话 + system_msg = template.task_description["system"] + user_msg = template.task_description["user"] + + # 构建完整的第一阶段消息 + full_user_msg = user_msg + + # 添加候选目标 + if template.candidate_targets: + full_user_msg += "\n\n" + template.build_candidate_targets_section() + + # 添加任务示例 + if template.task_examples: + full_user_msg += "\n\n" + template.build_task_examples_section() + + # 添加任务强调 + if template.task_emphasis: + full_user_msg += "\n\n" + template.build_task_emphasis_section() + + # 添加输入数据 + full_user_msg += f"\n\n输入数据:\n{text}" + + messages = [ + {"role": "system", "content": system_msg}, + {"role": "user", "content": full_user_msg} + ] + + # 第一阶段:初始提取 + logger.info("开始第一阶段实体识别...") + first_result = self.llm_client.chat(messages) + + # 解析第一阶段结果 + try: + first_entities = json.loads(first_result) + except json.JSONDecodeError: + logger.warning("第一阶段结果不是有效JSON,尝试修复...") + first_entities = self._try_parse_json(first_result) + + # 第二阶段:验证和优化 + if use_verification: + logger.info("开始第二阶段实体验证...") + verification_prompt = self.prompt_builder.build_verification_prompt() + + # 移除assistant消息,将第一阶段结果整合到user消息中(符合硅基流动API要求) + second_messages = [ + {"role": "system", "content": "你需要验证和优化之前的结果。请仔细检查结果的正确性和完整性。"}, + {"role": "user", "content": f"{verification_prompt}\n\n原始输入:\n{text}\n\n第一阶段结果:\n{first_result}\n\n请验证并优化这个结果,确保输出为有效的JSON格式。"} + ] + + second_result = self.llm_client.chat(second_messages) + + try: + final_entities = json.loads(second_result) + except json.JSONDecodeError: + logger.warning("第二阶段结果不是有效JSON,使用第一阶段结果") + final_entities = first_entities + else: + final_entities = first_entities + + return final_entities + + def _try_parse_json(self, text: str) -> Dict[str, List[str]]: + """尝试从文本中提取JSON(支持markdown代码块格式)""" + import re + + # 方法1: 尝试从markdown代码块中提取(```json ... ```) + json_block_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', text, re.DOTALL) + if json_block_match: + try: + json_str = json_block_match.group(1).strip() + return json.loads(json_str) + except json.JSONDecodeError: + pass + + # 方法2: 尝试找到完整的JSON对象(处理嵌套花括号,从后往前找最后一个完整的JSON) + # 从后往前找到最后一个完整的JSON对象 + brace_count = 0 + end_idx = -1 + for i in range(len(text) - 1, -1, -1): + char = text[i] + if char == '}': + if end_idx == -1: + end_idx = i + brace_count += 1 + elif char == '{': + brace_count -= 1 + if brace_count == 0 and end_idx != -1: + try: + json_str = text[i:end_idx+1] + return json.loads(json_str) + except json.JSONDecodeError: + end_idx = -1 + continue + + # 方法3: 简单的正则匹配(作为fallback) + json_match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', text, re.DOTALL) + if json_match: + try: + return json.loads(json_match.group()) + except json.JSONDecodeError: + pass + + # 如果都失败,返回空字典 + logger.error(f"无法解析JSON结果。原始输出前200字符: {text[:200]}") + return {} + + diff --git a/dofile/graphrag_pipeline/src/extraction/re.py b/dofile/graphrag_pipeline/src/extraction/re.py new file mode 100644 index 0000000..e6b1f14 --- /dev/null +++ b/dofile/graphrag_pipeline/src/extraction/re.py @@ -0,0 +1,236 @@ +""" +关系抽取模块 +使用多个LLM(GPT-4o, Doubao-pro, GLM-4)并行进行关系抽取 +""" + +import json +import logging +from typing import List, Dict, Any, Optional + +from ..prompts.re_prompts import REPromptBuilder +from ..utils.llm_client import LLMClient, LLMProvider +from ..utils.config import Config + +logger = logging.getLogger(__name__) + + +class REExtractor: + """关系抽取器""" + + def __init__( + self, + models: Optional[List[str]] = None, + config: Optional[Config] = None, + ): + """ + 初始化RE提取器 + + Args: + models: 使用的模型列表,默认使用多个模型并行 + config: 配置对象 + """ + self.config = config or Config() + + # 默认使用多个模型(优先使用硅基流动) + if models is None: + models = [] + # 优先使用硅基流动(使用两个模型) + if self.config.SILICONFLOW_API_KEY: + models.append(("siliconflow", "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B")) + models.append(("siliconflow", "Qwen/Qwen2.5-7B-Instruct")) + # 如果有其他API密钥,也添加 + if self.config.OPENAI_API_KEY: + models.append(("openai", "gpt-4o")) + if self.config.VOLCENGINE_ACCESS_KEY and self.config.VOLCENGINE_SECRET_KEY: + models.append(("doubao", "doubao-pro-32k")) + if self.config.ZHIPUAI_API_KEY: + models.append(("glm", "glm-4-airx")) + + # 如果都没有配置,至少使用硅基流动(即使没有密钥也会报错) + if not models: + models = [ + ("siliconflow", "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"), + ("siliconflow", "Qwen/Qwen2.5-7B-Instruct") + ] + + self.clients = [] + for provider_str, model in models: + try: + provider = LLMProvider(provider_str) + self.clients.append(LLMClient(provider, model, self.config)) + except Exception as e: + logger.warning(f"初始化{provider_str}客户端失败: {e},跳过该模型") + + self.prompt_builder = REPromptBuilder() + + def extract( + self, + sentence: str, + entities: List[Dict[str, str]], + syntax_info: Dict = None, + context: str = None, + use_verification: bool = True, + ) -> List[Dict[str, Any]]: + """ + 从句子中提取关系(多模型并行,支持二次对话验证) + + Args: + sentence: 输入句子 + entities: 已识别的实体列表,格式:[{"text": "实体文本", "type": "实体类型"}, ...] + syntax_info: 句法分析结果 + context: 上下文段落 + use_verification: 是否使用二次对话验证 + + Returns: + 结果列表,每个包含模型名称和三元组列表 + """ + # 格式化实体列表(用于Prompt) + formatted_entities = self._format_entities_for_prompt(entities) + + # 格式化句法信息 + formatted_syntax = self._format_syntax_info(syntax_info) if syntax_info else "无句法信息" + + # 构建Prompt模板 + template = self.prompt_builder.build_template( + entities=entities, + syntax_info=formatted_syntax, + context=context or "" + ) + + # 构建第一阶段消息 + system_msg = template.task_description["system"] + user_msg = template.task_description["user"] + + # 构建完整的用户消息 + input_data = f"""已识别实体: +{formatted_entities} + +句子文本: +{sentence} + +句法分析结果: +{formatted_syntax} + +上下文段落: +{context or "无上下文"}""" + + full_user_msg = user_msg + if template.candidate_targets: + full_user_msg += "\n\n" + template.build_candidate_targets_section() + if template.task_examples: + full_user_msg += "\n\n" + template.build_task_examples_section() + if template.task_emphasis: + full_user_msg += "\n\n" + template.build_task_emphasis_section() + full_user_msg += f"\n\n输入数据:\n{input_data}" + + messages = [ + {"role": "system", "content": system_msg}, + {"role": "user", "content": full_user_msg} + ] + + # 并行调用多个模型 + results = [] + for i, client in enumerate(self.clients): + try: + model_name = f"model_{i+1}" + logger.info(f"使用模型 {model_name} 进行关系抽取...") + + # 第一阶段:初始提取 + first_result = client.chat(messages) + + # 解析第一阶段结果 + first_triplets = self._parse_triplets(first_result) + + # 第二阶段:验证和优化 + if use_verification and first_triplets: + verification_prompt = self.prompt_builder.build_verification_prompt() + + # 移除assistant消息,将第一阶段结果整合到user消息中(符合硅基流动API要求) + second_messages = [ + {"role": "system", "content": "你需要验证和优化之前提取的三元组结果。请特别关注语义逻辑的正确性、遗漏关系的补全,以及利用上下文补全省略的成分。"}, + {"role": "user", "content": f"{verification_prompt}\n\n原始输入:\n{input_data}\n\n第一阶段结果:\n{first_result}\n\n请验证并优化这个结果,确保输出为有效的JSON格式。"} + ] + + second_result = client.chat(second_messages) + final_triplets = self._parse_triplets(second_result) + else: + final_triplets = first_triplets + + results.append({ + "model": model_name, + "triplets": final_triplets, + "first_triplets": first_triplets, + }) + + except Exception as e: + logger.error(f"模型 {i+1} 调用失败: {e}") + results.append({ + "model": f"model_{i+1}", + "triplets": [], + "error": str(e) + }) + + return results + + def _format_entities_for_prompt(self, entities: List[Dict[str, str]]) -> str: + """格式化实体列表用于Prompt""" + if not entities: + return "无识别实体" + + lines = [] + for entity in entities: + entity_type = entity.get("type", "未知类型") + entity_text = entity.get("text", "") + lines.append(f"- {entity_type}: {entity_text}") + + return "\n".join(lines) + + def _format_syntax_info(self, syntax_info: Dict) -> str: + """格式化句法信息""" + if not syntax_info: + return "无句法信息" + + formatted = [] + if "dependency" in syntax_info: + deps = syntax_info["dependency"] + formatted.append("依存关系:") + for dep in deps[:5]: # 只显示前5个 + formatted.append(f" {dep.get('word', '')} <-{dep.get('deprel', '')}- {dep.get('head', '')}") + + return "\n".join(formatted) + + def _parse_triplets(self, result: str) -> List[Dict[str, str]]: + """解析三元组结果""" + # 尝试解析JSON + try: + triplets = json.loads(result) + if isinstance(triplets, list): + # 验证三元组格式 + valid_triplets = [] + for t in triplets: + if isinstance(t, dict) and "head" in t and "relation" in t and "tail" in t: + valid_triplets.append({ + "head": str(t.get("head", "")), + "relation": str(t.get("relation", "")), + "tail": str(t.get("tail", "")) + }) + return valid_triplets + elif isinstance(triplets, dict): + # 可能是嵌套结构,尝试提取 + return [] + return [] + except json.JSONDecodeError: + # 尝试从文本中提取JSON + import re + json_match = re.search(r'\[.*\]', result, re.DOTALL) + if json_match: + try: + triplets = json.loads(json_match.group()) + if isinstance(triplets, list): + return triplets + except: + pass + logger.warning(f"无法解析三元组结果: {result[:100]}") + return [] + + diff --git a/dofile/graphrag_pipeline/src/kg_builder/__init__.py b/dofile/graphrag_pipeline/src/kg_builder/__init__.py new file mode 100644 index 0000000..4f7cc9b --- /dev/null +++ b/dofile/graphrag_pipeline/src/kg_builder/__init__.py @@ -0,0 +1,11 @@ +"""知识图谱构建模块(参考GraphRAG)""" + +from .indexer import GraphIndexer +from .graph import KnowledgeGraph + +__all__ = ["GraphIndexer", "KnowledgeGraph"] + + + + + diff --git a/dofile/graphrag_pipeline/src/kg_builder/graph.py b/dofile/graphrag_pipeline/src/kg_builder/graph.py new file mode 100644 index 0000000..d94379d --- /dev/null +++ b/dofile/graphrag_pipeline/src/kg_builder/graph.py @@ -0,0 +1,129 @@ +""" +知识图谱结构管理 +""" + +import networkx as nx +import logging +from typing import List, Dict, Any, Optional +import json + +logger = logging.getLogger(__name__) + + +class KnowledgeGraph: + """知识图谱类""" + + def __init__(self): + """初始化知识图谱""" + self.graph = nx.DiGraph() + + def add_triplet( + self, + head: str, + relation: str, + tail: str, + metadata: Optional[Dict] = None, + ): + """ + 添加三元组到图谱 + + Args: + head: 头实体 + relation: 关系 + tail: 尾实体 + metadata: 元数据 + """ + if not self.graph.has_node(head): + self.graph.add_node(head, type="entity") + if not self.graph.has_node(tail): + self.graph.add_node(tail, type="entity") + + self.graph.add_edge(head, tail, relation=relation, **(metadata or {})) + + def query_path(self, source: str, target: str, max_hops: int = 3) -> List: + """查询路径""" + try: + paths = list(nx.all_simple_paths( + self.graph, source, target, cutoff=max_hops + )) + return paths + except nx.NodeNotFound: + return [] + + def get_subgraph(self, nodes: List[str]) -> nx.DiGraph: + """获取子图""" + return self.graph.subgraph(nodes) + + def save(self, filepath: str, format: str = "json"): + """ + 保存图谱 + + Args: + filepath: 文件路径 + format: 保存格式("json"或"graphml") + """ + if format == "json": + data = { + "nodes": [ + {"id": str(node), **{k: v for k, v in attrs.items()}} + for node, attrs in self.graph.nodes(data=True) + ], + "edges": [ + { + "source": str(u), + "target": str(v), + **{k: v for k, v in attrs.items()} + } + for u, v, attrs in self.graph.edges(data=True) + ], + "metadata": self.graph.graph.get("metadata", {}) + } + with open(filepath, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + elif format == "graphml": + try: + nx.write_graphml(self.graph, filepath) + except Exception as e: + logger.error(f"保存GraphML失败: {e}") + + def load(self, filepath: str, format: str = "json"): + """ + 从文件加载图谱 + + Args: + filepath: 文件路径 + format: 文件格式("json"或"graphml") + """ + if format == "json": + with open(filepath, "r", encoding="utf-8") as f: + data = json.load(f) + + self.graph.clear() + + # 加载节点 + for node_data in data.get("nodes", []): + node_id = node_data.pop("id", "") + if node_id: + self.graph.add_node(node_id, **node_data) + + # 加载边 + for edge_data in data.get("edges", []): + source = edge_data.pop("source", "") + target = edge_data.pop("target", "") + if source and target: + self.graph.add_edge(source, target, **edge_data) + + # 加载元数据 + if "metadata" in data: + self.graph.graph["metadata"] = data["metadata"] + + logger.info(f"图谱加载完成:{self.graph.number_of_nodes()} 个节点,{self.graph.number_of_edges()} 条边") + + elif format == "graphml": + try: + self.graph = nx.read_graphml(filepath) + except Exception as e: + logger.error(f"加载GraphML失败: {e}") + raise + + diff --git a/dofile/graphrag_pipeline/src/kg_builder/indexer.py b/dofile/graphrag_pipeline/src/kg_builder/indexer.py new file mode 100644 index 0000000..fbc7622 --- /dev/null +++ b/dofile/graphrag_pipeline/src/kg_builder/indexer.py @@ -0,0 +1,361 @@ +""" +知识图谱索引器(参考GraphRAG) +实现索引阶段:文本切分、实体提取、图构建、社区分析、摘要生成 +""" + +import logging +from typing import List, Dict, Any, Optional +import networkx as nx + +from ..extraction.ner import NERExtractor +from ..extraction.re import REExtractor +from ..utils.config import Config + +logger = logging.getLogger(__name__) + + +class GraphIndexer: + """ + 知识图谱索引器 + 参考GraphRAG的实现思路 + """ + + def __init__(self, config: Optional[Config] = None): + """ + 初始化索引器 + + Args: + config: 配置对象 + """ + self.config = config or Config() + self.ner_extractor = NERExtractor(config=config) + self.re_extractor = REExtractor(config=config) + self.graph = nx.DiGraph() + + def index( + self, + textunits: List[Dict[str, Any]], + use_verification: bool = True, + ) -> nx.DiGraph: + """ + 构建知识图谱索引(参考GraphRAG流程) + + Args: + textunits: TextUnit列表 + use_verification: 是否使用二次对话验证 + + Returns: + 构建的知识图谱 + """ + logger.info(f"开始索引 {len(textunits)} 个TextUnit...") + + # 步骤1: 提取实体和关系(从每个TextUnit) + all_triplets = [] + all_entities = {} + + from ..preprocessing.text_processor import TextProcessor + text_processor = TextProcessor(self.config.HANLP_MODEL_PATH) + + for i, textunit in enumerate(textunits): + text = textunit.get("text", "") + textunit_id = textunit.get("id", f"textunit_{i}") + + if not text.strip(): + continue + + logger.info(f"处理TextUnit {i+1}/{len(textunits)}: {textunit_id}") + + # 1.1 NER:提取实体 + entities_result = self.ner_extractor.extract(text, use_verification=use_verification) + + # 转换实体格式为RE需要的格式 + entity_list = [] + for entity_type, entity_texts in entities_result.items(): + for entity_text in entity_texts: + if entity_text: # 过滤空字符串 + entity_list.append({ + "text": entity_text, + "type": entity_type + }) + + all_entities[textunit_id] = entity_list + + # 1.2 文本预处理:获取句法信息 + sentence_analysis = text_processor.process_sentence(text) + syntax_info = { + "dependency": sentence_analysis.get("dependency", []), + "constituency": sentence_analysis.get("constituency", {}) + } + + # 1.3 RE:提取关系(多模型并行) + if entity_list: + re_results = self.re_extractor.extract( + sentence=text, + entities=entity_list, + syntax_info=syntax_info, + context=textunit.get("context", ""), + use_verification=use_verification + ) + + # 收集所有模型的三元组 + for re_result in re_results: + triplets = re_result.get("triplets", []) + for triplet in triplets: + triplet["textunit_id"] = textunit_id + triplet["source"] = text + all_triplets.append(triplet) + + logger.info(f"提取完成:{len(all_triplets)} 个三元组,{sum(len(ents) for ents in all_entities.values())} 个实体") + + # 步骤2: 三元组评估和筛选 + evaluated_triplets = all_triplets + + # 注意:完整的三元组评估需要大量LLM调用,可能会很慢 + # 这里可以根据需要启用评估,或者使用简化的筛选策略 + if False and all_triplets: # 默认关闭,避免大量API调用 + from ..extraction.evaluator import TripletEvaluator + evaluator = TripletEvaluator(config=self.config) + + # 按TextUnit分组进行评估 + textunit_groups = {} + for triplet in all_triplets: + textunit_id = triplet.get("textunit_id") + source = triplet.get("source", "") + if textunit_id not in textunit_groups: + textunit_groups[textunit_id] = { + "triplets": [], + "source": source + } + textunit_groups[textunit_id]["triplets"].append(triplet) + + # 对每个TextUnit的三元组进行评估(可选) + evaluated_triplets = [] + for textunit_id, group_data in list(textunit_groups.items())[:5]: # 限制评估数量 + triplets = group_data["triplets"] + source = group_data["source"] + evaluated_triplets.extend(triplets) + + # 简单去重 + seen = set() + unique_triplets = [] + for triplet in evaluated_triplets: + key = (triplet.get("head", ""), triplet.get("relation", ""), triplet.get("tail", "")) + if key and key not in seen: + seen.add(key) + unique_triplets.append(triplet) + + evaluated_triplets = unique_triplets + + # 步骤3: 构建图 + self._build_graph(evaluated_triplets if all_triplets else []) + + # 步骤4: 社区层次结构(使用Leiden算法) + communities = self._detect_communities() + + # 步骤5: 生成社区摘要 + summaries = self._generate_community_summaries(communities) + + # 存储元数据 + self.graph.graph["metadata"] = { + "num_textunits": len(textunits), + "num_triplets": len(all_triplets), + "num_entities": sum(len(ents) for ents in all_entities.values()), + "num_communities": len(communities), + "communities": communities, + "summaries": summaries + } + + return self.graph + + def _build_graph(self, triplets: List[Dict[str, str]]): + """ + 构建图结构 + + Args: + triplets: 三元组列表 + """ + logger.info(f"构建图结构,包含 {len(triplets)} 个三元组...") + + self.graph.clear() + + for triplet in triplets: + head = triplet.get("head", "") + tail = triplet.get("tail", "") + relation = triplet.get("relation", "") + + if not head or not tail or not relation: + continue + + # 添加节点(如果不存在) + if not self.graph.has_node(head): + self.graph.add_node(head, type="entity") + if not self.graph.has_node(tail): + self.graph.add_node(tail, type="entity") + + # 添加边 + if self.graph.has_edge(head, tail): + # 如果边已存在,更新关系列表 + edge_data = self.graph[head][tail] + if "relations" not in edge_data: + edge_data["relations"] = [edge_data.get("relation", "")] + if relation not in edge_data["relations"]: + edge_data["relations"].append(relation) + else: + # 添加新边 + self.graph.add_edge( + head, + tail, + relation=relation, + textunit_id=triplet.get("textunit_id", ""), + source=triplet.get("source", "") + ) + + logger.info(f"图构建完成:{self.graph.number_of_nodes()} 个节点,{self.graph.number_of_edges()} 条边") + + def _detect_communities(self): + """ + 检测社区(使用Leiden算法) + + Returns: + 社区列表,每个社区包含节点列表 + """ + if self.graph.number_of_nodes() == 0: + return [] + + try: + import igraph as ig + logger.info("使用Leiden算法检测社区...") + + # 将NetworkX图转换为igraph图 + # 创建节点映射 + node_list = list(self.graph.nodes()) + node_to_idx = {node: i for i, node in enumerate(node_list)} + + # 创建边列表(使用节点索引) + edges = [(node_to_idx[u], node_to_idx[v]) for u, v in self.graph.edges()] + + # 创建无向图 + g_ig = ig.Graph(edges, directed=False) + + # 设置节点名称 + g_ig.vs["name"] = node_list + + # 运行Leiden算法 + communities_result = g_ig.community_leiden( + objective_function="modularity", + resolution_parameter=1.0 + ) + + # 转换为节点列表 + communities = [] + for community in communities_result: + nodes = [g_ig.vs[i]["name"] for i in community] + communities.append(nodes) + + logger.info(f"检测到 {len(communities)} 个社区") + return communities + + except ImportError: + logger.warning("igraph未安装,无法使用Leiden算法,使用简单连通分量代替") + # 使用NetworkX的连通分量作为替代 + if not self.graph.is_directed(): + components = list(nx.connected_components(self.graph.to_undirected())) + else: + # 对于有向图,转换为无向图 + undirected = self.graph.to_undirected() + components = list(nx.connected_components(undirected)) + return [list(comp) for comp in components] + except Exception as e: + logger.error(f"社区检测失败: {e}") + return [] + + def _generate_community_summaries(self, communities: List): + """ + 生成社区摘要(使用LLM) + + Args: + communities: 社区列表 + + Returns: + 社区摘要字典 + """ + if not communities: + return {} + + logger.info(f"为 {len(communities)} 个社区生成摘要...") + + summaries = {} + + # 使用第一个可用的LLM客户端生成摘要 + try: + from ..utils.llm_client import LLMClient, LLMProvider + + # 优先使用硅基流动 + if self.config.SILICONFLOW_API_KEY: + summarizer = LLMClient( + provider=LLMProvider.SILICONFLOW, + model="deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", + config=self.config + ) + elif self.config.DASHSCOPE_API_KEY: + summarizer = LLMClient( + provider=LLMProvider.QWEN, + model="qwen-max", + config=self.config + ) + else: + raise ValueError("请配置SILICONFLOW_API_KEY或DASHSCOPE_API_KEY") + + for i, community_nodes in enumerate(communities[:20]): # 限制前20个社区 + # 获取社区中的节点和关系信息 + subgraph = self.graph.subgraph(community_nodes) + + # 构建摘要提示 + nodes_info = "\n".join([f"- {node}" for node in list(community_nodes)[:10]]) + edges_info = "\n".join([ + f"- {u} --{self.graph[u][v].get('relation', '')}--> {v}" + for u, v in list(subgraph.edges())[:10] + ]) + + prompt = f"""请为以下知识图谱社区生成一个简洁的摘要(1-2句话),概括该社区的主要主题和内容。 + +社区节点: +{nodes_info} + +社区关系: +{edges_info} + +摘要:""" + + messages = [ + {"role": "system", "content": "你是一个知识图谱分析专家,擅长生成简洁准确的社区摘要。"}, + {"role": "user", "content": prompt} + ] + + try: + summary = summarizer.chat(messages) + summaries[f"community_{i+1}"] = { + "nodes": list(community_nodes), + "summary": summary.strip(), + "size": len(community_nodes) + } + except Exception as e: + logger.warning(f"社区 {i+1} 摘要生成失败: {e}") + summaries[f"community_{i+1}"] = { + "nodes": list(community_nodes), + "summary": "摘要生成失败", + "size": len(community_nodes) + } + except Exception as e: + logger.warning(f"摘要生成器初始化失败: {e}") + # 生成简单摘要 + for i, community_nodes in enumerate(communities): + summaries[f"community_{i+1}"] = { + "nodes": list(community_nodes), + "summary": f"包含 {len(community_nodes)} 个实体的社区", + "size": len(community_nodes) + } + + return summaries + + diff --git a/dofile/graphrag_pipeline/src/ontology/__init__.py b/dofile/graphrag_pipeline/src/ontology/__init__.py new file mode 100644 index 0000000..aa51ef4 --- /dev/null +++ b/dofile/graphrag_pipeline/src/ontology/__init__.py @@ -0,0 +1,18 @@ +"""本体模型定义模块""" + +from .entities import EntityType, get_entity_definitions +from .relations import RelationType, get_relation_definitions +from .schema import OntologySchema + +__all__ = [ + "EntityType", + "get_entity_definitions", + "RelationType", + "get_relation_definitions", + "OntologySchema", +] + + + + + diff --git a/dofile/graphrag_pipeline/src/ontology/entities.py b/dofile/graphrag_pipeline/src/ontology/entities.py new file mode 100644 index 0000000..e6de917 --- /dev/null +++ b/dofile/graphrag_pipeline/src/ontology/entities.py @@ -0,0 +1,82 @@ +""" +实体类型定义 +对应论文中的9种实体类型 +""" + +from enum import Enum +from typing import Dict + + +class EntityType(str, Enum): + """实体类型枚举""" + + # 法规条文 + LEGAL_PROVISION = "法规条文" + + # 目标要素 + TARGET_ELEMENT = "目标要素" + + # 原则要素 + PRINCIPLE_ELEMENT = "原则要素" + + # 空间要素 + SPATIAL_ELEMENT = "空间要素" + + # 功能要素 + FUNCTIONAL_ELEMENT = "功能要素" + + # 环境要素 + ENVIRONMENTAL_ELEMENT = "环境要素" + + # 管控要素 + CONTROL_ELEMENT = "管控要素" + + # 行政区域 + ADMINISTRATIVE_REGION = "行政区域" + + # 时间要素 + TEMPORAL_ELEMENT = "时间要素" + + +def get_entity_definitions() -> Dict[str, str]: + """ + 获取实体类型定义(对应论文Table 1) + + Returns: + 实体类型名称到定义的映射 + """ + return { + EntityType.LEGAL_PROVISION.value: "法规文档中的具体条文,包括章、节、条、款、项等不同层级", + EntityType.TARGET_ELEMENT.value: "法规中明确表达的战略目标、发展愿景、总体目标等宏观性表述", + EntityType.PRINCIPLE_ELEMENT.value: "法规遵循的基本准则、指导方针、基本原则等", + EntityType.SPATIAL_ELEMENT.value: "法规涉及的地理空间实体,包括行政区划、功能区、保护区域等", + EntityType.FUNCTIONAL_ELEMENT.value: "法规涉及的功能性要素,如土地利用类型、产业类型、基础设施等", + EntityType.ENVIRONMENTAL_ELEMENT.value: "法规涉及的环境保护相关要素,如生态保护区、环境质量标准等", + EntityType.CONTROL_ELEMENT.value: "法规对各类要素的管控要求,包括禁止、限制、引导等管控措施", + EntityType.ADMINISTRATIVE_REGION.value: "法规适用的行政区划,包括省、市、县、乡等不同层级", + EntityType.TEMPORAL_ELEMENT.value: "法规涉及的时间概念,如规划期限、实施阶段、有效期等", + } + + +def get_four_element_types() -> Dict[str, str]: + """ + 获取四要素类型(目标、原则、要素、管控) + + Returns: + 四要素类型定义 + """ + return { + "目标": EntityType.TARGET_ELEMENT.value, + "原则": EntityType.PRINCIPLE_ELEMENT.value, + "要素": [ + EntityType.SPATIAL_ELEMENT.value, + EntityType.FUNCTIONAL_ELEMENT.value, + EntityType.ENVIRONMENTAL_ELEMENT.value, + ], + "管控": EntityType.CONTROL_ELEMENT.value, + } + + + + + diff --git a/dofile/graphrag_pipeline/src/ontology/relations.py b/dofile/graphrag_pipeline/src/ontology/relations.py new file mode 100644 index 0000000..7903b81 --- /dev/null +++ b/dofile/graphrag_pipeline/src/ontology/relations.py @@ -0,0 +1,73 @@ +""" +关系类型定义 +对应论文中的8种关系类型 +""" + +from enum import Enum +from typing import Dict + + +class RelationType(str, Enum): + """关系类型枚举""" + + # 引用关系 + CITES = "引用" + + # 层级关系 + HIERARCHY = "层级" + + # 包含关系 + CONTAINS = "包含" + + # 管控关系 + CONTROLS = "管控" + + # 遵循关系 + FOLLOWS = "遵循" + + # 涉及关系 + INVOLVES = "涉及" + + # 适用关系 + APPLIES_TO = "适用" + + # 演化关系 + EVOLVES = "演化" + + +def get_relation_definitions() -> Dict[str, str]: + """ + 获取关系类型定义(对应论文Table 2) + + Returns: + 关系类型名称到定义的映射 + """ + return { + RelationType.CITES.value: "一条法规条文明确引用另一条法规条文", + RelationType.HIERARCHY.value: "上下级法规之间的隶属关系或法规条文内部的层级关系", + RelationType.CONTAINS.value: "一个实体在逻辑上包含另一个实体", + RelationType.CONTROLS.value: "管控要素对目标要素、原则要素或其他要素的约束关系", + RelationType.FOLLOWS.value: "要素遵循某种原则或目标的关系", + RelationType.INVOLVES.value: "法规条文涉及某个空间、功能或环境要素", + RelationType.APPLIES_TO.value: "法规适用于某个行政区域", + RelationType.EVOLVES.value: "法规修订前后的变化关系或时间序列上的演化关系", + } + + +def get_hierarchical_relations() -> Dict[str, str]: + """ + 获取层级化关系(目标—原则—要素—管控) + + Returns: + 层级关系定义 + """ + return { + "目标→原则": RelationType.FOLLOWS.value, + "原则→要素": RelationType.FOLLOWS.value, + "要素→管控": RelationType.CONTROLS.value, + } + + + + + diff --git a/dofile/graphrag_pipeline/src/ontology/schema.py b/dofile/graphrag_pipeline/src/ontology/schema.py new file mode 100644 index 0000000..60f5adf --- /dev/null +++ b/dofile/graphrag_pipeline/src/ontology/schema.py @@ -0,0 +1,126 @@ +""" +本体模式定义 +实现三维本体结构:法规层级—要素类型—关联关系 +""" + +from typing import Dict, List, Optional +from .entities import EntityType, get_entity_definitions, get_four_element_types +from .relations import RelationType, get_relation_definitions, get_hierarchical_relations + + +class OntologySchema: + """ + 法规知识本体模式 + 三维结构:法规层级—要素类型—关联关系 + """ + + # 法规层级 + LEGAL_LEVELS = [ + "法律", + "行政法规", + "部门规章", + "党中央国务院文件", + "国家主管部门文件", + "技术标准", + ] + + def __init__(self): + """初始化本体模式""" + self.entity_types = get_entity_definitions() + self.relation_types = get_relation_definitions() + self.four_elements = get_four_element_types() + self.hierarchical_relations = get_hierarchical_relations() + + def get_entity_type(self, entity_name: str) -> Optional[str]: + """ + 根据实体名称获取实体类型 + + Args: + entity_name: 实体名称 + + Returns: + 实体类型,如果未找到返回None + """ + for entity_type, definition in self.entity_types.items(): + if entity_name in definition: + return entity_type + return None + + def get_relation_type(self, relation_name: str) -> Optional[str]: + """ + 根据关系名称获取关系类型 + + Args: + relation_name: 关系名称 + + Returns: + 关系类型,如果未找到返回None + """ + return self.relation_types.get(relation_name) + + def is_valid_entity_relation_pair( + self, + head_entity_type: str, + relation_type: str, + tail_entity_type: str + ) -> bool: + """ + 检查实体-关系-实体三元组是否有效 + + Args: + head_entity_type: 头实体类型 + relation_type: 关系类型 + tail_entity_type: 尾实体类型 + + Returns: + 是否为有效的三元组 + """ + # 检查层级关系:目标→原则→要素→管控 + if relation_type == RelationType.FOLLOWS.value: + if (head_entity_type == EntityType.TARGET_ELEMENT.value and + tail_entity_type == EntityType.PRINCIPLE_ELEMENT.value): + return True + if (head_entity_type == EntityType.PRINCIPLE_ELEMENT.value and + tail_entity_type in [ + EntityType.SPATIAL_ELEMENT.value, + EntityType.FUNCTIONAL_ELEMENT.value, + EntityType.ENVIRONMENTAL_ELEMENT.value, + ]): + return True + + if relation_type == RelationType.CONTROLS.value: + if (head_entity_type == EntityType.CONTROL_ELEMENT.value and + tail_entity_type in [ + EntityType.TARGET_ELEMENT.value, + EntityType.PRINCIPLE_ELEMENT.value, + EntityType.SPATIAL_ELEMENT.value, + EntityType.FUNCTIONAL_ELEMENT.value, + EntityType.ENVIRONMENTAL_ELEMENT.value, + ]): + return True + + # 其他关系类型的基本检查 + return (head_entity_type in self.entity_types and + tail_entity_type in self.entity_types and + relation_type in self.relation_types) + + def get_schema_summary(self) -> Dict: + """ + 获取本体模式摘要 + + Returns: + 包含实体类型数、关系类型数、法规层级数等的字典 + """ + return { + "legal_levels": len(self.LEGAL_LEVELS), + "entity_types": len(self.entity_types), + "relation_types": len(self.relation_types), + "four_elements": list(self.four_elements.keys()), + "entity_definitions": self.entity_types, + "relation_definitions": self.relation_types, + } + + + + + diff --git a/dofile/graphrag_pipeline/src/preprocessing/__init__.py b/dofile/graphrag_pipeline/src/preprocessing/__init__.py new file mode 100644 index 0000000..1985665 --- /dev/null +++ b/dofile/graphrag_pipeline/src/preprocessing/__init__.py @@ -0,0 +1,11 @@ +"""文本预处理模块""" + +from .text_processor import TextProcessor +from .document_parser import DocumentParser + +__all__ = ["TextProcessor", "DocumentParser"] + + + + + diff --git a/dofile/graphrag_pipeline/src/preprocessing/document_parser.py b/dofile/graphrag_pipeline/src/preprocessing/document_parser.py new file mode 100644 index 0000000..aa8f43b --- /dev/null +++ b/dofile/graphrag_pipeline/src/preprocessing/document_parser.py @@ -0,0 +1,242 @@ +""" +法规文档解析模块 +解析Word文档(.docx)格式的法规文件,提取文本并进行切分 +""" + +import os +import logging +from pathlib import Path +from typing import List, Dict, Optional +from docx import Document +from docx.shared import Inches + +logger = logging.getLogger(__name__) + + +class DocumentParser: + """法规文档解析器""" + + def __init__(self): + """初始化文档解析器""" + pass + + def parse_docx(self, file_path: str) -> Dict: + """ + 解析.docx文档 + + Args: + file_path: 文档路径 + + Returns: + 包含文档信息的字典: + - title: 文档标题 + - text: 完整文本 + - paragraphs: 段落列表 + - structure: 文档结构(章节信息) + """ + try: + doc = Document(file_path) + + # 提取标题(通常在第一段) + title = "" + if doc.paragraphs: + title = doc.paragraphs[0].text.strip() + + # 提取所有段落 + paragraphs = [] + structure = [] + current_chapter = None + current_section = None + + for para in doc.paragraphs: + text = para.text.strip() + if not text: + continue + + # 识别章节结构 + if self._is_chapter_heading(text): + current_chapter = text + current_section = None + structure.append({ + 'type': 'chapter', + 'title': text, + 'level': self._get_heading_level(para) + }) + elif self._is_section_heading(text): + current_section = text + structure.append({ + 'type': 'section', + 'title': text, + 'chapter': current_chapter, + 'level': self._get_heading_level(para) + }) + + paragraphs.append({ + 'text': text, + 'chapter': current_chapter, + 'section': current_section, + 'style': para.style.name if para.style else None + }) + + # 合并所有文本 + full_text = '\n'.join([p['text'] for p in paragraphs]) + + return { + 'file_path': file_path, + 'title': title, + 'text': full_text, + 'paragraphs': paragraphs, + 'structure': structure + } + except Exception as e: + logger.error(f"解析文档失败 {file_path}: {e}") + raise + + def _is_chapter_heading(self, text: str) -> bool: + """判断是否为章节标题""" + # 匹配模式:第一章、第一章、第一编等 + patterns = [ + r'^第[一二三四五六七八九十]+章', + r'^第[一二三四五六七八九十]+编', + r'^第\d+章', + r'^第\d+编' + ] + import re + for pattern in patterns: + if re.match(pattern, text): + return True + return False + + def _is_section_heading(self, text: str) -> bool: + """判断是否为节标题""" + patterns = [ + r'^第[一二三四五六七八九十]+节', + r'^第\d+节' + ] + import re + for pattern in patterns: + if re.match(pattern, text): + return True + return False + + def _get_heading_level(self, para) -> int: + """获取标题层级""" + style_name = para.style.name if para.style else "" + if 'Heading 1' in style_name or '标题 1' in style_name: + return 1 + elif 'Heading 2' in style_name or '标题 2' in style_name: + return 2 + elif 'Heading 3' in style_name or '标题 3' in style_name: + return 3 + else: + return 0 + + def split_into_textunits( + self, + doc_data: Dict, + max_length: int = 500 + ) -> List[Dict]: + """ + 将文档切分为TextUnit(参考GraphRAG) + + Args: + doc_data: 解析后的文档数据 + max_length: 每个TextUnit的最大字符数 + + Returns: + TextUnit列表,每个包含: + - text: 文本内容 + - chapter: 所属章节 + - section: 所属节 + - paragraph_index: 段落索引 + - start_char: 起始字符位置 + - end_char: 结束字符位置 + """ + textunits = [] + current_text = "" + current_unit = { + 'chapter': None, + 'section': None, + 'paragraph_index': None, + 'start_char': 0 + } + char_offset = 0 + + for i, para in enumerate(doc_data['paragraphs']): + para_text = para['text'] + + # 如果当前累积文本加上新段落超过限制,保存当前TextUnit + if len(current_text) + len(para_text) > max_length and current_text: + current_unit.update({ + 'text': current_text.strip(), + 'end_char': char_offset, + 'file_path': doc_data['file_path'] + }) + textunits.append(current_unit.copy()) + + # 开始新的TextUnit + current_text = para_text + current_unit = { + 'chapter': para.get('chapter'), + 'section': para.get('section'), + 'paragraph_index': i, + 'start_char': char_offset + } + else: + # 累积文本 + if current_text: + current_text += "\n" + para_text + else: + current_text = para_text + current_unit.update({ + 'chapter': para.get('chapter'), + 'section': para.get('section'), + 'paragraph_index': i, + 'start_char': char_offset + }) + + char_offset += len(para_text) + 1 # +1 for newline + + # 保存最后一个TextUnit + if current_text: + current_unit.update({ + 'text': current_text.strip(), + 'end_char': char_offset, + 'file_path': doc_data['file_path'] + }) + textunits.append(current_unit) + + return textunits + + def parse_directory( + self, + directory: str, + file_pattern: str = "*.docx" + ) -> List[Dict]: + """ + 解析目录中的所有文档 + + Args: + directory: 目录路径 + file_pattern: 文件匹配模式 + + Returns: + 所有文档的解析结果列表 + """ + docs = [] + dir_path = Path(directory) + + for file_path in dir_path.rglob(file_pattern): + try: + doc_data = self.parse_docx(str(file_path)) + docs.append(doc_data) + logger.info(f"成功解析文档: {file_path}") + except Exception as e: + logger.warning(f"跳过文档 {file_path}: {e}") + + return docs + + + + + diff --git a/dofile/graphrag_pipeline/src/preprocessing/text_processor.py b/dofile/graphrag_pipeline/src/preprocessing/text_processor.py new file mode 100644 index 0000000..1654b41 --- /dev/null +++ b/dofile/graphrag_pipeline/src/preprocessing/text_processor.py @@ -0,0 +1,271 @@ +""" +HanLP文本处理模块 +处理中文法规文本的分词、依存句法分析和句法成分分析 +""" + +from typing import List, Dict, Optional, Tuple +import logging + +logger = logging.getLogger(__name__) + +# 延迟导入hanlp,避免在导入时失败 +try: + import hanlp + HANLP_AVAILABLE = True +except ImportError: + HANLP_AVAILABLE = False + logger.warning("HanLP未安装,将使用简化处理") + + +class TextProcessor: + """文本处理器,使用HanLP进行中文文本处理""" + + def __init__(self, model_path: Optional[str] = None): + """ + 初始化文本处理器 + + Args: + model_path: HanLP模型路径,如果为None则使用默认模型 + """ + self.hanlp = None + + if not HANLP_AVAILABLE: + logger.warning("HanLP未安装,将使用简化处理。安装命令: pip install 'hanlp[full]' -U") + return + + try: + if model_path: + logger.info(f"加载HanLP模型: {model_path}") + self.hanlp = hanlp.load(model_path) + logger.info("HanLP模型加载成功") + else: + # 尝试加载HanLP默认模型(较小的中文模型) + logger.info("尝试加载HanLP默认模型...") + try: + # 方法1: 尝试使用预定义的小型中文模型 + import hanlp.pretrained + model_name = hanlp.pretrained.mtl.CLOSE_TOK_POS_NER_SRL_DEP_SDP_CON_ELECTRA_SMALL_ZH + logger.info(f"使用模型: {model_name}") + self.hanlp = hanlp.load(model_name) + logger.info("HanLP默认模型加载成功") + except (AttributeError, ImportError) as e1: + logger.debug(f"方法1失败: {e1},尝试方法2...") + try: + # 方法2: 使用pipeline方式 + self.hanlp = hanlp.pipeline('tok/coarse') + logger.info("HanLP pipeline加载成功") + except Exception as e2: + logger.debug(f"方法2失败: {e2},尝试方法3...") + # 方法3: 尝试最简单的加载方式 + self.hanlp = hanlp.load() + logger.info("HanLP默认加载成功") + except Exception as e: + logger.warning(f"HanLP模型加载失败: {e},将使用简化处理(仅文档解析和基本分词)") + logger.info("提示: 首次加载会自动下载模型,可能需要一些时间") + logger.info("如需使用完整功能,请安装完整版: pip install 'hanlp[full]' -U") + self.hanlp = None + + def tokenize(self, text: str, coarse: bool = True) -> List[str]: + """ + 文本分词 + + Args: + text: 输入文本 + coarse: 是否使用粗粒度分词(默认True,保持术语完整性) + + Returns: + 分词结果列表 + """ + if self.hanlp is None: + # 简化处理:按标点符号和空格简单切分 + import re + tokens = re.split(r'[,。;:!?、\s]+', text) + return [t.strip() for t in tokens if t.strip()] + + if coarse: + # 使用粗粒度分词器,保持术语完整性(如"开敞空间") + result = self.hanlp(text, tasks='tok/coarse') + else: + result = self.hanlp(text, tasks='tok/fine') + return result.get('tok', []) + + def dependency_parse(self, text: str) -> List[Dict]: + """ + 依存句法分析 + + Args: + text: 输入文本 + + Returns: + 依存关系列表,每个元素包含:head(依存头)、deprel(依存关系类型) + """ + if self.hanlp is None: + # 返回空列表,简化处理 + return [] + + # 分别获取分词和依存句法,因为HanLP MTL模型不支持同时指定多个任务 + # 先获取分词结果 + tok_result = self.hanlp(text, tasks='tok/coarse') + tokens = tok_result.get('tok/coarse', []) + + # 再获取依存句法分析(会自动使用已分词的token) + dep_result = self.hanlp(text, tasks='dep') + dependencies = dep_result.get('dep', []) + + parsed = [] + for i, dep in enumerate(dependencies): + # HanLP返回的依赖关系格式:(head_index, deprel) + word = tokens[i] if i < len(tokens) else '' + + if isinstance(dep, tuple): + # tuple格式:(head_index, deprel) + head_idx = dep[0] if len(dep) > 0 else -1 + deprel = dep[1] if len(dep) > 1 else '' + head_word = tokens[head_idx] if 0 <= head_idx < len(tokens) else '' if head_idx >= 0 else 'ROOT' + + parsed.append({ + 'word': word, + 'head': head_idx, + 'head_word': head_word, + 'deprel': deprel + }) + elif hasattr(dep, 'form'): + # 如果是对象格式,有form属性 + parsed.append({ + 'word': dep.form, + 'head': dep.head, + 'head_word': tokens[dep.head] if 0 <= dep.head < len(tokens) else '' if dep.head >= 0 else 'ROOT', + 'deprel': dep.deprel + }) + elif isinstance(dep, dict): + # 如果是字典格式 + head_idx = dep.get('head', -1) + parsed.append({ + 'word': dep.get('form', dep.get('word', word)), + 'head': head_idx, + 'head_word': tokens[head_idx] if 0 <= head_idx < len(tokens) else '' if head_idx >= 0 else 'ROOT', + 'deprel': dep.get('deprel', '') + }) + else: + # 其他格式 + logger.warning(f"未知的依存关系格式: {type(dep)}, {dep}") + parsed.append({ + 'word': word, + 'head': -1, + 'head_word': 'ROOT', + 'deprel': '' + }) + return parsed + + def constituency_parse(self, text: str) -> Dict: + """ + 句法成分分析 + + Args: + text: 输入文本 + + Returns: + 句法成分树(嵌套字典结构) + """ + if self.hanlp is None: + # 返回空字典,简化处理 + return {} + + result = self.hanlp(text, tasks='con') + return result.get('con', {}) + + def pos_tag(self, text: str) -> List[Tuple[str, str]]: + """ + 词性标注 + + Args: + text: 输入文本 + + Returns: + (词, 词性)元组列表 + """ + if self.hanlp is None: + # 简化处理:返回分词结果和默认词性 + tokens = self.tokenize(text) + return [(token, 'UNKNOWN') for token in tokens] + + result = self.hanlp(text, tasks='pos') + tokens = result.get('tok', []) + pos_tags = result.get('pos', []) + return list(zip(tokens, pos_tags)) + + def process_sentence( + self, + sentence: str, + include_context: bool = True + ) -> Dict: + """ + 处理单个句子,返回所有分析结果 + + Args: + sentence: 输入句子 + include_context: 是否包含上下文信息 + + Returns: + 包含分词、依存、句法成分等信息的字典 + """ + result = { + 'text': sentence, + 'tokens': self.tokenize(sentence, coarse=True), + 'dependency': self.dependency_parse(sentence), + 'constituency': self.constituency_parse(sentence), + 'pos_tags': self.pos_tag(sentence) + } + return result + + def has_omitted_subject(self, dependencies: List[Dict]) -> bool: + """ + 检查句子是否省略了主语 + + Args: + dependencies: 依存关系列表 + + Returns: + 是否存在省略的主语 + """ + # 检查是否有主语相关的依存关系(nsubj等) + subject_deprels = ['nsubj', 'nsubjpass', 'csubj', 'csubjpass'] + has_subject = any( + dep['deprel'] in subject_deprels + for dep in dependencies + ) + return not has_subject + + def extract_coreference_candidates( + self, + text: str, + pronouns: List[str] = None + ) -> List[Dict]: + """ + 提取共指消解候选 + + Args: + text: 输入文本 + pronouns: 代词列表,默认为["其", "该", "这", "那"] + + Returns: + 共指候选列表 + """ + if pronouns is None: + pronouns = ["其", "该", "这", "那"] + + candidates = [] + tokens = self.tokenize(text) + pos_tags = self.pos_tag(text) + + for i, (token, pos) in enumerate(zip(tokens, pos_tags)): + if token in pronouns or pos[1] == 'PN': # PN表示代词 + candidates.append({ + 'pronoun': token, + 'position': i, + 'pos': pos[1] + }) + + return candidates + + diff --git a/dofile/graphrag_pipeline/src/prompts/__init__.py b/dofile/graphrag_pipeline/src/prompts/__init__.py new file mode 100644 index 0000000..f4029e6 --- /dev/null +++ b/dofile/graphrag_pipeline/src/prompts/__init__.py @@ -0,0 +1,17 @@ +"""Prompt模板模块""" + +from .template import PromptTemplate, PromptModule +from .ner_prompts import NERPromptBuilder +from .re_prompts import REPromptBuilder + +__all__ = [ + "PromptTemplate", + "PromptModule", + "NERPromptBuilder", + "REPromptBuilder", +] + + + + + diff --git a/dofile/graphrag_pipeline/src/prompts/ner_prompts.py b/dofile/graphrag_pipeline/src/prompts/ner_prompts.py new file mode 100644 index 0000000..140b610 --- /dev/null +++ b/dofile/graphrag_pipeline/src/prompts/ner_prompts.py @@ -0,0 +1,95 @@ +""" +NER (命名实体识别) Prompt构建器 +""" + +from typing import List, Dict, Any +from .template import PromptTemplate +from ..ontology.entities import EntityType, get_entity_definitions + + +class NERPromptBuilder: + """NER Prompt构建器""" + + def __init__(self): + """初始化NER Prompt构建器""" + self.entity_definitions = get_entity_definitions() + + def build_template(self) -> PromptTemplate: + """ + 构建NER Prompt模板 + + Returns: + PromptTemplate对象 + """ + # 任务描述 + system_message = "你是一个经验丰富的国土空间规划法规分析专家,擅长从法规文本中识别各类知识实体。" + user_message = """请从给定的法规文本中识别并分类所有实体。 + +任务要求: +1. 仔细阅读输入的法规文本 +2. 识别所有属于预定义实体类型的实体 +3. 对每个实体进行正确分类 +4. 确保识别结果的完整性和准确性 + +输出格式要求:请以JSON格式输出,格式如下: +{ + "法规条文": ["实体1", "实体2", ...], + "目标要素": ["实体1", "实体2", ...], + "原则要素": ["实体1", "实体2", ...], + ... +} + +每个实体类型对应一个列表,包含该类型的所有实体。如果某个类型没有实体,请使用空列表[]。""" + + # 候选目标 + candidate_targets = self.entity_definitions + + # 任务示例 + task_examples = [ + { + "input": "优先保障先进制造业、战略性新兴产业、都市产业等发展空间,促进价值创新园区建设。", + "output": '{"法规条文": [], "目标要素": [], "原则要素": [], "空间要素": [], "功能要素": ["先进制造业", "战略性新兴产业", "都市产业", "价值创新园区"], "环境要素": [], "管控要素": ["优先保障", "促进"], "行政区域": [], "时间要素": []}' + }, + { + "input": "广州市应当坚持生态优先、绿色发展,建设宜居花城、活力全球城市。", + "output": '{"法规条文": [], "目标要素": ["宜居花城", "活力全球城市"], "原则要素": ["生态优先", "绿色发展"], "空间要素": [], "功能要素": [], "环境要素": [], "管控要素": ["坚持", "建设"], "行政区域": ["广州市"], "时间要素": []}' + } + ] + + # 任务强调 + task_emphasis = """严格按要求: +1. **仅输出JSON格式结果**,不要添加任何解释性文字、markdown代码块标记或其他内容 +2. 直接输出JSON对象,不要使用```json```等markdown标记 +3. 不要添加"好的"、"让我分析"等开场白或解释 +4. 确保所有实体都被正确识别和分类 +5. 保持实体文本的完整性,不要截断 + +重要:请直接输出JSON,格式如下(不要添加任何其他文字): +{"法规条文": [], "目标要素": [], "原则要素": [], ...}""" + + return PromptTemplate( + task_description={ + "system": system_message, + "user": user_message + }, + candidate_targets=candidate_targets, + task_examples=task_examples, + task_emphasis=task_emphasis, + ) + + def build_verification_prompt(self) -> str: + """ + 构建验证提示(用于二次对话) + + Returns: + 验证提示文本 + """ + return """你之前提取的地理规划知识实体可能不完整或不准确。请结合之前的提取内容和经验,重新提取并验证结果。 + +请特别关注: +1. 是否有遗漏的实体 +2. 实体的分类是否正确 +3. 实体文本是否完整""" + + + diff --git a/dofile/graphrag_pipeline/src/prompts/re_prompts.py b/dofile/graphrag_pipeline/src/prompts/re_prompts.py new file mode 100644 index 0000000..dadc4d1 --- /dev/null +++ b/dofile/graphrag_pipeline/src/prompts/re_prompts.py @@ -0,0 +1,129 @@ +""" +RE (关系抽取) Prompt构建器 +""" + +from typing import List, Dict, Any +from .template import PromptTemplate +from ..ontology.relations import RelationType, get_relation_definitions + + +class REPromptBuilder: + """RE Prompt构建器""" + + def __init__(self): + """初始化RE Prompt构建器""" + self.relation_definitions = get_relation_definitions() + + def build_template( + self, + entities: List[Dict[str, str]], + syntax_info: Dict = None, + context: str = None + ) -> PromptTemplate: + """ + 构建RE Prompt模板 + + Args: + entities: 已识别的实体列表 + syntax_info: 句法分析结果 + context: 上下文段落信息 + + Returns: + PromptTemplate对象 + """ + # 任务描述 + system_message = "你是一个自然语言处理专家,擅长在城市规划领域处理关系抽取任务。" + + user_message = """请从给定的法规文本中识别实体之间的关系,并构建知识三元组。 + +任务要求: +1. 基于已识别的实体列表,找出实体之间的语义关系 +2. 对于省略主语的句子,需要利用上下文信息补全 +3. 构建形式的三元组 +4. 确保三元组的语义逻辑正确 + +输出格式要求:请以JSON格式输出三元组列表,格式如下: +[ + {"head": "头实体", "relation": "关系类型", "tail": "尾实体"}, + ... +] + +如果句子中无法提取完整的三元组,缺失的部分请标记为null。""" + + # 候选目标(关系类型) + candidate_targets = self.relation_definitions + + # 构建输入数据说明 + input_data_desc = f"""已识别实体: +{self._format_entities(entities)} + +句子文本: +[待填充] + +句法分析结果: +{syntax_info or "待填充"} + +上下文段落: +{context or "待填充"}""" + + # 任务示例 + task_examples = [ + { + "input": "实体:['广州市', '生态优先'],句子:'广州市应当坚持生态优先、绿色发展。'", + "output": '[{"head": "广州市", "relation": "遵循", "tail": "生态优先"}]' + } + ] + + # 任务强调 + task_emphasis = """严格按要求: +1. 仅输出JSON格式的三元组数组,不要添加任何解释 +2. 确保三元组中的实体必须是已识别实体列表中的实体 +3. 关系类型必须是预定义关系类型之一 +4. 对于省略成分,利用上下文补全""" + + template = PromptTemplate( + task_description={ + "system": system_message, + "user": user_message + }, + candidate_targets=candidate_targets, + task_examples=task_examples, + task_emphasis=task_emphasis, + ) + + # 存储额外信息 + template.input_data_description = input_data_desc + return template + + def _format_entities(self, entities: List[Dict[str, str]]) -> str: + """格式化实体列表""" + if not entities: + return "无" + + lines = [] + for entity in entities: + entity_type = entity.get("type", "未知类型") + entity_text = entity.get("text", "") + lines.append(f"- {entity_type}: {entity_text}") + + return "\n".join(lines) + + def build_verification_prompt(self) -> str: + """ + 构建验证提示(用于二次对话) + + Returns: + 验证提示文本 + """ + return """请验证和优化之前提取的三元组结果。 + +请特别关注: +1. 三元组的语义逻辑是否正确 +2. 是否遗漏了重要的关系 +3. 对于省略的成分,是否利用上下文正确补全 +4. 三元组是否准确反映了源文本的语义""" + + + + + diff --git a/dofile/graphrag_pipeline/src/prompts/template.py b/dofile/graphrag_pipeline/src/prompts/template.py new file mode 100644 index 0000000..f2c1c31 --- /dev/null +++ b/dofile/graphrag_pipeline/src/prompts/template.py @@ -0,0 +1,227 @@ +""" +统一Prompt模板框架 +实现五个核心模块:任务描述、候选目标、任务示例、任务强调、二次对话 +""" + +from typing import Dict, List, Optional, Any +from enum import Enum + + +class PromptModule(str, Enum): + """Prompt模块类型""" + TASK_DESCRIPTION = "任务描述" + CANDIDATE_TARGETS = "候选目标" + TASK_EXAMPLES = "任务示例" + TASK_EMPHASIS = "任务强调" + SECOND_CONVERSATION = "二次对话" + + +class PromptTemplate: + """ + 统一Prompt模板类 + 实现论文中描述的五个模块设计 + """ + + def __init__( + self, + task_description: Optional[str] = None, + candidate_targets: Optional[Dict[str, str]] = None, + task_examples: Optional[List[Dict[str, Any]]] = None, + task_emphasis: Optional[str] = None, + second_conversation: Optional[str] = None, + ): + """ + 初始化Prompt模板 + + Args: + task_description: 任务描述(系统消息和用户消息) + candidate_targets: 候选目标(实体类型或关系类型定义) + task_examples: 任务示例(少样本学习) + task_emphasis: 任务强调(输出格式要求) + second_conversation: 二次对话提示 + """ + self.task_description = task_description or {} + self.candidate_targets = candidate_targets or {} + self.task_examples = task_examples or [] + self.task_emphasis = task_emphasis or "" + self.second_conversation = second_conversation or "" + + def build_system_message(self, role: str, capability: str, task: str) -> str: + """ + 构建系统消息(任务描述模块的一部分) + + Args: + role: 模型角色(如"经验丰富的国土空间规划法规分析专家") + capability: 模型能力 + task: 任务描述 + + Returns: + 系统消息字符串 + """ + return f"""你是一个{role},{capability}。你的任务是{task}。""" + + def build_user_message( + self, + task_steps: List[str], + input_format: str, + output_format: str, + ) -> str: + """ + 构建用户消息(任务描述模块的一部分) + + Args: + task_steps: 任务步骤列表 + input_format: 输入格式说明 + output_format: 输出格式说明 + + Returns: + 用户消息字符串 + """ + steps_str = "\n".join([f"{i+1}. {step}" for i, step in enumerate(task_steps)]) + return f"""任务步骤: +{steps_str} + +输入格式: +{input_format} + +输出格式: +{output_format}""" + + def build_candidate_targets_section(self) -> str: + """ + 构建候选目标部分 + + Returns: + 候选目标文本 + """ + if not self.candidate_targets: + return "" + + lines = ["候选目标及其定义:"] + for target, definition in self.candidate_targets.items(): + lines.append(f"- {target}: {definition}") + + return "\n".join(lines) + + def build_task_examples_section(self) -> str: + """ + 构建任务示例部分(少样本学习) + + Returns: + 任务示例文本 + """ + if not self.task_examples: + return "" + + examples_text = "任务示例:\n\n" + for i, example in enumerate(self.task_examples, 1): + examples_text += f"示例 {i}:\n" + examples_text += f"输入:{example.get('input', '')}\n" + examples_text += f"输出:{example.get('output', '')}\n\n" + + return examples_text + + def build_task_emphasis_section(self) -> str: + """ + 构建任务强调部分 + + Returns: + 任务强调文本 + """ + if not self.task_emphasis: + return "" + + return f"""重要提示: +{self.task_emphasis} + +请严格按照输出格式要求,仅输出结果,避免任何解释性内容。""" + + def build_first_conversation( + self, + input_data: str, + system_message: str, + user_message: str, + ) -> List[Dict[str, str]]: + """ + 构建第一阶段对话消息 + + Args: + input_data: 输入数据 + system_message: 系统消息 + user_message: 用户消息 + + Returns: + 对话消息列表 + """ + # 构建完整的用户消息 + full_user_message = user_message + + # 添加候选目标 + if self.candidate_targets: + full_user_message += "\n\n" + self.build_candidate_targets_section() + + # 添加任务示例 + if self.task_examples: + full_user_message += "\n\n" + self.build_task_examples_section() + + # 添加任务强调 + if self.task_emphasis: + full_user_message += "\n\n" + self.build_task_emphasis_section() + + # 添加输入数据 + full_user_message += f"\n\n输入数据:\n{input_data}" + + return [ + {"role": "system", "content": system_message}, + {"role": "user", "content": full_user_message} + ] + + def build_second_conversation( + self, + original_input: str, + first_result: str, + verification_instruction: str, + ) -> List[Dict[str, str]]: + """ + 构建第二阶段对话消息(验证和优化) + + Args: + original_input: 原始输入数据 + first_result: 第一阶段的结果 + verification_instruction: 验证指令 + + Returns: + 对话消息列表 + """ + system_message = """你需要验证和优化之前的结果。请仔细检查结果的正确性和完整性,根据上下文信息补全缺失的内容,并修正任何错误。""" + + user_message = f"""{verification_instruction} + +原始输入: +{original_input} + +之前的结果: +{first_result} + +请对结果进行验证、补全和优化。""" + + return [ + {"role": "system", "content": system_message}, + {"role": "assistant", "content": first_result}, + {"role": "user", "content": user_message} + ] + + def to_dict(self) -> Dict[str, Any]: + """将模板转换为字典""" + return { + "task_description": self.task_description, + "candidate_targets": self.candidate_targets, + "task_examples": self.task_examples, + "task_emphasis": self.task_emphasis, + "second_conversation": self.second_conversation, + } + + + + + diff --git a/dofile/graphrag_pipeline/src/query/__init__.py b/dofile/graphrag_pipeline/src/query/__init__.py new file mode 100644 index 0000000..f4fa6a7 --- /dev/null +++ b/dofile/graphrag_pipeline/src/query/__init__.py @@ -0,0 +1,12 @@ +"""查询模块(参考GraphRAG)""" + +from .global_search import GlobalSearcher +from .local_search import LocalSearcher +from .drift_search import DriftSearcher + +__all__ = ["GlobalSearcher", "LocalSearcher", "DriftSearcher"] + + + + + diff --git a/dofile/graphrag_pipeline/src/query/drift_search.py b/dofile/graphrag_pipeline/src/query/drift_search.py new file mode 100644 index 0000000..295584b --- /dev/null +++ b/dofile/graphrag_pipeline/src/query/drift_search.py @@ -0,0 +1,187 @@ +""" +DRIFT搜索(参考GraphRAG) +结合社区信息的上下文搜索(Community-aware search) +""" + +import logging +from typing import List, Dict, Any, Optional, Set +import networkx as nx + +from .local_search import LocalSearcher +from .global_search import GlobalSearcher +from ..utils.config import Config + +logger = logging.getLogger(__name__) + + +class DriftSearcher: + """DRIFT搜索器(结合社区信息的局部搜索)""" + + def __init__( + self, + kg: Optional[nx.DiGraph] = None, + communities: Dict = None, + config: Optional[Config] = None, + ): + """ + 初始化DRIFT搜索器 + + Args: + kg: 知识图谱 + communities: 社区信息(包含摘要) + config: 配置对象 + """ + self.kg = kg + self.communities = communities or {} + self.config = config or Config() + + # 使用局部搜索器作为基础 + self.local_searcher = LocalSearcher(kg, config) + self.global_searcher = GlobalSearcher(kg, communities, config) + + def search(self, query: str, entity: str = None, depth: int = 2) -> Dict[str, Any]: + """ + DRIFT搜索:结合社区上下文信息的局部搜索 + + Args: + query: 查询文本 + entity: 实体名称(如果提供,则进行局部搜索) + depth: 搜索深度 + + Returns: + 搜索结果,结合了局部搜索和社区信息 + """ + logger.info(f"执行DRIFT搜索: query={query}, entity={entity}") + + result = { + "query": query, + "entity": entity, + } + + # 1. 如果有实体,先进行局部搜索 + local_result = None + if entity: + local_result = self.local_searcher.search(entity, depth=depth) + result["local_search"] = local_result + + # 2. 执行全局搜索获取相关社区 + global_result = self.global_searcher.search(query, top_k=3) + result["global_context"] = global_result + + # 3. 结合社区信息扩展局部搜索结果 + if local_result and "subgraph" in local_result: + subgraph_nodes = set(local_result["subgraph"]["nodes"]) + + # 从相关社区中获取额外的上下文节点 + context_nodes = set() + for comm in global_result.get("relevant_communities", []): + context_nodes.update(comm.get("nodes", [])) + + # 合并节点 + expanded_nodes = subgraph_nodes | context_nodes + expanded_nodes = list(expanded_nodes)[:100] # 限制大小 + + # 构建扩展子图 + if self.kg: + expanded_subgraph = self.kg.subgraph(expanded_nodes) + + # 提取社区上下文关系 + community_relations = [] + for comm in global_result.get("relevant_communities", []): + comm_nodes = set(comm.get("nodes", [])) + # 找到子图中的社区节点之间的连接 + for u in comm_nodes: + if u in expanded_subgraph: + for v in expanded_subgraph.successors(u): + if v in comm_nodes: + edge_data = expanded_subgraph[u][v] + community_relations.append({ + "source": u, + "target": v, + "relation": edge_data.get("relation", ""), + "community": comm.get("community_id", ""), + "summary": comm.get("summary", "") + }) + + result["expanded_subgraph"] = { + "nodes": expanded_nodes, + "num_nodes": len(expanded_nodes), + "num_edges": expanded_subgraph.number_of_edges(), + "community_relations": community_relations + } + + # 4. 生成综合答案(结合局部和全局信息) + if self.global_searcher.llm_client: + answer = self._generate_combined_answer( + query, + local_result, + global_result + ) + result["answer"] = answer + + return result + + def _generate_combined_answer( + self, + query: str, + local_result: Optional[Dict], + global_result: Dict, + ) -> str: + """ + 生成结合局部和全局信息的综合答案 + + Args: + query: 查询文本 + local_result: 局部搜索结果 + global_result: 全局搜索结果 + + Returns: + 综合答案 + """ + llm_client = self.global_searcher.llm_client + if not llm_client: + return "答案生成功能不可用" + + # 构建上下文信息 + context_parts = [] + + # 添加局部搜索信息 + if local_result and "subgraph" in local_result: + subgraph = local_result["subgraph"] + context_parts.append(f"""局部实体关系网络: +- 实体节点数:{subgraph['num_nodes']} +- 关系边数:{subgraph['num_edges']} +- 主要关系:{', '.join([r['relation'] for r in subgraph.get('relations', [])[:5]])}""") + + # 添加全局社区信息 + communities_info = "\n\n".join([ + f"社区 {i+1}:{comm['summary']}" + for i, comm in enumerate(global_result.get("relevant_communities", [])[:3]) + ]) + if communities_info: + context_parts.append(f"相关社区摘要:\n{communities_info}") + + context = "\n\n".join(context_parts) + + prompt = f"""基于以下知识图谱的局部和全局信息,回答用户的问题。 + +查询:{query} + +知识图谱上下文: +{context} + +请生成一个准确、完整的答案,充分利用局部实体关系和全局社区信息:""" + + messages = [ + {"role": "system", "content": "你是一个法规知识问答专家,擅长结合知识图谱的局部和全局信息回答复杂问题。"}, + {"role": "user", "content": prompt} + ] + + try: + answer = llm_client.chat(messages) + return answer.strip() + except Exception as e: + logger.error(f"综合答案生成失败: {e}") + return "答案生成失败" + + diff --git a/dofile/graphrag_pipeline/src/query/global_search.py b/dofile/graphrag_pipeline/src/query/global_search.py new file mode 100644 index 0000000..e6fd4de --- /dev/null +++ b/dofile/graphrag_pipeline/src/query/global_search.py @@ -0,0 +1,194 @@ +""" +全局搜索(参考GraphRAG) +利用社区摘要进行整体推理 +""" + +import logging +from typing import List, Dict, Any, Optional +import networkx as nx + +from ..utils.llm_client import LLMClient, LLMProvider +from ..utils.config import Config + +logger = logging.getLogger(__name__) + + +class GlobalSearcher: + """全局搜索器""" + + def __init__( + self, + kg: Optional[nx.DiGraph] = None, + communities: Dict = None, + config: Optional[Config] = None, + ): + """ + 初始化全局搜索器 + + Args: + kg: 知识图谱 + communities: 社区信息(包含摘要) + config: 配置对象 + """ + self.kg = kg + self.communities = communities or {} + self.config = config or Config() + + try: + self.llm_client = LLMClient( + provider=LLMProvider.QWEN, + model="qwen-max", + config=self.config + ) + except Exception as e: + logger.warning(f"LLM客户端初始化失败: {e}") + self.llm_client = None + + def search(self, query: str, top_k: int = 5) -> Dict[str, Any]: + """ + 全局搜索:利用社区摘要进行整体推理 + + Args: + query: 查询文本 + top_k: 返回top-k个最相关的社区 + + Returns: + 搜索结果,包含相关社区及其摘要 + """ + logger.info(f"执行全局搜索: {query}") + + if not self.communities: + logger.warning("社区信息为空,无法执行全局搜索") + return { + "query": query, + "results": [], + "message": "社区信息不可用" + } + + # 使用LLM评估查询与社区摘要的相关性 + relevant_communities = [] + + if self.llm_client: + # 使用LLM进行相关性评估 + for comm_id, comm_data in self.communities.items(): + summary = comm_data.get("summary", "") + nodes = comm_data.get("nodes", []) + + if not summary: + continue + + # 评估相关性 + relevance_score = self._evaluate_relevance(query, summary) + + relevant_communities.append({ + "community_id": comm_id, + "summary": summary, + "nodes": nodes, + "size": len(nodes), + "relevance_score": relevance_score + }) + else: + # 简单的关键词匹配 + query_lower = query.lower() + for comm_id, comm_data in self.communities.items(): + summary = comm_data.get("summary", "") + if query_lower in summary.lower(): + relevant_communities.append({ + "community_id": comm_id, + "summary": summary, + "nodes": comm_data.get("nodes", []), + "size": len(comm_data.get("nodes", [])), + "relevance_score": 0.5 + }) + + # 按相关性排序 + relevant_communities.sort(key=lambda x: x.get("relevance_score", 0), reverse=True) + + # 取top-k + top_communities = relevant_communities[:top_k] + + # 使用LLM生成综合答案 + answer = self._generate_answer(query, top_communities) + + return { + "query": query, + "answer": answer, + "relevant_communities": top_communities, + "total_communities": len(self.communities) + } + + def _evaluate_relevance(self, query: str, summary: str) -> float: + """ + 评估查询与社区摘要的相关性 + + Args: + query: 查询文本 + summary: 社区摘要 + + Returns: + 相关性分数(0-1) + """ + if not self.llm_client: + return 0.5 + + prompt = f"""请评估以下查询与社区摘要的相关性,给出0-1之间的分数(1表示高度相关,0表示不相关)。 + +查询:{query} + +社区摘要:{summary} + +请只回答一个0-1之间的浮点数:""" + + messages = [ + {"role": "system", "content": "你是一个相关性评估专家。"}, + {"role": "user", "content": prompt} + ] + + try: + result = self.llm_client.chat(messages).strip() + score = float(result) + return max(0.0, min(1.0, score)) + except: + return 0.5 + + def _generate_answer(self, query: str, communities: List[Dict]) -> str: + """ + 基于相关社区生成综合答案 + + Args: + query: 查询文本 + communities: 相关社区列表 + + Returns: + 生成的答案 + """ + if not self.llm_client: + return "答案生成功能不可用(LLM未初始化)" + + communities_info = "\n\n".join([ + f"社区 {i+1}(包含{comm['size']}个实体):\n{comm['summary']}" + for i, comm in enumerate(communities) + ]) + + prompt = f"""基于以下知识图谱社区摘要,回答用户的问题。 + +查询:{query} + +相关社区摘要: +{communities_info} + +请生成一个准确、完整的答案:""" + + messages = [ + {"role": "system", "content": "你是一个法规知识问答专家,擅长基于知识图谱信息回答法规相关问题。"}, + {"role": "user", "content": prompt} + ] + + try: + answer = self.llm_client.chat(messages) + return answer.strip() + except Exception as e: + logger.error(f"答案生成失败: {e}") + return "答案生成失败" + + diff --git a/dofile/graphrag_pipeline/src/query/local_search.py b/dofile/graphrag_pipeline/src/query/local_search.py new file mode 100644 index 0000000..a11dbdb --- /dev/null +++ b/dofile/graphrag_pipeline/src/query/local_search.py @@ -0,0 +1,162 @@ +""" +局部搜索(参考GraphRAG) +扩展到邻居和相关概念 +""" + +import logging +from typing import List, Dict, Any, Optional, Set +import networkx as nx + +from ..utils.config import Config + +logger = logging.getLogger(__name__) + + +class LocalSearcher: + """局部搜索器""" + + def __init__(self, kg: Optional[nx.DiGraph] = None, config: Optional[Config] = None): + """ + 初始化局部搜索器 + + Args: + kg: 知识图谱 + config: 配置对象 + """ + self.kg = kg + self.config = config or Config() + + def search(self, entity: str, depth: int = 2, max_nodes: int = 50) -> Dict[str, Any]: + """ + 局部搜索:从指定实体扩展到邻居和相关概念 + + Args: + entity: 实体名称 + depth: 搜索深度(跳数) + max_nodes: 最大节点数 + + Returns: + 搜索结果,包含子图和相关信息 + """ + logger.info(f"执行局部搜索: {entity}, depth={depth}") + + if not self.kg: + return { + "entity": entity, + "error": "知识图谱未加载" + } + + # 查找实体节点(支持模糊匹配) + target_nodes = self._find_entity_nodes(entity) + + if not target_nodes: + return { + "entity": entity, + "error": f"未找到实体: {entity}", + "suggestions": list(self.kg.nodes())[:10] # 提供一些建议 + } + + # 扩展搜索:收集所有相关的节点 + explored_nodes: Set[str] = set() + nodes_to_explore = list(target_nodes) + + for current_depth in range(depth + 1): + if not nodes_to_explore: + break + + current_level_nodes = list(nodes_to_explore) + nodes_to_explore = [] + + for node in current_level_nodes: + if node in explored_nodes: + continue + + explored_nodes.add(node) + + # 添加到下一层探索的节点 + if current_depth < depth: + # 前驱节点 + predecessors = list(self.kg.predecessors(node)) + # 后继节点 + successors = list(self.kg.successors(node)) + + for neighbor in predecessors + successors: + if neighbor not in explored_nodes: + nodes_to_explore.append(neighbor) + + if len(explored_nodes) >= max_nodes: + break + + # 构建子图 + subgraph_nodes = list(explored_nodes) + subgraph = self.kg.subgraph(subgraph_nodes) + + # 提取关系信息 + relations = [] + for u, v, data in subgraph.edges(data=True): + relations.append({ + "source": u, + "target": v, + "relation": data.get("relation", ""), + "metadata": {k: v for k, v in data.items() if k != "relation"} + }) + + # 计算中心性(在子图中) + centrality = {} + if subgraph.number_of_nodes() > 0: + try: + degree_centrality = nx.degree_centrality(subgraph) + centrality = { + node: { + "degree": degree_centrality.get(node, 0), + "neighbors": len(list(subgraph.neighbors(node))) + } + for node in target_nodes + } + except: + pass + + return { + "entity": entity, + "target_nodes": list(target_nodes), + "subgraph": { + "nodes": subgraph_nodes, + "num_nodes": len(subgraph_nodes), + "num_edges": subgraph.number_of_edges(), + "relations": relations + }, + "centrality": centrality, + "depth": depth + } + + def _find_entity_nodes(self, entity: str) -> List[str]: + """ + 在图中查找实体节点(支持模糊匹配) + + Args: + entity: 实体名称 + + Returns: + 匹配的节点列表 + """ + if not self.kg: + return [] + + entity_lower = entity.lower() + exact_matches = [] + partial_matches = [] + + for node in self.kg.nodes(): + node_str = str(node).lower() + if node_str == entity_lower: + exact_matches.append(node) + elif entity_lower in node_str or node_str in entity_lower: + partial_matches.append(node) + + # 优先返回精确匹配 + if exact_matches: + return exact_matches + else: + return partial_matches[:5] # 最多返回5个部分匹配 + + diff --git a/dofile/graphrag_pipeline/src/utils/__init__.py b/dofile/graphrag_pipeline/src/utils/__init__.py new file mode 100644 index 0000000..6cd613a --- /dev/null +++ b/dofile/graphrag_pipeline/src/utils/__init__.py @@ -0,0 +1,17 @@ +"""工具函数模块""" + +from .config import Config, load_config +from .llm_client import LLMClient, LLMProvider +from .file_utils import save_json, load_json, save_text + +__all__ = [ + "Config", + "load_config", + "LLMClient", + "LLMProvider", + "save_json", + "load_json", + "save_text", +] + + diff --git a/dofile/graphrag_pipeline/src/utils/config.py b/dofile/graphrag_pipeline/src/utils/config.py new file mode 100644 index 0000000..2d4afb1 --- /dev/null +++ b/dofile/graphrag_pipeline/src/utils/config.py @@ -0,0 +1,52 @@ +""" +配置管理模块 +""" + +import os +from pathlib import Path +from typing import Optional +from dotenv import load_dotenv + +# 加载.env文件 +env_path = Path(__file__).parent.parent.parent / ".env" +if env_path.exists(): + load_dotenv(env_path) + + +class Config: + """配置类""" + + # API Keys + OPENAI_API_KEY: Optional[str] = os.getenv("OPENAI_API_KEY") + ANTHROPIC_API_KEY: Optional[str] = os.getenv("ANTHROPIC_API_KEY") + DASHSCOPE_API_KEY: Optional[str] = os.getenv("DASHSCOPE_API_KEY") + VOLCENGINE_ACCESS_KEY: Optional[str] = os.getenv("VOLCENGINE_ACCESS_KEY") + VOLCENGINE_SECRET_KEY: Optional[str] = os.getenv("VOLCENGINE_SECRET_KEY") + ZHIPUAI_API_KEY: Optional[str] = os.getenv("ZHIPUAI_API_KEY") + SILICONFLOW_API_KEY: Optional[str] = os.getenv("SILICONFLOW_API_KEY") + SILICONFLOW_API_BASE: Optional[str] = os.getenv("SILICONFLOW_API_BASE", "https://api.siliconflow.cn/v1") + + # 路径配置 + DATA_DIR: str = os.getenv("DATA_DIR", "../data") + OUTPUT_DIR: str = os.getenv("OUTPUT_DIR", "./output") + + # 日志配置 + LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO") + + # HanLP配置 + HANLP_MODEL_PATH: Optional[str] = os.getenv("HANLP_MODEL_PATH") + + # LLM配置 + TEMPERATURE: float = float(os.getenv("TEMPERATURE", "0.4")) + FREQUENCY_PENALTY: float = float(os.getenv("FREQUENCY_PENALTY", "0.6")) + PRESENCE_PENALTY: float = float(os.getenv("PRESENCE_PENALTY", "0.6")) + + # 文本处理配置 + MAX_TEXTUNIT_LENGTH: int = int(os.getenv("MAX_TEXTUNIT_LENGTH", "500")) + + +def load_config() -> Config: + """加载配置""" + return Config() + + diff --git a/dofile/graphrag_pipeline/src/utils/file_utils.py b/dofile/graphrag_pipeline/src/utils/file_utils.py new file mode 100644 index 0000000..81ccc3d --- /dev/null +++ b/dofile/graphrag_pipeline/src/utils/file_utils.py @@ -0,0 +1,58 @@ +""" +文件工具函数 +""" + +import json +import logging +from pathlib import Path +from typing import Any, Dict, List, Optional + +logger = logging.getLogger(__name__) + + +def save_json(data: Any, filepath: str, indent: int = 2, ensure_ascii: bool = False): + """ + 保存数据为JSON文件 + + Args: + data: 要保存的数据 + filepath: 文件路径 + indent: JSON缩进 + ensure_ascii: 是否确保ASCII编码 + """ + path = Path(filepath) + path.parent.mkdir(parents=True, exist_ok=True) + + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=ensure_ascii, indent=indent) + + logger.info(f"数据已保存到: {filepath}") + + +def load_json(filepath: str) -> Any: + """ + 从JSON文件加载数据 + + Args: + filepath: 文件路径 + + Returns: + 加载的数据 + """ + with open(filepath, "r", encoding="utf-8") as f: + return json.load(f) + + +def save_text(text: str, filepath: str): + """保存文本到文件""" + path = Path(filepath) + path.parent.mkdir(parents=True, exist_ok=True) + + with open(path, "w", encoding="utf-8") as f: + f.write(text) + + logger.info(f"文本已保存到: {filepath}") + + + + diff --git a/dofile/graphrag_pipeline/src/utils/llm_client.py b/dofile/graphrag_pipeline/src/utils/llm_client.py new file mode 100644 index 0000000..905e172 --- /dev/null +++ b/dofile/graphrag_pipeline/src/utils/llm_client.py @@ -0,0 +1,219 @@ +""" +LLM客户端封装 +支持多种LLM提供商:OpenAI, Anthropic, Qwen, Doubao, GLM, SiliconFlow +""" + +import asyncio +import logging +from typing import List, Dict, Optional, Any +from enum import Enum +from tenacity import retry, stop_after_attempt, wait_exponential + +from .config import Config + +logger = logging.getLogger(__name__) + + +class LLMProvider(str, Enum): + """LLM提供商枚举""" + OPENAI = "openai" + ANTHROPIC = "anthropic" + QWEN = "qwen" + DOUBAO = "doubao" + GLM = "glm" + SILICONFLOW = "siliconflow" + + +class LLMClient: + """LLM客户端,统一封装不同提供商的API""" + + def __init__( + self, + provider: LLMProvider, + model: str, + config: Optional[Config] = None, + ): + """ + 初始化LLM客户端 + + Args: + provider: LLM提供商 + model: 模型名称 + config: 配置对象 + """ + self.provider = provider + self.model = model + self.config = config or Config() + + # 初始化对应的客户端 + self._init_client() + + def _init_client(self): + """初始化对应提供商的客户端""" + if self.provider == LLMProvider.OPENAI: + try: + import openai + self.client = openai.OpenAI(api_key=self.config.OPENAI_API_KEY) + except ImportError: + raise ImportError("请安装openai包: pip install openai") + + elif self.provider == LLMProvider.ANTHROPIC: + try: + import anthropic + self.client = anthropic.Anthropic(api_key=self.config.ANTHROPIC_API_KEY) + except ImportError: + raise ImportError("请安装anthropic包: pip install anthropic") + + elif self.provider == LLMProvider.QWEN: + try: + import dashscope + dashscope.api_key = self.config.DASHSCOPE_API_KEY + self.client = dashscope + except ImportError: + raise ImportError("请安装dashscope包: pip install dashscope") + + elif self.provider == LLMProvider.DOUBAO: + try: + from volcengine.maas import MaasService, MaasException, ChatRole + self.client = MaasService( + "cn-beijing", + access_key=self.config.VOLCENGINE_ACCESS_KEY, + secret_key=self.config.VOLCENGINE_SECRET_KEY, + ) + except ImportError: + raise ImportError("请安装volcengine-python-sdk包") + + elif self.provider == LLMProvider.GLM: + try: + import zhipuai + zhipuai.api_key = self.config.ZHIPUAI_API_KEY + self.client = zhipuai + except ImportError: + raise ImportError("请安装zhipuai包: pip install zhipuai") + + elif self.provider == LLMProvider.SILICONFLOW: + try: + import openai + # 硅基流动兼容OpenAI API格式 + self.client = openai.OpenAI( + api_key=self.config.SILICONFLOW_API_KEY, + base_url=self.config.SILICONFLOW_API_BASE or "https://api.siliconflow.cn/v1" + ) + except ImportError: + raise ImportError("请安装openai包: pip install openai") + + else: + raise ValueError(f"不支持的LLM提供商: {self.provider}") + + @retry( + stop=stop_after_attempt(3), + wait=wait_exponential(multiplier=1, min=4, max=10) + ) + async def chat_async( + self, + messages: List[Dict[str, str]], + temperature: Optional[float] = None, + max_tokens: Optional[int] = None, + ) -> str: + """ + 异步聊天接口 + + Args: + messages: 消息列表 + temperature: 温度参数 + max_tokens: 最大token数 + + Returns: + 模型响应文本 + """ + temperature = temperature or self.config.TEMPERATURE + + try: + if self.provider == LLMProvider.OPENAI: + response = self.client.chat.completions.create( + model=self.model, + messages=messages, + temperature=temperature, + max_tokens=max_tokens, + ) + return response.choices[0].message.content + + elif self.provider == LLMProvider.ANTHROPIC: + response = await self.client.messages.create( + model=self.model, + messages=messages, + temperature=temperature, + max_tokens=max_tokens or 4096, + ) + return response.content[0].text + + elif self.provider == LLMProvider.QWEN: + response = self.client.Generation.call( + model=self.model, + messages=messages, + temperature=temperature, + ) + return response.output.text + + elif self.provider == LLMProvider.DOUBAO: + # 字节跳动API同步调用 + response = self.client.chat( + model=self.model, + messages=messages, + parameters={ + "temperature": temperature, + "max_tokens": max_tokens or 4096, + } + ) + return response.get("choices", [{}])[0].get("message", {}).get("content", "") + + elif self.provider == LLMProvider.GLM: + response = self.client.model_api.invoke( + model=self.model, + messages=messages, + temperature=temperature, + ) + return response.get("data", {}).get("choices", [{}])[0].get("message", {}).get("content", "") + + elif self.provider == LLMProvider.SILICONFLOW: + # 硅基流动兼容OpenAI API格式 + response = self.client.chat.completions.create( + model=self.model, + messages=messages, + temperature=temperature, + max_tokens=max_tokens, + ) + return response.choices[0].message.content + + except Exception as e: + logger.error(f"LLM调用失败 ({self.provider}): {e}") + raise + + def chat( + self, + messages: List[Dict[str, str]], + temperature: Optional[float] = None, + max_tokens: Optional[int] = None, + ) -> str: + """ + 同步聊天接口(内部调用异步接口) + + Args: + messages: 消息列表 + temperature: 温度参数 + max_tokens: 最大token数 + + Returns: + 模型响应文本 + """ + try: + loop = asyncio.get_event_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + return loop.run_until_complete( + self.chat_async(messages, temperature, max_tokens) + ) + + diff --git a/dofile/graphrag_pipeline/tests/README.md b/dofile/graphrag_pipeline/tests/README.md new file mode 100644 index 0000000..dbb979f --- /dev/null +++ b/dofile/graphrag_pipeline/tests/README.md @@ -0,0 +1,75 @@ +# 测试文件说明 + +本目录包含所有测试脚本。 + +## 测试文件列表 + +### 1. `test_basic.py` +基础功能测试,包括: +- 模块导入测试 +- 本体模型测试 +- Prompt模板测试 + +运行方式: +```bash +uv run python tests/test_basic.py +``` + +### 2. `test_siliconflow.py` +测试硅基流动API配置,验证API密钥和连接是否正常。 + +运行方式: +```bash +uv run python tests/test_siliconflow.py +``` + +### 3. `test_documents.py` +测试文档解析功能,包括: +- 文档解析(.docx文件) +- TextUnit切分 +- 不调用LLM,仅测试解析逻辑 + +运行方式: +```bash +uv run python tests/test_documents.py +``` + +### 4. `test_model.py` +测试硅基流动平台上的多个模型,找出可用模型。 + +运行方式: +```bash +uv run python tests/test_model.py +``` + +### 5. `test_my_models.py` +测试项目配置使用的两个指定模型: +- `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` +- `Qwen/Qwen2.5-7B-Instruct` + +运行方式: +```bash +uv run python tests/test_my_models.py +``` + +### 6. `test_quick.py` +快速测试完整流程,处理少量TextUnit(1个文档的前2个),验证: +- 文档解析 +- 实体识别(NER) +- 关系抽取(RE) +- 知识图谱构建 +- 社区检测 + +运行方式: +```bash +uv run python tests/test_quick.py +``` + +## 注意事项 + +- 测试脚本需要使用配置的API密钥(`.env`文件) +- 部分测试会调用LLM API,可能需要一些时间 +- 确保已安装所有依赖包:`uv sync` + + + diff --git a/dofile/graphrag_pipeline/tests/test_basic.py b/dofile/graphrag_pipeline/tests/test_basic.py new file mode 100644 index 0000000..724bdea --- /dev/null +++ b/dofile/graphrag_pipeline/tests/test_basic.py @@ -0,0 +1,72 @@ +""" +基础功能测试 +""" + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent.parent)) + +def test_imports(): + """测试模块导入""" + try: + from src.preprocessing.text_processor import TextProcessor + from src.preprocessing.document_parser import DocumentParser + from src.ontology.schema import OntologySchema + from src.prompts.ner_prompts import NERPromptBuilder + from src.extraction.ner import NERExtractor + from src.kg_builder.graph import KnowledgeGraph + print("✅ 所有模块导入成功") + return True + except ImportError as e: + print(f"❌ 模块导入失败: {e}") + return False + + +def test_ontology(): + """测试本体模型""" + try: + from src.ontology.schema import OntologySchema + + schema = OntologySchema() + summary = schema.get_schema_summary() + + assert summary["entity_types"] == 9 + assert summary["relation_types"] == 8 + print("✅ 本体模型测试通过") + return True + except Exception as e: + print(f"❌ 本体模型测试失败: {e}") + return False + + +def test_prompt_template(): + """测试Prompt模板""" + try: + from src.prompts.template import PromptTemplate + + template = PromptTemplate( + task_description={"system": "test", "user": "test"}, + candidate_targets={"entity": "definition"}, + task_examples=[{"input": "test", "output": "test"}], + task_emphasis="test", + ) + + assert template.task_description is not None + print("✅ Prompt模板测试通过") + return True + except Exception as e: + print(f"❌ Prompt模板测试失败: {e}") + return False + + +if __name__ == "__main__": + print("开始运行基础测试...") + test_imports() + test_ontology() + test_prompt_template() + print("测试完成!") + + + + diff --git a/dofile/graphrag_pipeline/tests/test_documents.py b/dofile/graphrag_pipeline/tests/test_documents.py new file mode 100644 index 0000000..9c5e259 --- /dev/null +++ b/dofile/graphrag_pipeline/tests/test_documents.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +""" +测试文档解析(仅测试3个文件,不调用LLM) +""" + +import sys +import json +import logging +from pathlib import Path + +# 修复:移除conda环境的路径,确保使用虚拟环境的包 +sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()] +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.preprocessing.document_parser import DocumentParser +from src.utils.config import load_config + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +def main(): + """测试文档解析""" + logger.info("=" * 60) + logger.info("测试文档解析(3个文件)") + logger.info("=" * 60) + + # 配置 + config = load_config() + data_dir = Path("../data/1法律") + output_dir = Path("./output") + output_dir.mkdir(parents=True, exist_ok=True) + + # 解析文档 + logger.info(f"解析目录: {data_dir}") + parser = DocumentParser() + docs = parser.parse_directory(str(data_dir)) + + # 只处理前3个 + docs = docs[:3] + logger.info(f"处理 {len(docs)} 个文档") + + # 切分为TextUnit + logger.info("切分文档为TextUnit...") + all_textunits = [] + for i, doc in enumerate(docs, 1): + logger.info(f"处理文档 {i}/{len(docs)}: {Path(doc.get('file_path', '')).name}") + textunits = parser.split_into_textunits( + doc, + max_length=config.MAX_TEXTUNIT_LENGTH + ) + for tu in textunits: + tu["doc_id"] = doc.get("file_path", "") + tu["id"] = f"{doc.get('file_path', '')}_{tu.get('paragraph_index', 0)}" + all_textunits.extend(textunits) + logger.info(f" - 生成 {len(textunits)} 个TextUnit") + + logger.info(f"共生成 {len(all_textunits)} 个TextUnit") + + # 保存结果 + output_file = output_dir / "test_textunits.json" + result = { + "num_docs": len(docs), + "num_textunits": len(all_textunits), + "docs": [ + { + "file_path": doc.get("file_path", ""), + "title": doc.get("title", ""), + "text_length": len(doc.get("text", "")), + } + for doc in docs + ], + "sample_textunits": [ + { + "id": tu.get("id", ""), + "text": tu.get("text", "")[:200] + "..." if len(tu.get("text", "")) > 200 else tu.get("text", ""), + "paragraph_index": tu.get("paragraph_index", 0), + } + for tu in all_textunits[:10] # 只保存前10个作为示例 + ] + } + + with open(output_file, "w", encoding="utf-8") as f: + json.dump(result, f, ensure_ascii=False, indent=2) + + logger.info("=" * 60) + logger.info("测试完成!") + logger.info(f"结果已保存到: {output_file}") + logger.info(f"统计信息:") + logger.info(f" - 文档数: {result['num_docs']}") + logger.info(f" - TextUnit数: {result['num_textunits']}") + logger.info("=" * 60) + + +if __name__ == "__main__": + main() + diff --git a/dofile/graphrag_pipeline/tests/test_model.py b/dofile/graphrag_pipeline/tests/test_model.py new file mode 100644 index 0000000..1141695 --- /dev/null +++ b/dofile/graphrag_pipeline/tests/test_model.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +""" +测试硅基流动API并找到可用的模型 +""" + +import sys +import os +from pathlib import Path + +sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()] +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.utils.config import load_config +from src.utils.llm_client import LLMClient, LLMProvider + +# 常见的硅基流动模型名称 +TEST_MODELS = [ + "deepseek-ai/DeepSeek-V2.5", + "deepseek-ai/DeepSeek-V2", + "Qwen/Qwen2.5-72B-Instruct", + "Qwen/Qwen2.5-72B-Chat", + "meta-llama/Llama-3.1-70B-Instruct", + "01-ai/Yi-1.5-34B-Chat", + "mistralai/Mistral-7B-Instruct-v0.2", +] + +def test_model(provider, model_name): + """测试单个模型""" + try: + config = load_config() + client = LLMClient(provider=provider, model=model_name, config=config) + messages = [{"role": "user", "content": "你好"}] + response = client.chat(messages) + print(f"✅ 模型 {model_name} 可用") + print(f" 响应: {response[:50]}...") + return True + except Exception as e: + print(f"❌ 模型 {model_name} 不可用: {str(e)[:100]}") + return False + +if __name__ == "__main__": + print("=" * 60) + print("测试硅基流动模型") + print("=" * 60) + print() + + config = load_config() + if not config.SILICONFLOW_API_KEY: + print("❌ SILICONFLOW_API_KEY 未配置") + sys.exit(1) + + print(f"API密钥: {config.SILICONFLOW_API_KEY[:20]}...") + print(f"API Base: {config.SILICONFLOW_API_BASE}") + print() + + # 测试所有模型 + available_models = [] + for model in TEST_MODELS: + if test_model(LLMProvider.SILICONFLOW, model): + available_models.append(model) + print() + + print("=" * 60) + if available_models: + print(f"✅ 找到 {len(available_models)} 个可用模型:") + for model in available_models: + print(f" - {model}") + print(f"\n建议使用的模型: {available_models[0]}") + else: + print("❌ 没有找到可用的模型,请检查API密钥或模型名称") + print("=" * 60) + + diff --git a/dofile/graphrag_pipeline/tests/test_my_models.py b/dofile/graphrag_pipeline/tests/test_my_models.py new file mode 100644 index 0000000..4f4230d --- /dev/null +++ b/dofile/graphrag_pipeline/tests/test_my_models.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +""" +测试用户指定的两个模型 +""" + +import sys +from pathlib import Path + +sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()] +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.utils.llm_client import LLMClient, LLMProvider +from src.utils.config import load_config + +def test_model(provider, model_name): + """测试单个模型""" + try: + config = load_config() + client = LLMClient(provider=provider, model=model_name, config=config) + messages = [{"role": "user", "content": "你好"}] + response = client.chat(messages) + print(f"✅ 模型 {model_name} 可用") + print(f" 响应: {response[:50]}...") + return True + except Exception as e: + print(f"❌ 模型 {model_name} 不可用: {str(e)[:100]}") + return False + +if __name__ == "__main__": + print("=" * 60) + print("测试指定的两个模型") + print("=" * 60) + print() + + config = load_config() + if not config.SILICONFLOW_API_KEY: + print("❌ SILICONFLOW_API_KEY 未配置") + sys.exit(1) + + # 测试用户指定的两个模型 + models_to_test = [ + "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", + "Qwen/Qwen2.5-7B-Instruct", + ] + + available = [] + for model in models_to_test: + if test_model(LLMProvider.SILICONFLOW, model): + available.append(model) + print() + + print("=" * 60) + if len(available) == len(models_to_test): + print(f"✅ 所有模型都可用!") + elif available: + print(f"⚠️ 部分模型可用: {len(available)}/{len(models_to_test)}") + else: + print("❌ 没有可用模型") + print("=" * 60) + + diff --git a/dofile/graphrag_pipeline/tests/test_quick.py b/dofile/graphrag_pipeline/tests/test_quick.py new file mode 100644 index 0000000..a6880cd --- /dev/null +++ b/dofile/graphrag_pipeline/tests/test_quick.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +""" +快速测试:只处理1个文档的前2个TextUnit +""" + +import sys +import logging +from pathlib import Path + +sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()] +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.preprocessing.document_parser import DocumentParser +from src.kg_builder.indexer import GraphIndexer +from src.utils.config import load_config + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +def main(): + logger.info("=" * 60) + logger.info("快速测试:处理1个文档的前2个TextUnit") + logger.info("=" * 60) + + config = load_config() + + # 解析1个文档 + parser = DocumentParser() + doc_path = "../data/1法律/10-中华人民共和国乡村振兴促进法.docx" + doc = parser.parse_docx(doc_path) + + # 切分为TextUnit,只取前2个 + textunits = parser.split_into_textunits(doc, max_length=config.MAX_TEXTUNIT_LENGTH)[:2] + for tu in textunits: + tu["doc_id"] = doc.get("file_path", "") + tu["id"] = f"{doc.get('file_path', '')}_{tu.get('paragraph_index', 0)}" + + logger.info(f"准备处理 {len(textunits)} 个TextUnit") + + # 构建知识图谱(只处理这2个) + logger.info("开始构建知识图谱...") + indexer = GraphIndexer(config=config) + kg_graph = indexer.index(textunits, use_verification=True) + + logger.info("=" * 60) + logger.info("快速测试完成!") + logger.info(f"节点数: {kg_graph.number_of_nodes()}") + logger.info(f"边数: {kg_graph.number_of_edges()}") + logger.info("=" * 60) + +if __name__ == "__main__": + main() + + diff --git a/dofile/graphrag_pipeline/tests/test_siliconflow.py b/dofile/graphrag_pipeline/tests/test_siliconflow.py new file mode 100644 index 0000000..4ab13fb --- /dev/null +++ b/dofile/graphrag_pipeline/tests/test_siliconflow.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +""" +测试硅基流动API配置 +""" + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.utils.llm_client import LLMClient, LLMProvider +from src.utils.config import load_config + + +def test_siliconflow(): + """测试硅基流动API""" + print("=" * 60) + print("测试硅基流动API配置") + print("=" * 60) + + # 加载配置 + config = load_config() + + # 检查API密钥 + if not config.SILICONFLOW_API_KEY: + print("❌ 错误: SILICONFLOW_API_KEY 未配置") + print("请在 .env 文件中设置 SILICONFLOW_API_KEY") + return False + + print(f"✅ API密钥已配置: {config.SILICONFLOW_API_KEY[:20]}...") + print(f"✅ API Base URL: {config.SILICONFLOW_API_BASE}") + + # 创建客户端(使用一个常见的模型,用户需要根据实际可用模型调整) + print("\n尝试创建客户端...") + try: + # 注意:这里使用的模型名称需要根据硅基流动平台实际可用模型调整 + # 常用的模型包括:Qwen/Qwen2.5-72B-Instruct, meta-llama/Llama-3.1-70B-Instruct 等 + client = LLMClient( + provider=LLMProvider.SILICONFLOW, + model="Qwen/Qwen2.5-72B-Instruct", # 请根据实际情况修改模型名称 + config=config + ) + print("✅ 客户端创建成功") + except Exception as e: + print(f"❌ 客户端创建失败: {e}") + return False + + # 测试简单对话 + print("\n测试API调用...") + messages = [ + {"role": "user", "content": "请用一句话介绍知识图谱。"} + ] + + try: + response = client.chat(messages) + print(f"✅ API调用成功") + print(f"\n响应内容:\n{response}") + return True + except Exception as e: + print(f"❌ API调用失败: {e}") + print("\n提示:") + print("1. 请检查API密钥是否正确") + print("2. 请检查网络连接") + print("3. 请确认模型名称是否在硅基流动平台可用") + print("4. 可以访问 https://siliconflow.cn/ 查看可用模型列表") + return False + + +if __name__ == "__main__": + success = test_siliconflow() + if success: + print("\n" + "=" * 60) + print("✅ 硅基流动API配置测试通过!") + print("=" * 60) + else: + print("\n" + "=" * 60) + print("❌ 硅基流动API配置测试失败,请检查配置") + print("=" * 60) + sys.exit(1) + + diff --git a/dofile/graphrag_pipeline/uv.lock b/dofile/graphrag_pipeline/uv.lock new file mode 100644 index 0000000..0ce0fa2 --- /dev/null +++ b/dofile/graphrag_pipeline/uv.lock @@ -0,0 +1,2379 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994, upload-time = "2025-10-28T20:59:39.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/9b/01f00e9856d0a73260e86dd8ed0c2234a466c5c1712ce1c281548df39777/aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b", size = 737623, upload-time = "2025-10-28T20:56:30.797Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1b/4be39c445e2b2bd0aab4ba736deb649fabf14f6757f405f0c9685019b9e9/aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc", size = 492664, upload-time = "2025-10-28T20:56:32.708Z" }, + { url = "https://files.pythonhosted.org/packages/28/66/d35dcfea8050e131cdd731dff36434390479b4045a8d0b9d7111b0a968f1/aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7", size = 491808, upload-time = "2025-10-28T20:56:34.57Z" }, + { url = "https://files.pythonhosted.org/packages/00/29/8e4609b93e10a853b65f8291e64985de66d4f5848c5637cddc70e98f01f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb", size = 1738863, upload-time = "2025-10-28T20:56:36.377Z" }, + { url = "https://files.pythonhosted.org/packages/9d/fa/4ebdf4adcc0def75ced1a0d2d227577cd7b1b85beb7edad85fcc87693c75/aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3", size = 1700586, upload-time = "2025-10-28T20:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/da/04/73f5f02ff348a3558763ff6abe99c223381b0bace05cd4530a0258e52597/aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f", size = 1768625, upload-time = "2025-10-28T20:56:39.75Z" }, + { url = "https://files.pythonhosted.org/packages/f8/49/a825b79ffec124317265ca7d2344a86bcffeb960743487cb11988ffb3494/aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6", size = 1867281, upload-time = "2025-10-28T20:56:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/b9/48/adf56e05f81eac31edcfae45c90928f4ad50ef2e3ea72cb8376162a368f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e", size = 1752431, upload-time = "2025-10-28T20:56:43.162Z" }, + { url = "https://files.pythonhosted.org/packages/30/ab/593855356eead019a74e862f21523db09c27f12fd24af72dbc3555b9bfd9/aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7", size = 1562846, upload-time = "2025-10-28T20:56:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/39/0f/9f3d32271aa8dc35036e9668e31870a9d3b9542dd6b3e2c8a30931cb27ae/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d", size = 1699606, upload-time = "2025-10-28T20:56:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3c/52d2658c5699b6ef7692a3f7128b2d2d4d9775f2a68093f74bca06cf01e1/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b", size = 1720663, upload-time = "2025-10-28T20:56:48.528Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d4/8f8f3ff1fb7fb9e3f04fcad4e89d8a1cd8fc7d05de67e3de5b15b33008ff/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8", size = 1737939, upload-time = "2025-10-28T20:56:50.77Z" }, + { url = "https://files.pythonhosted.org/packages/03/d3/ddd348f8a27a634daae39a1b8e291ff19c77867af438af844bf8b7e3231b/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16", size = 1555132, upload-time = "2025-10-28T20:56:52.568Z" }, + { url = "https://files.pythonhosted.org/packages/39/b8/46790692dc46218406f94374903ba47552f2f9f90dad554eed61bfb7b64c/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169", size = 1764802, upload-time = "2025-10-28T20:56:54.292Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e4/19ce547b58ab2a385e5f0b8aa3db38674785085abcf79b6e0edd1632b12f/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248", size = 1719512, upload-time = "2025-10-28T20:56:56.428Z" }, + { url = "https://files.pythonhosted.org/packages/70/30/6355a737fed29dcb6dfdd48682d5790cb5eab050f7b4e01f49b121d3acad/aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e", size = 426690, upload-time = "2025-10-28T20:56:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/b10ac09069973d112de6ef980c1f6bb31cb7dcd0bc363acbdad58f927873/aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45", size = 453465, upload-time = "2025-10-28T20:57:00.795Z" }, + { url = "https://files.pythonhosted.org/packages/bf/78/7e90ca79e5aa39f9694dcfd74f4720782d3c6828113bb1f3197f7e7c4a56/aiohttp-3.13.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7519bdc7dfc1940d201651b52bf5e03f5503bda45ad6eacf64dda98be5b2b6be", size = 732139, upload-time = "2025-10-28T20:57:02.455Z" }, + { url = "https://files.pythonhosted.org/packages/db/ed/1f59215ab6853fbaa5c8495fa6cbc39edfc93553426152b75d82a5f32b76/aiohttp-3.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:088912a78b4d4f547a1f19c099d5a506df17eacec3c6f4375e2831ec1d995742", size = 490082, upload-time = "2025-10-28T20:57:04.784Z" }, + { url = "https://files.pythonhosted.org/packages/68/7b/fe0fe0f5e05e13629d893c760465173a15ad0039c0a5b0d0040995c8075e/aiohttp-3.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5276807b9de9092af38ed23ce120539ab0ac955547b38563a9ba4f5b07b95293", size = 489035, upload-time = "2025-10-28T20:57:06.894Z" }, + { url = "https://files.pythonhosted.org/packages/d2/04/db5279e38471b7ac801d7d36a57d1230feeee130bbe2a74f72731b23c2b1/aiohttp-3.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1237c1375eaef0db4dcd7c2559f42e8af7b87ea7d295b118c60c36a6e61cb811", size = 1720387, upload-time = "2025-10-28T20:57:08.685Z" }, + { url = "https://files.pythonhosted.org/packages/31/07/8ea4326bd7dae2bd59828f69d7fdc6e04523caa55e4a70f4a8725a7e4ed2/aiohttp-3.13.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96581619c57419c3d7d78703d5b78c1e5e5fc0172d60f555bdebaced82ded19a", size = 1688314, upload-time = "2025-10-28T20:57:10.693Z" }, + { url = "https://files.pythonhosted.org/packages/48/ab/3d98007b5b87ffd519d065225438cc3b668b2f245572a8cb53da5dd2b1bc/aiohttp-3.13.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2713a95b47374169409d18103366de1050fe0ea73db358fc7a7acb2880422d4", size = 1756317, upload-time = "2025-10-28T20:57:12.563Z" }, + { url = "https://files.pythonhosted.org/packages/97/3d/801ca172b3d857fafb7b50c7c03f91b72b867a13abca982ed6b3081774ef/aiohttp-3.13.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:228a1cd556b3caca590e9511a89444925da87d35219a49ab5da0c36d2d943a6a", size = 1858539, upload-time = "2025-10-28T20:57:14.623Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0d/4764669bdf47bd472899b3d3db91fffbe925c8e3038ec591a2fd2ad6a14d/aiohttp-3.13.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6cde5fba8d7d8c6ac963dbb0256a9854e9fafff52fbcc58fdf819357892c3e", size = 1739597, upload-time = "2025-10-28T20:57:16.399Z" }, + { url = "https://files.pythonhosted.org/packages/c4/52/7bd3c6693da58ba16e657eb904a5b6decfc48ecd06e9ac098591653b1566/aiohttp-3.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2bef8237544f4e42878c61cef4e2839fee6346dc60f5739f876a9c50be7fcdb", size = 1555006, upload-time = "2025-10-28T20:57:18.288Z" }, + { url = "https://files.pythonhosted.org/packages/48/30/9586667acec5993b6f41d2ebcf96e97a1255a85f62f3c653110a5de4d346/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:16f15a4eac3bc2d76c45f7ebdd48a65d41b242eb6c31c2245463b40b34584ded", size = 1683220, upload-time = "2025-10-28T20:57:20.241Z" }, + { url = "https://files.pythonhosted.org/packages/71/01/3afe4c96854cfd7b30d78333852e8e851dceaec1c40fd00fec90c6402dd2/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bb7fb776645af5cc58ab804c58d7eba545a97e047254a52ce89c157b5af6cd0b", size = 1712570, upload-time = "2025-10-28T20:57:22.253Z" }, + { url = "https://files.pythonhosted.org/packages/11/2c/22799d8e720f4697a9e66fd9c02479e40a49de3de2f0bbe7f9f78a987808/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b4951125ec10c70802f2cb09736c895861cd39fd9dcb35107b4dc8ae6220b8", size = 1733407, upload-time = "2025-10-28T20:57:24.37Z" }, + { url = "https://files.pythonhosted.org/packages/34/cb/90f15dd029f07cebbd91f8238a8b363978b530cd128488085b5703683594/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:550bf765101ae721ee1d37d8095f47b1f220650f85fe1af37a90ce75bab89d04", size = 1550093, upload-time = "2025-10-28T20:57:26.257Z" }, + { url = "https://files.pythonhosted.org/packages/69/46/12dce9be9d3303ecbf4d30ad45a7683dc63d90733c2d9fe512be6716cd40/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fe91b87fc295973096251e2d25a811388e7d8adf3bd2b97ef6ae78bc4ac6c476", size = 1758084, upload-time = "2025-10-28T20:57:28.349Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c8/0932b558da0c302ffd639fc6362a313b98fdf235dc417bc2493da8394df7/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0c8e31cfcc4592cb200160344b2fb6ae0f9e4effe06c644b5a125d4ae5ebe23", size = 1716987, upload-time = "2025-10-28T20:57:30.233Z" }, + { url = "https://files.pythonhosted.org/packages/5d/8b/f5bd1a75003daed099baec373aed678f2e9b34f2ad40d85baa1368556396/aiohttp-3.13.2-cp313-cp313-win32.whl", hash = "sha256:0740f31a60848d6edb296a0df827473eede90c689b8f9f2a4cdde74889eb2254", size = 425859, upload-time = "2025-10-28T20:57:32.105Z" }, + { url = "https://files.pythonhosted.org/packages/5d/28/a8a9fc6957b2cee8902414e41816b5ab5536ecf43c3b1843c10e82c559b2/aiohttp-3.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:a88d13e7ca367394908f8a276b89d04a3652044612b9a408a0bb22a5ed976a1a", size = 452192, upload-time = "2025-10-28T20:57:34.166Z" }, + { url = "https://files.pythonhosted.org/packages/9b/36/e2abae1bd815f01c957cbf7be817b3043304e1c87bad526292a0410fdcf9/aiohttp-3.13.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2475391c29230e063ef53a66669b7b691c9bfc3f1426a0f7bcdf1216bdbac38b", size = 735234, upload-time = "2025-10-28T20:57:36.415Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e3/1ee62dde9b335e4ed41db6bba02613295a0d5b41f74a783c142745a12763/aiohttp-3.13.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f33c8748abef4d8717bb20e8fb1b3e07c6adacb7fd6beaae971a764cf5f30d61", size = 490733, upload-time = "2025-10-28T20:57:38.205Z" }, + { url = "https://files.pythonhosted.org/packages/1a/aa/7a451b1d6a04e8d15a362af3e9b897de71d86feac3babf8894545d08d537/aiohttp-3.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ae32f24bbfb7dbb485a24b30b1149e2f200be94777232aeadba3eecece4d0aa4", size = 491303, upload-time = "2025-10-28T20:57:40.122Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/209958dbb9b01174870f6a7538cd1f3f28274fdbc88a750c238e2c456295/aiohttp-3.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7f02042c1f009ffb70067326ef183a047425bb2ff3bc434ead4dd4a4a66a2b", size = 1717965, upload-time = "2025-10-28T20:57:42.28Z" }, + { url = "https://files.pythonhosted.org/packages/08/aa/6a01848d6432f241416bc4866cae8dc03f05a5a884d2311280f6a09c73d6/aiohttp-3.13.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93655083005d71cd6c072cdab54c886e6570ad2c4592139c3fb967bfc19e4694", size = 1667221, upload-time = "2025-10-28T20:57:44.869Z" }, + { url = "https://files.pythonhosted.org/packages/87/4f/36c1992432d31bbc789fa0b93c768d2e9047ec8c7177e5cd84ea85155f36/aiohttp-3.13.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0db1e24b852f5f664cd728db140cf11ea0e82450471232a394b3d1a540b0f906", size = 1757178, upload-time = "2025-10-28T20:57:47.216Z" }, + { url = "https://files.pythonhosted.org/packages/ac/b4/8e940dfb03b7e0f68a82b88fd182b9be0a65cb3f35612fe38c038c3112cf/aiohttp-3.13.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b009194665bcd128e23eaddef362e745601afa4641930848af4c8559e88f18f9", size = 1838001, upload-time = "2025-10-28T20:57:49.337Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ef/39f3448795499c440ab66084a9db7d20ca7662e94305f175a80f5b7e0072/aiohttp-3.13.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c038a8fdc8103cd51dbd986ecdce141473ffd9775a7a8057a6ed9c3653478011", size = 1716325, upload-time = "2025-10-28T20:57:51.327Z" }, + { url = "https://files.pythonhosted.org/packages/d7/51/b311500ffc860b181c05d91c59a1313bdd05c82960fdd4035a15740d431e/aiohttp-3.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66bac29b95a00db411cd758fea0e4b9bdba6d549dfe333f9a945430f5f2cc5a6", size = 1547978, upload-time = "2025-10-28T20:57:53.554Z" }, + { url = "https://files.pythonhosted.org/packages/31/64/b9d733296ef79815226dab8c586ff9e3df41c6aff2e16c06697b2d2e6775/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4ebf9cfc9ba24a74cf0718f04aac2a3bbe745902cc7c5ebc55c0f3b5777ef213", size = 1682042, upload-time = "2025-10-28T20:57:55.617Z" }, + { url = "https://files.pythonhosted.org/packages/3f/30/43d3e0f9d6473a6db7d472104c4eff4417b1e9df01774cb930338806d36b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a4b88ebe35ce54205c7074f7302bd08a4cb83256a3e0870c72d6f68a3aaf8e49", size = 1680085, upload-time = "2025-10-28T20:57:57.59Z" }, + { url = "https://files.pythonhosted.org/packages/16/51/c709f352c911b1864cfd1087577760ced64b3e5bee2aa88b8c0c8e2e4972/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:98c4fb90bb82b70a4ed79ca35f656f4281885be076f3f970ce315402b53099ae", size = 1728238, upload-time = "2025-10-28T20:57:59.525Z" }, + { url = "https://files.pythonhosted.org/packages/19/e2/19bd4c547092b773caeb48ff5ae4b1ae86756a0ee76c16727fcfd281404b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:ec7534e63ae0f3759df3a1ed4fa6bc8f75082a924b590619c0dd2f76d7043caa", size = 1544395, upload-time = "2025-10-28T20:58:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/cf/87/860f2803b27dfc5ed7be532832a3498e4919da61299b4a1f8eb89b8ff44d/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5b927cf9b935a13e33644cbed6c8c4b2d0f25b713d838743f8fe7191b33829c4", size = 1742965, upload-time = "2025-10-28T20:58:03.972Z" }, + { url = "https://files.pythonhosted.org/packages/67/7f/db2fc7618925e8c7a601094d5cbe539f732df4fb570740be88ed9e40e99a/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:88d6c017966a78c5265d996c19cdb79235be5e6412268d7e2ce7dee339471b7a", size = 1697585, upload-time = "2025-10-28T20:58:06.189Z" }, + { url = "https://files.pythonhosted.org/packages/0c/07/9127916cb09bb38284db5036036042b7b2c514c8ebaeee79da550c43a6d6/aiohttp-3.13.2-cp314-cp314-win32.whl", hash = "sha256:f7c183e786e299b5d6c49fb43a769f8eb8e04a2726a2bd5887b98b5cc2d67940", size = 431621, upload-time = "2025-10-28T20:58:08.636Z" }, + { url = "https://files.pythonhosted.org/packages/fb/41/554a8a380df6d3a2bba8a7726429a23f4ac62aaf38de43bb6d6cde7b4d4d/aiohttp-3.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:fe242cd381e0fb65758faf5ad96c2e460df6ee5b2de1072fe97e4127927e00b4", size = 457627, upload-time = "2025-10-28T20:58:11Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8e/3824ef98c039d3951cb65b9205a96dd2b20f22241ee17d89c5701557c826/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f10d9c0b0188fe85398c61147bbd2a657d616c876863bfeff43376e0e3134673", size = 767360, upload-time = "2025-10-28T20:58:13.358Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0f/6a03e3fc7595421274fa34122c973bde2d89344f8a881b728fa8c774e4f1/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e7c952aefdf2460f4ae55c5e9c3e80aa72f706a6317e06020f80e96253b1accd", size = 504616, upload-time = "2025-10-28T20:58:15.339Z" }, + { url = "https://files.pythonhosted.org/packages/c6/aa/ed341b670f1bc8a6f2c6a718353d13b9546e2cef3544f573c6a1ff0da711/aiohttp-3.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c20423ce14771d98353d2e25e83591fa75dfa90a3c1848f3d7c68243b4fbded3", size = 509131, upload-time = "2025-10-28T20:58:17.693Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f0/c68dac234189dae5c4bbccc0f96ce0cc16b76632cfc3a08fff180045cfa4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e96eb1a34396e9430c19d8338d2ec33015e4a87ef2b4449db94c22412e25ccdf", size = 1864168, upload-time = "2025-10-28T20:58:20.113Z" }, + { url = "https://files.pythonhosted.org/packages/8f/65/75a9a76db8364b5d0e52a0c20eabc5d52297385d9af9c35335b924fafdee/aiohttp-3.13.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23fb0783bc1a33640036465019d3bba069942616a6a2353c6907d7fe1ccdaf4e", size = 1719200, upload-time = "2025-10-28T20:58:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/f5/55/8df2ed78d7f41d232f6bd3ff866b6f617026551aa1d07e2f03458f964575/aiohttp-3.13.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1a9bea6244a1d05a4e57c295d69e159a5c50d8ef16aa390948ee873478d9a5", size = 1843497, upload-time = "2025-10-28T20:58:24.672Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e0/94d7215e405c5a02ccb6a35c7a3a6cfff242f457a00196496935f700cde5/aiohttp-3.13.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a3d54e822688b56e9f6b5816fb3de3a3a64660efac64e4c2dc435230ad23bad", size = 1935703, upload-time = "2025-10-28T20:58:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/0b/78/1eeb63c3f9b2d1015a4c02788fb543141aad0a03ae3f7a7b669b2483f8d4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a653d872afe9f33497215745da7a943d1dc15b728a9c8da1c3ac423af35178e", size = 1792738, upload-time = "2025-10-28T20:58:29.787Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/aaf1eea4c188e51538c04cc568040e3082db263a57086ea74a7d38c39e42/aiohttp-3.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56d36e80d2003fa3fc0207fac644216d8532e9504a785ef9a8fd013f84a42c61", size = 1624061, upload-time = "2025-10-28T20:58:32.529Z" }, + { url = "https://files.pythonhosted.org/packages/9b/c2/3b6034de81fbcc43de8aeb209073a2286dfb50b86e927b4efd81cf848197/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:78cd586d8331fb8e241c2dd6b2f4061778cc69e150514b39a9e28dd050475661", size = 1789201, upload-time = "2025-10-28T20:58:34.618Z" }, + { url = "https://files.pythonhosted.org/packages/c9/38/c15dcf6d4d890217dae79d7213988f4e5fe6183d43893a9cf2fe9e84ca8d/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:20b10bbfbff766294fe99987f7bb3b74fdd2f1a2905f2562132641ad434dcf98", size = 1776868, upload-time = "2025-10-28T20:58:38.835Z" }, + { url = "https://files.pythonhosted.org/packages/04/75/f74fd178ac81adf4f283a74847807ade5150e48feda6aef024403716c30c/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9ec49dff7e2b3c85cdeaa412e9d438f0ecd71676fde61ec57027dd392f00c693", size = 1790660, upload-time = "2025-10-28T20:58:41.507Z" }, + { url = "https://files.pythonhosted.org/packages/e7/80/7368bd0d06b16b3aba358c16b919e9c46cf11587dc572091031b0e9e3ef0/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:94f05348c4406450f9d73d38efb41d669ad6cd90c7ee194810d0eefbfa875a7a", size = 1617548, upload-time = "2025-10-28T20:58:43.674Z" }, + { url = "https://files.pythonhosted.org/packages/7d/4b/a6212790c50483cb3212e507378fbe26b5086d73941e1ec4b56a30439688/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:fa4dcb605c6f82a80c7f95713c2b11c3b8e9893b3ebd2bc9bde93165ed6107be", size = 1817240, upload-time = "2025-10-28T20:58:45.787Z" }, + { url = "https://files.pythonhosted.org/packages/ff/f7/ba5f0ba4ea8d8f3c32850912944532b933acbf0f3a75546b89269b9b7dde/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf00e5db968c3f67eccd2778574cf64d8b27d95b237770aa32400bd7a1ca4f6c", size = 1762334, upload-time = "2025-10-28T20:58:47.936Z" }, + { url = "https://files.pythonhosted.org/packages/7e/83/1a5a1856574588b1cad63609ea9ad75b32a8353ac995d830bf5da9357364/aiohttp-3.13.2-cp314-cp314t-win32.whl", hash = "sha256:d23b5fe492b0805a50d3371e8a728a9134d8de5447dce4c885f5587294750734", size = 464685, upload-time = "2025-10-28T20:58:50.642Z" }, + { url = "https://files.pythonhosted.org/packages/9f/4d/d22668674122c08f4d56972297c51a624e64b3ed1efaa40187607a7cb66e/aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f", size = 498093, upload-time = "2025-10-28T20:58:52.782Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anthropic" +version = "0.72.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/07/61f3ca8e69c5dcdaec31b36b79a53ea21c5b4ca5e93c7df58c71f43bf8d8/anthropic-0.72.0.tar.gz", hash = "sha256:8971fe76dcffc644f74ac3883069beb1527641115ae0d6eb8fa21c1ce4082f7a", size = 493721, upload-time = "2025-10-28T19:13:01.755Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/b7/160d4fb30080395b4143f1d1a4f6c646ba9105561108d2a434b606c03579/anthropic-0.72.0-py3-none-any.whl", hash = "sha256:0e9f5a7582f038cab8efbb4c959e49ef654a56bfc7ba2da51b5a7b8a84de2e4d", size = 357464, upload-time = "2025-10-28T19:13:00.215Z" }, +] + +[[package]] +name = "anyio" +version = "4.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "black" +version = "25.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/8e/319cfe6c82f7e2d5bfb4d3353c6cc85b523d677ff59edc61fdb9ee275234/black-25.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1b9dc70c21ef8b43248f1d86aedd2aaf75ae110b958a7909ad8463c4aa0880b0", size = 1742012, upload-time = "2025-09-19T00:33:08.678Z" }, + { url = "https://files.pythonhosted.org/packages/94/cc/f562fe5d0a40cd2a4e6ae3f685e4c36e365b1f7e494af99c26ff7f28117f/black-25.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e46eecf65a095fa62e53245ae2795c90bdecabd53b50c448d0a8bcd0d2e74c4", size = 1581421, upload-time = "2025-09-19T00:35:25.937Z" }, + { url = "https://files.pythonhosted.org/packages/84/67/6db6dff1ebc8965fd7661498aea0da5d7301074b85bba8606a28f47ede4d/black-25.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9101ee58ddc2442199a25cb648d46ba22cd580b00ca4b44234a324e3ec7a0f7e", size = 1655619, upload-time = "2025-09-19T00:30:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/10/10/3faef9aa2a730306cf469d76f7f155a8cc1f66e74781298df0ba31f8b4c8/black-25.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:77e7060a00c5ec4b3367c55f39cf9b06e68965a4f2e61cecacd6d0d9b7ec945a", size = 1342481, upload-time = "2025-09-19T00:31:29.625Z" }, + { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, +] + +[[package]] +name = "cachetools" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201", size = 31325, upload-time = "2025-10-12T14:55:30.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701", size = 11280, upload-time = "2025-10-12T14:55:28.382Z" }, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "click" +version = "8.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, +] + +[[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 = "cryptography" +version = "46.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" }, + { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" }, + { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" }, + { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" }, + { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" }, + { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" }, + { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" }, + { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" }, + { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, +] + +[[package]] +name = "dashscope" +version = "1.24.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "certifi" }, + { name = "cryptography" }, + { name = "requests" }, + { name = "websocket-client" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/53/845b2fc4e0c4a130428c4ecfbaff9e569834315e1ac71cae2cca8d20f459/dashscope-1.24.9-py3-none-any.whl", hash = "sha256:720120ab52b364f15d0b68af4872d99e6cfcd58f8cd876e897a4173df33db146", size = 1311042, upload-time = "2025-10-29T09:50:40.743Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, +] + +[[package]] +name = "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.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2025.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hanlp" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hanlp-common" }, + { name = "hanlp-downloader" }, + { name = "hanlp-trie" }, + { name = "pynvml" }, + { name = "sentencepiece" }, + { name = "termcolor" }, + { name = "toposort" }, + { name = "torch" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/03/f91915b2f16f3334179f5b9ea0413c64172f4634c842c474809a9062f63e/hanlp-2.1.3.tar.gz", hash = "sha256:12d0c391065cf87db8854b41277b99ae4651ddaf407a95987fa1f057a50d1772", size = 503791, upload-time = "2025-10-19T02:48:37.38Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/2d/197bdfcc4b6b99045060a23edd8864c3a72a86718fb305a84ad55e5a4bcf/hanlp-2.1.3-py3-none-any.whl", hash = "sha256:07aaeec6740416857e2bad3c342633fe9d3bbe1793d29d3e2b2070a8cad970e7", size = 654066, upload-time = "2025-10-19T02:48:35.357Z" }, +] + +[[package]] +name = "hanlp-common" +version = "0.0.23" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "phrasetree" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/c7/ae20a89dce88f1534f91d8045092dcd8fa6d9f51d859e5a5e5c818d003cf/hanlp_common-0.0.23.tar.gz", hash = "sha256:d7d7ab23a00c65cb38585b523812782e7dd46b3d7a881f5071f7ec407ee74594", size = 28930, upload-time = "2025-01-13T00:54:21.08Z" } + +[[package]] +name = "hanlp-downloader" +version = "0.0.25" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/4d/fef63be131c99e7f42f866175cc47be834ae0b5afe6f3e38a5b3d090fc2b/hanlp_downloader-0.0.25.tar.gz", hash = "sha256:11081cd55d8ad84ba909fe870dd10ef5e26b720d254f73f800b281cfb6c5d1b4", size = 13873, upload-time = "2021-09-04T19:17:00.182Z" } + +[[package]] +name = "hanlp-trie" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hanlp-common" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/7a/134f5fbbb449023508508b35c9d2ab5f1ddd15c213d839892bf1141d1ad1/hanlp_trie-0.0.5.tar.gz", hash = "sha256:c60f67e1d1492365d27a23333b46540409804bbf98bd92962b5604db55939e59", size = 6690, upload-time = "2022-04-30T19:53:43.98Z" } + +[[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 = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "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 = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "igraph" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "texttable" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/be/56bef1919005b4caf1f71522b300d359f7faeb7ae93a3b0baa9b4f146a87/igraph-1.0.0.tar.gz", hash = "sha256:2414d0be2e4d77ee5357807d100974b40f6082bb1bb71988ec46cfb6728651ee", size = 5077105, upload-time = "2025-10-23T12:22:50.127Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/03/3278ad0ceb3ea0e84d8ae3a85bdded4d0e57853aeb802a200feb43847b93/igraph-1.0.0-cp39-abi3-macosx_10_15_x86_64.whl", hash = "sha256:c2cbc415e02523e5a241eecee82319080bf928a70b1ba299f3b3e25bf029b6d4", size = 2257415, upload-time = "2025-10-23T12:22:27.246Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bc/6281ec7f9baaf71ee57c3b1748da2d3148d15d253e1a03006f204aa68ca5/igraph-1.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a27753cd80680a8f676c2d5a467aaa4a95e510b30748398ec4e4aeb982130e8", size = 2048555, upload-time = "2025-10-23T12:22:29.49Z" }, + { url = "https://files.pythonhosted.org/packages/2a/38/3cd6428a4ed4c09a56df05998438e7774fd1d799ee4fb8fc481674f5f7fc/igraph-1.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a55dc3a2a4e3fc3eba42479910c1511bfc3ecb33cdf5f0406891fd85f14b5aee", size = 5314141, upload-time = "2025-10-23T12:22:31.023Z" }, + { url = "https://files.pythonhosted.org/packages/7d/da/dd2867c25adbb41563720f14b5fc895c98bf88be682a3faff4f7b3118d2a/igraph-1.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d04c2c76f686fb1f554ee35dfd3085f5e73b7965ba6b4cf06d53e66b1955522", size = 5683134, upload-time = "2025-10-23T12:22:32.423Z" }, + { url = "https://files.pythonhosted.org/packages/e5/40/243c118d34ab80382d7009c4dcb99b887384c3d2ce84d29eeac19e2a007a/igraph-1.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2b52dc1757fff0fed29a9f7a276d971a11db4211569ed78b9eab36288dfcc9d", size = 6211583, upload-time = "2025-10-23T12:22:34.238Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b7/88f433819c54b496cb0315fce28e658970cb20ff5dbd52a5a605ce2888de/igraph-1.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:05c79a2a8fca695b2f217a6fa7f2549f896f757d4db41be32a055400cb19cc30", size = 6594509, upload-time = "2025-10-23T12:22:35.831Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5d/8f7f6f619d374e959aa3664ebc4b24c10abc90c2e8efbed97f2623fadaf5/igraph-1.0.0-cp39-abi3-win32.whl", hash = "sha256:c2bce3cd472fec3dd9c4d8a3ea5b6b9be65fb30edf760beb4850760dd4f2d479", size = 2725406, upload-time = "2025-10-23T12:22:37.588Z" }, + { url = "https://files.pythonhosted.org/packages/af/77/a85b3745cf40a0572bae2de8cd9c2a2a8af78e5cf3e880fc0a249114e609/igraph-1.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:faeff8ede0cf15eb4ded44b0fcea6e1886740146e60504c24ad2da14e0939563", size = 3221663, upload-time = "2025-10-23T12:22:39.404Z" }, + { url = "https://files.pythonhosted.org/packages/ef/7e/5df541c37bdf6493035e89c22bd53f30d99b291bcda6c78e9a8afeecec2b/igraph-1.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:b607cafc24b10a615e713ee96e58208ef27e0764af80140c7cc45d4724a3f2df", size = 2785701, upload-time = "2025-10-23T12:22:41.03Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[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 = "jiter" +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/68/0357982493a7b20925aece061f7fb7a2678e3b232f8d73a6edb7e5304443/jiter-0.11.1.tar.gz", hash = "sha256:849dcfc76481c0ea0099391235b7ca97d7279e0fa4c86005457ac7c88e8b76dc", size = 168385, upload-time = "2025-10-17T11:31:15.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/8b/318e8af2c904a9d29af91f78c1e18f0592e189bbdb8a462902d31fe20682/jiter-0.11.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c92148eec91052538ce6823dfca9525f5cfc8b622d7f07e9891a280f61b8c96c", size = 305655, upload-time = "2025-10-17T11:29:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/f7/29/6c7de6b5d6e511d9e736312c0c9bfcee8f9b6bef68182a08b1d78767e627/jiter-0.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ecd4da91b5415f183a6be8f7158d127bdd9e6a3174138293c0d48d6ea2f2009d", size = 315645, upload-time = "2025-10-17T11:29:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5f/ef9e5675511ee0eb7f98dd8c90509e1f7743dbb7c350071acae87b0145f3/jiter-0.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7e3ac25c00b9275684d47aa42febaa90a9958e19fd1726c4ecf755fbe5e553b", size = 348003, upload-time = "2025-10-17T11:29:22.712Z" }, + { url = "https://files.pythonhosted.org/packages/56/1b/abe8c4021010b0a320d3c62682769b700fb66f92c6db02d1a1381b3db025/jiter-0.11.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57d7305c0a841858f866cd459cd9303f73883fb5e097257f3d4a3920722c69d4", size = 365122, upload-time = "2025-10-17T11:29:24.408Z" }, + { url = "https://files.pythonhosted.org/packages/2a/2d/4a18013939a4f24432f805fbd5a19893e64650b933edb057cd405275a538/jiter-0.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e86fa10e117dce22c547f31dd6d2a9a222707d54853d8de4e9a2279d2c97f239", size = 488360, upload-time = "2025-10-17T11:29:25.724Z" }, + { url = "https://files.pythonhosted.org/packages/f0/77/38124f5d02ac4131f0dfbcfd1a19a0fac305fa2c005bc4f9f0736914a1a4/jiter-0.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae5ef1d48aec7e01ee8420155d901bb1d192998fa811a65ebb82c043ee186711", size = 376884, upload-time = "2025-10-17T11:29:27.056Z" }, + { url = "https://files.pythonhosted.org/packages/7b/43/59fdc2f6267959b71dd23ce0bd8d4aeaf55566aa435a5d00f53d53c7eb24/jiter-0.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb68e7bf65c990531ad8715e57d50195daf7c8e6f1509e617b4e692af1108939", size = 358827, upload-time = "2025-10-17T11:29:28.698Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d0/b3cc20ff5340775ea3bbaa0d665518eddecd4266ba7244c9cb480c0c82ec/jiter-0.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43b30c8154ded5845fa454ef954ee67bfccce629b2dea7d01f795b42bc2bda54", size = 385171, upload-time = "2025-10-17T11:29:30.078Z" }, + { url = "https://files.pythonhosted.org/packages/d2/bc/94dd1f3a61f4dc236f787a097360ec061ceeebebf4ea120b924d91391b10/jiter-0.11.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:586cafbd9dd1f3ce6a22b4a085eaa6be578e47ba9b18e198d4333e598a91db2d", size = 518359, upload-time = "2025-10-17T11:29:31.464Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8c/12ee132bd67e25c75f542c227f5762491b9a316b0dad8e929c95076f773c/jiter-0.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:677cc2517d437a83bb30019fd4cf7cad74b465914c56ecac3440d597ac135250", size = 509205, upload-time = "2025-10-17T11:29:32.895Z" }, + { url = "https://files.pythonhosted.org/packages/39/d5/9de848928ce341d463c7e7273fce90ea6d0ea4343cd761f451860fa16b59/jiter-0.11.1-cp312-cp312-win32.whl", hash = "sha256:fa992af648fcee2b850a3286a35f62bbbaeddbb6dbda19a00d8fbc846a947b6e", size = 205448, upload-time = "2025-10-17T11:29:34.217Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b0/8002d78637e05009f5e3fb5288f9d57d65715c33b5d6aa20fd57670feef5/jiter-0.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:88b5cae9fa51efeb3d4bd4e52bfd4c85ccc9cac44282e2a9640893a042ba4d87", size = 204285, upload-time = "2025-10-17T11:29:35.446Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a2/bb24d5587e4dff17ff796716542f663deee337358006a80c8af43ddc11e5/jiter-0.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:9a6cae1ab335551917f882f2c3c1efe7617b71b4c02381e4382a8fc80a02588c", size = 188712, upload-time = "2025-10-17T11:29:37.027Z" }, + { url = "https://files.pythonhosted.org/packages/7c/4b/e4dd3c76424fad02a601d570f4f2a8438daea47ba081201a721a903d3f4c/jiter-0.11.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:71b6a920a5550f057d49d0e8bcc60945a8da998019e83f01adf110e226267663", size = 305272, upload-time = "2025-10-17T11:29:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/67/83/2cd3ad5364191130f4de80eacc907f693723beaab11a46c7d155b07a092c/jiter-0.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b3de72e925388453a5171be83379549300db01284f04d2a6f244d1d8de36f94", size = 314038, upload-time = "2025-10-17T11:29:40.563Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3c/8e67d9ba524e97d2f04c8f406f8769a23205026b13b0938d16646d6e2d3e/jiter-0.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc19dd65a2bd3d9c044c5b4ebf657ca1e6003a97c0fc10f555aa4f7fb9821c00", size = 345977, upload-time = "2025-10-17T11:29:42.009Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a5/489ce64d992c29bccbffabb13961bbb0435e890d7f2d266d1f3df5e917d2/jiter-0.11.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d58faaa936743cd1464540562f60b7ce4fd927e695e8bc31b3da5b914baa9abd", size = 364503, upload-time = "2025-10-17T11:29:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c0/e321dd83ee231d05c8fe4b1a12caf1f0e8c7a949bf4724d58397104f10f2/jiter-0.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:902640c3103625317291cb73773413b4d71847cdf9383ba65528745ff89f1d14", size = 487092, upload-time = "2025-10-17T11:29:44.835Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5e/8f24ec49c8d37bd37f34ec0112e0b1a3b4b5a7b456c8efff1df5e189ad43/jiter-0.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30405f726e4c2ed487b176c09f8b877a957f535d60c1bf194abb8dadedb5836f", size = 376328, upload-time = "2025-10-17T11:29:46.175Z" }, + { url = "https://files.pythonhosted.org/packages/7f/70/ded107620e809327cf7050727e17ccfa79d6385a771b7fe38fb31318ef00/jiter-0.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3217f61728b0baadd2551844870f65219ac4a1285d5e1a4abddff3d51fdabe96", size = 356632, upload-time = "2025-10-17T11:29:47.454Z" }, + { url = "https://files.pythonhosted.org/packages/19/53/c26f7251613f6a9079275ee43c89b8a973a95ff27532c421abc2a87afb04/jiter-0.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1364cc90c03a8196f35f396f84029f12abe925415049204446db86598c8b72c", size = 384358, upload-time = "2025-10-17T11:29:49.377Z" }, + { url = "https://files.pythonhosted.org/packages/84/16/e0f2cc61e9c4d0b62f6c1bd9b9781d878a427656f88293e2a5335fa8ff07/jiter-0.11.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:53a54bf8e873820ab186b2dca9f6c3303f00d65ae5e7b7d6bda1b95aa472d646", size = 517279, upload-time = "2025-10-17T11:29:50.968Z" }, + { url = "https://files.pythonhosted.org/packages/60/5c/4cd095eaee68961bca3081acbe7c89e12ae24a5dae5fd5d2a13e01ed2542/jiter-0.11.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7e29aca023627b0e0c2392d4248f6414d566ff3974fa08ff2ac8dbb96dfee92a", size = 508276, upload-time = "2025-10-17T11:29:52.619Z" }, + { url = "https://files.pythonhosted.org/packages/4f/25/f459240e69b0e09a7706d96ce203ad615ca36b0fe832308d2b7123abf2d0/jiter-0.11.1-cp313-cp313-win32.whl", hash = "sha256:f153e31d8bca11363751e875c0a70b3d25160ecbaee7b51e457f14498fb39d8b", size = 205593, upload-time = "2025-10-17T11:29:53.938Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/461bafe22bae79bab74e217a09c907481a46d520c36b7b9fe71ee8c9e983/jiter-0.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:f773f84080b667c69c4ea0403fc67bb08b07e2b7ce1ef335dea5868451e60fed", size = 203518, upload-time = "2025-10-17T11:29:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/7b/72/c45de6e320edb4fa165b7b1a414193b3cae302dd82da2169d315dcc78b44/jiter-0.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:635ecd45c04e4c340d2187bcb1cea204c7cc9d32c1364d251564bf42e0e39c2d", size = 188062, upload-time = "2025-10-17T11:29:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/4a57922437ca8753ef823f434c2dec5028b237d84fa320f06a3ba1aec6e8/jiter-0.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d892b184da4d94d94ddb4031296931c74ec8b325513a541ebfd6dfb9ae89904b", size = 313814, upload-time = "2025-10-17T11:29:58.509Z" }, + { url = "https://files.pythonhosted.org/packages/76/50/62a0683dadca25490a4bedc6a88d59de9af2a3406dd5a576009a73a1d392/jiter-0.11.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa22c223a3041dacb2fcd37c70dfd648b44662b4a48e242592f95bda5ab09d58", size = 344987, upload-time = "2025-10-17T11:30:00.208Z" }, + { url = "https://files.pythonhosted.org/packages/da/00/2355dbfcbf6cdeaddfdca18287f0f38ae49446bb6378e4a5971e9356fc8a/jiter-0.11.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:330e8e6a11ad4980cd66a0f4a3e0e2e0f646c911ce047014f984841924729789", size = 356399, upload-time = "2025-10-17T11:30:02.084Z" }, + { url = "https://files.pythonhosted.org/packages/c9/07/c2bd748d578fa933d894a55bff33f983bc27f75fc4e491b354bef7b78012/jiter-0.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:09e2e386ebf298547ca3a3704b729471f7ec666c2906c5c26c1a915ea24741ec", size = 203289, upload-time = "2025-10-17T11:30:03.656Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ee/ace64a853a1acbd318eb0ca167bad1cf5ee037207504b83a868a5849747b/jiter-0.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:fe4a431c291157e11cee7c34627990ea75e8d153894365a3bc84b7a959d23ca8", size = 188284, upload-time = "2025-10-17T11:30:05.046Z" }, + { url = "https://files.pythonhosted.org/packages/8d/00/d6006d069e7b076e4c66af90656b63da9481954f290d5eca8c715f4bf125/jiter-0.11.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:0fa1f70da7a8a9713ff8e5f75ec3f90c0c870be6d526aa95e7c906f6a1c8c676", size = 304624, upload-time = "2025-10-17T11:30:06.678Z" }, + { url = "https://files.pythonhosted.org/packages/fc/45/4a0e31eb996b9ccfddbae4d3017b46f358a599ccf2e19fbffa5e531bd304/jiter-0.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:569ee559e5046a42feb6828c55307cf20fe43308e3ae0d8e9e4f8d8634d99944", size = 315042, upload-time = "2025-10-17T11:30:08.87Z" }, + { url = "https://files.pythonhosted.org/packages/e7/91/22f5746f5159a28c76acdc0778801f3c1181799aab196dbea2d29e064968/jiter-0.11.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f69955fa1d92e81987f092b233f0be49d4c937da107b7f7dcf56306f1d3fcce9", size = 346357, upload-time = "2025-10-17T11:30:10.222Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4f/57620857d4e1dc75c8ff4856c90cb6c135e61bff9b4ebfb5dc86814e82d7/jiter-0.11.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:090f4c9d4a825e0fcbd0a2647c9a88a0f366b75654d982d95a9590745ff0c48d", size = 365057, upload-time = "2025-10-17T11:30:11.585Z" }, + { url = "https://files.pythonhosted.org/packages/ce/34/caf7f9cc8ae0a5bb25a5440cc76c7452d264d1b36701b90fdadd28fe08ec/jiter-0.11.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf3d8cedf9e9d825233e0dcac28ff15c47b7c5512fdfe2e25fd5bbb6e6b0cee", size = 487086, upload-time = "2025-10-17T11:30:13.052Z" }, + { url = "https://files.pythonhosted.org/packages/50/17/85b5857c329d533d433fedf98804ebec696004a1f88cabad202b2ddc55cf/jiter-0.11.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2aa9b1958f9c30d3d1a558b75f0626733c60eb9b7774a86b34d88060be1e67fe", size = 376083, upload-time = "2025-10-17T11:30:14.416Z" }, + { url = "https://files.pythonhosted.org/packages/85/d3/2d9f973f828226e6faebdef034097a2918077ea776fb4d88489949024787/jiter-0.11.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42d1ca16590b768c5e7d723055acd2633908baacb3628dd430842e2e035aa90", size = 357825, upload-time = "2025-10-17T11:30:15.765Z" }, + { url = "https://files.pythonhosted.org/packages/f4/55/848d4dabf2c2c236a05468c315c2cb9dc736c5915e65449ccecdba22fb6f/jiter-0.11.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5db4c2486a023820b701a17aec9c5a6173c5ba4393f26662f032f2de9c848b0f", size = 383933, upload-time = "2025-10-17T11:30:17.34Z" }, + { url = "https://files.pythonhosted.org/packages/0b/6c/204c95a4fbb0e26dfa7776c8ef4a878d0c0b215868011cc904bf44f707e2/jiter-0.11.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4573b78777ccfac954859a6eff45cbd9d281d80c8af049d0f1a3d9fc323d5c3a", size = 517118, upload-time = "2025-10-17T11:30:18.684Z" }, + { url = "https://files.pythonhosted.org/packages/88/25/09956644ea5a2b1e7a2a0f665cb69a973b28f4621fa61fc0c0f06ff40a31/jiter-0.11.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:7593ac6f40831d7961cb67633c39b9fef6689a211d7919e958f45710504f52d3", size = 508194, upload-time = "2025-10-17T11:30:20.719Z" }, + { url = "https://files.pythonhosted.org/packages/09/49/4d1657355d7f5c9e783083a03a3f07d5858efa6916a7d9634d07db1c23bd/jiter-0.11.1-cp314-cp314-win32.whl", hash = "sha256:87202ec6ff9626ff5f9351507def98fcf0df60e9a146308e8ab221432228f4ea", size = 203961, upload-time = "2025-10-17T11:30:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/76/bd/f063bd5cc2712e7ca3cf6beda50894418fc0cfeb3f6ff45a12d87af25996/jiter-0.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:a5dd268f6531a182c89d0dd9a3f8848e86e92dfff4201b77a18e6b98aa59798c", size = 202804, upload-time = "2025-10-17T11:30:23.452Z" }, + { url = "https://files.pythonhosted.org/packages/52/ca/4d84193dfafef1020bf0bedd5e1a8d0e89cb67c54b8519040effc694964b/jiter-0.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:5d761f863f912a44748a21b5c4979c04252588ded8d1d2760976d2e42cd8d991", size = 188001, upload-time = "2025-10-17T11:30:24.915Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fa/3b05e5c9d32efc770a8510eeb0b071c42ae93a5b576fd91cee9af91689a1/jiter-0.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2cc5a3965285ddc33e0cab933e96b640bc9ba5940cea27ebbbf6695e72d6511c", size = 312561, upload-time = "2025-10-17T11:30:26.742Z" }, + { url = "https://files.pythonhosted.org/packages/50/d3/335822eb216154ddb79a130cbdce88fdf5c3e2b43dc5dba1fd95c485aaf5/jiter-0.11.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b572b3636a784c2768b2342f36a23078c8d3aa6d8a30745398b1bab58a6f1a8", size = 344551, upload-time = "2025-10-17T11:30:28.252Z" }, + { url = "https://files.pythonhosted.org/packages/31/6d/a0bed13676b1398f9b3ba61f32569f20a3ff270291161100956a577b2dd3/jiter-0.11.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad93e3d67a981f96596d65d2298fe8d1aa649deb5374a2fb6a434410ee11915e", size = 363051, upload-time = "2025-10-17T11:30:30.009Z" }, + { url = "https://files.pythonhosted.org/packages/a4/03/313eda04aa08545a5a04ed5876e52f49ab76a4d98e54578896ca3e16313e/jiter-0.11.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a83097ce379e202dcc3fe3fc71a16d523d1ee9192c8e4e854158f96b3efe3f2f", size = 485897, upload-time = "2025-10-17T11:30:31.429Z" }, + { url = "https://files.pythonhosted.org/packages/5f/13/a1011b9d325e40b53b1b96a17c010b8646013417f3902f97a86325b19299/jiter-0.11.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7042c51e7fbeca65631eb0c332f90c0c082eab04334e7ccc28a8588e8e2804d9", size = 375224, upload-time = "2025-10-17T11:30:33.18Z" }, + { url = "https://files.pythonhosted.org/packages/92/da/1b45026b19dd39b419e917165ff0ea629dbb95f374a3a13d2df95e40a6ac/jiter-0.11.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a68d679c0e47649a61df591660507608adc2652442de7ec8276538ac46abe08", size = 356606, upload-time = "2025-10-17T11:30:34.572Z" }, + { url = "https://files.pythonhosted.org/packages/7a/0c/9acb0e54d6a8ba59ce923a180ebe824b4e00e80e56cefde86cc8e0a948be/jiter-0.11.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1b0da75dbf4b6ec0b3c9e604d1ee8beaf15bc046fff7180f7d89e3cdbd3bb51", size = 384003, upload-time = "2025-10-17T11:30:35.987Z" }, + { url = "https://files.pythonhosted.org/packages/3f/2b/e5a5fe09d6da2145e4eed651e2ce37f3c0cf8016e48b1d302e21fb1628b7/jiter-0.11.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:69dd514bf0fa31c62147d6002e5ca2b3e7ef5894f5ac6f0a19752385f4e89437", size = 516946, upload-time = "2025-10-17T11:30:37.425Z" }, + { url = "https://files.pythonhosted.org/packages/5f/fe/db936e16e0228d48eb81f9934e8327e9fde5185e84f02174fcd22a01be87/jiter-0.11.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:bb31ac0b339efa24c0ca606febd8b77ef11c58d09af1b5f2be4c99e907b11111", size = 507614, upload-time = "2025-10-17T11:30:38.977Z" }, + { url = "https://files.pythonhosted.org/packages/86/db/c4438e8febfb303486d13c6b72f5eb71cf851e300a0c1f0b4140018dd31f/jiter-0.11.1-cp314-cp314t-win32.whl", hash = "sha256:b2ce0d6156a1d3ad41da3eec63b17e03e296b78b0e0da660876fccfada86d2f7", size = 204043, upload-time = "2025-10-17T11:30:40.308Z" }, + { url = "https://files.pythonhosted.org/packages/36/59/81badb169212f30f47f817dfaabf965bc9b8204fed906fab58104ee541f9/jiter-0.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f4db07d127b54c4a2d43b4cf05ff0193e4f73e0dd90c74037e16df0b29f666e1", size = 204046, upload-time = "2025-10-17T11:30:41.692Z" }, + { url = "https://files.pythonhosted.org/packages/dd/01/43f7b4eb61db3e565574c4c5714685d042fb652f9eef7e5a3de6aafa943a/jiter-0.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:28e4fdf2d7ebfc935523e50d1efa3970043cfaa161674fe66f9642409d001dfe", size = 188069, upload-time = "2025-10-17T11:30:43.23Z" }, + { url = "https://files.pythonhosted.org/packages/a6/bc/950dd7f170c6394b6fdd73f989d9e729bd98907bcc4430ef080a72d06b77/jiter-0.11.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:0d4d6993edc83cf75e8c6828a8d6ce40a09ee87e38c7bfba6924f39e1337e21d", size = 302626, upload-time = "2025-10-17T11:31:09.645Z" }, + { url = "https://files.pythonhosted.org/packages/3a/65/43d7971ca82ee100b7b9b520573eeef7eabc0a45d490168ebb9a9b5bb8b2/jiter-0.11.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f78d151c83a87a6cf5461d5ee55bc730dd9ae227377ac6f115b922989b95f838", size = 297034, upload-time = "2025-10-17T11:31:10.975Z" }, + { url = "https://files.pythonhosted.org/packages/19/4c/000e1e0c0c67e96557a279f8969487ea2732d6c7311698819f977abae837/jiter-0.11.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9022974781155cd5521d5cb10997a03ee5e31e8454c9d999dcdccd253f2353f", size = 337328, upload-time = "2025-10-17T11:31:12.399Z" }, + { url = "https://files.pythonhosted.org/packages/d9/71/71408b02c6133153336d29fa3ba53000f1e1a3f78bb2fc2d1a1865d2e743/jiter-0.11.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18c77aaa9117510d5bdc6a946baf21b1f0cfa58ef04d31c8d016f206f2118960", size = 343697, upload-time = "2025-10-17T11:31:13.773Z" }, +] + +[[package]] +name = "law-graph-kg" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "aiohttp" }, + { name = "anthropic" }, + { name = "dashscope" }, + { name = "hanlp" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "openai" }, + { name = "pandas" }, + { name = "python-docx" }, + { name = "python-dotenv" }, + { name = "python-igraph" }, + { name = "pyyaml" }, + { name = "tenacity" }, + { name = "tqdm" }, + { name = "volcengine-python-sdk" }, + { name = "zhipuai" }, +] + +[package.optional-dependencies] +dev = [ + { name = "black" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, +] + +[package.dev-dependencies] +dev = [ + { name = "black" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.9.0" }, + { name = "anthropic", specifier = ">=0.18.0" }, + { name = "black", marker = "extra == 'dev'", specifier = ">=23.0.0" }, + { name = "dashscope", specifier = ">=1.17.0" }, + { name = "hanlp", specifier = ">=2.1.0" }, + { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.5.0" }, + { name = "networkx", specifier = ">=3.2.0" }, + { name = "numpy", specifier = ">=1.24.0" }, + { name = "openai", specifier = ">=1.0.0" }, + { name = "pandas", specifier = ">=2.0.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.4.0" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21.0" }, + { name = "python-docx", specifier = ">=1.1.0" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, + { name = "python-igraph", specifier = ">=0.11.0" }, + { name = "pyyaml", specifier = ">=6.0" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, + { name = "tenacity", specifier = ">=8.2.0" }, + { name = "tqdm", specifier = ">=4.66.0" }, + { name = "volcengine-python-sdk", specifier = ">=1.0.0" }, + { name = "zhipuai", specifier = ">=2.0.0" }, +] +provides-extras = ["dev"] + +[package.metadata.requires-dev] +dev = [ + { name = "black", specifier = ">=23.0.0" }, + { name = "mypy", specifier = ">=1.5.0" }, + { name = "pytest", specifier = ">=7.4.0" }, + { name = "pytest-asyncio", specifier = ">=0.21.0" }, + { name = "ruff", specifier = ">=0.1.0" }, +] + +[[package]] +name = "lxml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/c8/8ff2bc6b920c84355146cd1ab7d181bc543b89241cfb1ebee824a7c81457/lxml-6.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a59f5448ba2ceccd06995c95ea59a7674a10de0810f2ce90c9006f3cbc044456", size = 8661887, upload-time = "2025-09-22T04:01:17.265Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/9aae1008083bb501ef63284220ce81638332f9ccbfa53765b2b7502203cf/lxml-6.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e8113639f3296706fbac34a30813929e29247718e88173ad849f57ca59754924", size = 4667818, upload-time = "2025-09-22T04:01:19.688Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ca/31fb37f99f37f1536c133476674c10b577e409c0a624384147653e38baf2/lxml-6.0.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a8bef9b9825fa8bc816a6e641bb67219489229ebc648be422af695f6e7a4fa7f", size = 4950807, upload-time = "2025-09-22T04:01:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/da/87/f6cb9442e4bada8aab5ae7e1046264f62fdbeaa6e3f6211b93f4c0dd97f1/lxml-6.0.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:65ea18d710fd14e0186c2f973dc60bb52039a275f82d3c44a0e42b43440ea534", size = 5109179, upload-time = "2025-09-22T04:01:23.32Z" }, + { url = "https://files.pythonhosted.org/packages/c8/20/a7760713e65888db79bbae4f6146a6ae5c04e4a204a3c48896c408cd6ed2/lxml-6.0.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c371aa98126a0d4c739ca93ceffa0fd7a5d732e3ac66a46e74339acd4d334564", size = 5023044, upload-time = "2025-09-22T04:01:25.118Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b0/7e64e0460fcb36471899f75831509098f3fd7cd02a3833ac517433cb4f8f/lxml-6.0.2-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:700efd30c0fa1a3581d80a748157397559396090a51d306ea59a70020223d16f", size = 5359685, upload-time = "2025-09-22T04:01:27.398Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e1/e5df362e9ca4e2f48ed6411bd4b3a0ae737cc842e96877f5bf9428055ab4/lxml-6.0.2-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c33e66d44fe60e72397b487ee92e01da0d09ba2d66df8eae42d77b6d06e5eba0", size = 5654127, upload-time = "2025-09-22T04:01:29.629Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d1/232b3309a02d60f11e71857778bfcd4acbdb86c07db8260caf7d008b08f8/lxml-6.0.2-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90a345bbeaf9d0587a3aaffb7006aa39ccb6ff0e96a57286c0cb2fd1520ea192", size = 5253958, upload-time = "2025-09-22T04:01:31.535Z" }, + { url = "https://files.pythonhosted.org/packages/35/35/d955a070994725c4f7d80583a96cab9c107c57a125b20bb5f708fe941011/lxml-6.0.2-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:064fdadaf7a21af3ed1dcaa106b854077fbeada827c18f72aec9346847cd65d0", size = 4711541, upload-time = "2025-09-22T04:01:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/1e/be/667d17363b38a78c4bd63cfd4b4632029fd68d2c2dc81f25ce9eb5224dd5/lxml-6.0.2-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fbc74f42c3525ac4ffa4b89cbdd00057b6196bcefe8bce794abd42d33a018092", size = 5267426, upload-time = "2025-09-22T04:01:35.639Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/62c70aa4a1c26569bc958c9ca86af2bb4e1f614e8c04fb2989833874f7ae/lxml-6.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ddff43f702905a4e32bc24f3f2e2edfe0f8fde3277d481bffb709a4cced7a1f", size = 5064917, upload-time = "2025-09-22T04:01:37.448Z" }, + { url = "https://files.pythonhosted.org/packages/bd/55/6ceddaca353ebd0f1908ef712c597f8570cc9c58130dbb89903198e441fd/lxml-6.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6da5185951d72e6f5352166e3da7b0dc27aa70bd1090b0eb3f7f7212b53f1bb8", size = 4788795, upload-time = "2025-09-22T04:01:39.165Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e8/fd63e15da5e3fd4c2146f8bbb3c14e94ab850589beab88e547b2dbce22e1/lxml-6.0.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:57a86e1ebb4020a38d295c04fc79603c7899e0df71588043eb218722dabc087f", size = 5676759, upload-time = "2025-09-22T04:01:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/b3ec58dc5c374697f5ba37412cd2728f427d056315d124dd4b61da381877/lxml-6.0.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2047d8234fe735ab77802ce5f2297e410ff40f5238aec569ad7c8e163d7b19a6", size = 5255666, upload-time = "2025-09-22T04:01:43.363Z" }, + { url = "https://files.pythonhosted.org/packages/19/93/03ba725df4c3d72afd9596eef4a37a837ce8e4806010569bedfcd2cb68fd/lxml-6.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f91fd2b2ea15a6800c8e24418c0775a1694eefc011392da73bc6cef2623b322", size = 5277989, upload-time = "2025-09-22T04:01:45.215Z" }, + { url = "https://files.pythonhosted.org/packages/c6/80/c06de80bfce881d0ad738576f243911fccf992687ae09fd80b734712b39c/lxml-6.0.2-cp312-cp312-win32.whl", hash = "sha256:3ae2ce7d6fedfb3414a2b6c5e20b249c4c607f72cb8d2bb7cc9c6ec7c6f4e849", size = 3611456, upload-time = "2025-09-22T04:01:48.243Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d7/0cdfb6c3e30893463fb3d1e52bc5f5f99684a03c29a0b6b605cfae879cd5/lxml-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:72c87e5ee4e58a8354fb9c7c84cbf95a1c8236c127a5d1b7683f04bed8361e1f", size = 4011793, upload-time = "2025-09-22T04:01:50.042Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7b/93c73c67db235931527301ed3785f849c78991e2e34f3fd9a6663ffda4c5/lxml-6.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:61cb10eeb95570153e0c0e554f58df92ecf5109f75eacad4a95baa709e26c3d6", size = 3672836, upload-time = "2025-09-22T04:01:52.145Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/4e8f0540608977aea078bf6d79f128e0e2c2bba8af1acf775c30baa70460/lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77", size = 8648494, upload-time = "2025-09-22T04:01:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f4/2a94a3d3dfd6c6b433501b8d470a1960a20ecce93245cf2db1706adf6c19/lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f", size = 4661146, upload-time = "2025-09-22T04:01:56.282Z" }, + { url = "https://files.pythonhosted.org/packages/25/2e/4efa677fa6b322013035d38016f6ae859d06cac67437ca7dc708a6af7028/lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452", size = 4946932, upload-time = "2025-09-22T04:01:58.989Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0f/526e78a6d38d109fdbaa5049c62e1d32fdd70c75fb61c4eadf3045d3d124/lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048", size = 5100060, upload-time = "2025-09-22T04:02:00.812Z" }, + { url = "https://files.pythonhosted.org/packages/81/76/99de58d81fa702cc0ea7edae4f4640416c2062813a00ff24bd70ac1d9c9b/lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df", size = 5019000, upload-time = "2025-09-22T04:02:02.671Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/9e57d25482bc9a9882cb0037fdb9cc18f4b79d85df94fa9d2a89562f1d25/lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1", size = 5348496, upload-time = "2025-09-22T04:02:04.904Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8e/cb99bd0b83ccc3e8f0f528e9aa1f7a9965dfec08c617070c5db8d63a87ce/lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916", size = 5643779, upload-time = "2025-09-22T04:02:06.689Z" }, + { url = "https://files.pythonhosted.org/packages/d0/34/9e591954939276bb679b73773836c6684c22e56d05980e31d52a9a8deb18/lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd", size = 5244072, upload-time = "2025-09-22T04:02:08.587Z" }, + { url = "https://files.pythonhosted.org/packages/8d/27/b29ff065f9aaca443ee377aff699714fcbffb371b4fce5ac4ca759e436d5/lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6", size = 4718675, upload-time = "2025-09-22T04:02:10.783Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f756f9c2cd27caa1a6ef8c32ae47aadea697f5c2c6d07b0dae133c244fbe/lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a", size = 5255171, upload-time = "2025-09-22T04:02:12.631Z" }, + { url = "https://files.pythonhosted.org/packages/61/46/bb85ea42d2cb1bd8395484fd72f38e3389611aa496ac7772da9205bbda0e/lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679", size = 5057175, upload-time = "2025-09-22T04:02:14.718Z" }, + { url = "https://files.pythonhosted.org/packages/95/0c/443fc476dcc8e41577f0af70458c50fe299a97bb6b7505bb1ae09aa7f9ac/lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659", size = 4785688, upload-time = "2025-09-22T04:02:16.957Z" }, + { url = "https://files.pythonhosted.org/packages/48/78/6ef0b359d45bb9697bc5a626e1992fa5d27aa3f8004b137b2314793b50a0/lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484", size = 5660655, upload-time = "2025-09-22T04:02:18.815Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ea/e1d33808f386bc1339d08c0dcada6e4712d4ed8e93fcad5f057070b7988a/lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2", size = 5247695, upload-time = "2025-09-22T04:02:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/4f/47/eba75dfd8183673725255247a603b4ad606f4ae657b60c6c145b381697da/lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314", size = 5269841, upload-time = "2025-09-22T04:02:22.489Z" }, + { url = "https://files.pythonhosted.org/packages/76/04/5c5e2b8577bc936e219becb2e98cdb1aca14a4921a12995b9d0c523502ae/lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2", size = 3610700, upload-time = "2025-09-22T04:02:24.465Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0a/4643ccc6bb8b143e9f9640aa54e38255f9d3b45feb2cbe7ae2ca47e8782e/lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7", size = 4010347, upload-time = "2025-09-22T04:02:26.286Z" }, + { url = "https://files.pythonhosted.org/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" }, + { url = "https://files.pythonhosted.org/packages/03/15/d4a377b385ab693ce97b472fe0c77c2b16ec79590e688b3ccc71fba19884/lxml-6.0.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:b0c732aa23de8f8aec23f4b580d1e52905ef468afb4abeafd3fec77042abb6fe", size = 8659801, upload-time = "2025-09-22T04:02:30.113Z" }, + { url = "https://files.pythonhosted.org/packages/c8/e8/c128e37589463668794d503afaeb003987373c5f94d667124ffd8078bbd9/lxml-6.0.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4468e3b83e10e0317a89a33d28f7aeba1caa4d1a6fd457d115dd4ffe90c5931d", size = 4659403, upload-time = "2025-09-22T04:02:32.119Z" }, + { url = "https://files.pythonhosted.org/packages/00/ce/74903904339decdf7da7847bb5741fc98a5451b42fc419a86c0c13d26fe2/lxml-6.0.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:abd44571493973bad4598a3be7e1d807ed45aa2adaf7ab92ab7c62609569b17d", size = 4966974, upload-time = "2025-09-22T04:02:34.155Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d3/131dec79ce61c5567fecf82515bd9bc36395df42501b50f7f7f3bd065df0/lxml-6.0.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:370cd78d5855cfbffd57c422851f7d3864e6ae72d0da615fca4dad8c45d375a5", size = 5102953, upload-time = "2025-09-22T04:02:36.054Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ea/a43ba9bb750d4ffdd885f2cd333572f5bb900cd2408b67fdda07e85978a0/lxml-6.0.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:901e3b4219fa04ef766885fb40fa516a71662a4c61b80c94d25336b4934b71c0", size = 5055054, upload-time = "2025-09-22T04:02:38.154Z" }, + { url = "https://files.pythonhosted.org/packages/60/23/6885b451636ae286c34628f70a7ed1fcc759f8d9ad382d132e1c8d3d9bfd/lxml-6.0.2-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:a4bf42d2e4cf52c28cc1812d62426b9503cdb0c87a6de81442626aa7d69707ba", size = 5352421, upload-time = "2025-09-22T04:02:40.413Z" }, + { url = "https://files.pythonhosted.org/packages/48/5b/fc2ddfc94ddbe3eebb8e9af6e3fd65e2feba4967f6a4e9683875c394c2d8/lxml-6.0.2-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2c7fdaa4d7c3d886a42534adec7cfac73860b89b4e5298752f60aa5984641a0", size = 5673684, upload-time = "2025-09-22T04:02:42.288Z" }, + { url = "https://files.pythonhosted.org/packages/29/9c/47293c58cc91769130fbf85531280e8cc7868f7fbb6d92f4670071b9cb3e/lxml-6.0.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98a5e1660dc7de2200b00d53fa00bcd3c35a3608c305d45a7bbcaf29fa16e83d", size = 5252463, upload-time = "2025-09-22T04:02:44.165Z" }, + { url = "https://files.pythonhosted.org/packages/9b/da/ba6eceb830c762b48e711ded880d7e3e89fc6c7323e587c36540b6b23c6b/lxml-6.0.2-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:dc051506c30b609238d79eda75ee9cab3e520570ec8219844a72a46020901e37", size = 4698437, upload-time = "2025-09-22T04:02:46.524Z" }, + { url = "https://files.pythonhosted.org/packages/a5/24/7be3f82cb7990b89118d944b619e53c656c97dc89c28cfb143fdb7cd6f4d/lxml-6.0.2-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8799481bbdd212470d17513a54d568f44416db01250f49449647b5ab5b5dccb9", size = 5269890, upload-time = "2025-09-22T04:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bd/dcfb9ea1e16c665efd7538fc5d5c34071276ce9220e234217682e7d2c4a5/lxml-6.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9261bb77c2dab42f3ecd9103951aeca2c40277701eb7e912c545c1b16e0e4917", size = 5097185, upload-time = "2025-09-22T04:02:50.746Z" }, + { url = "https://files.pythonhosted.org/packages/21/04/a60b0ff9314736316f28316b694bccbbabe100f8483ad83852d77fc7468e/lxml-6.0.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:65ac4a01aba353cfa6d5725b95d7aed6356ddc0a3cd734de00124d285b04b64f", size = 4745895, upload-time = "2025-09-22T04:02:52.968Z" }, + { url = "https://files.pythonhosted.org/packages/d6/bd/7d54bd1846e5a310d9c715921c5faa71cf5c0853372adf78aee70c8d7aa2/lxml-6.0.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b22a07cbb82fea98f8a2fd814f3d1811ff9ed76d0fc6abc84eb21527596e7cc8", size = 5695246, upload-time = "2025-09-22T04:02:54.798Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/5643d6ab947bc371da21323acb2a6e603cedbe71cb4c99c8254289ab6f4e/lxml-6.0.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d759cdd7f3e055d6bc8d9bec3ad905227b2e4c785dc16c372eb5b5e83123f48a", size = 5260797, upload-time = "2025-09-22T04:02:57.058Z" }, + { url = "https://files.pythonhosted.org/packages/33/da/34c1ec4cff1eea7d0b4cd44af8411806ed943141804ac9c5d565302afb78/lxml-6.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:945da35a48d193d27c188037a05fec5492937f66fb1958c24fc761fb9d40d43c", size = 5277404, upload-time = "2025-09-22T04:02:58.966Z" }, + { url = "https://files.pythonhosted.org/packages/82/57/4eca3e31e54dc89e2c3507e1cd411074a17565fa5ffc437c4ae0a00d439e/lxml-6.0.2-cp314-cp314-win32.whl", hash = "sha256:be3aaa60da67e6153eb15715cc2e19091af5dc75faef8b8a585aea372507384b", size = 3670072, upload-time = "2025-09-22T04:03:38.05Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e0/c96cf13eccd20c9421ba910304dae0f619724dcf1702864fd59dd386404d/lxml-6.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa25afbadead523f7001caf0c2382afd272c315a033a7b06336da2637d92d6ed", size = 4080617, upload-time = "2025-09-22T04:03:39.835Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5d/b3f03e22b3d38d6f188ef044900a9b29b2fe0aebb94625ce9fe244011d34/lxml-6.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:063eccf89df5b24e361b123e257e437f9e9878f425ee9aae3144c77faf6da6d8", size = 3754930, upload-time = "2025-09-22T04:03:41.565Z" }, + { url = "https://files.pythonhosted.org/packages/5e/5c/42c2c4c03554580708fc738d13414801f340c04c3eff90d8d2d227145275/lxml-6.0.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6162a86d86893d63084faaf4ff937b3daea233e3682fb4474db07395794fa80d", size = 8910380, upload-time = "2025-09-22T04:03:01.645Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4f/12df843e3e10d18d468a7557058f8d3733e8b6e12401f30b1ef29360740f/lxml-6.0.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:414aaa94e974e23a3e92e7ca5b97d10c0cf37b6481f50911032c69eeb3991bba", size = 4775632, upload-time = "2025-09-22T04:03:03.814Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0c/9dc31e6c2d0d418483cbcb469d1f5a582a1cd00a1f4081953d44051f3c50/lxml-6.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48461bd21625458dd01e14e2c38dd0aea69addc3c4f960c30d9f59d7f93be601", size = 4975171, upload-time = "2025-09-22T04:03:05.651Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2b/9b870c6ca24c841bdd887504808f0417aa9d8d564114689266f19ddf29c8/lxml-6.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:25fcc59afc57d527cfc78a58f40ab4c9b8fd096a9a3f964d2781ffb6eb33f4ed", size = 5110109, upload-time = "2025-09-22T04:03:07.452Z" }, + { url = "https://files.pythonhosted.org/packages/bf/0c/4f5f2a4dd319a178912751564471355d9019e220c20d7db3fb8307ed8582/lxml-6.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5179c60288204e6ddde3f774a93350177e08876eaf3ab78aa3a3649d43eb7d37", size = 5041061, upload-time = "2025-09-22T04:03:09.297Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/554eed290365267671fe001a20d72d14f468ae4e6acef1e179b039436967/lxml-6.0.2-cp314-cp314t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:967aab75434de148ec80597b75062d8123cadf2943fb4281f385141e18b21338", size = 5306233, upload-time = "2025-09-22T04:03:11.651Z" }, + { url = "https://files.pythonhosted.org/packages/7a/31/1d748aa275e71802ad9722df32a7a35034246b42c0ecdd8235412c3396ef/lxml-6.0.2-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d100fcc8930d697c6561156c6810ab4a508fb264c8b6779e6e61e2ed5e7558f9", size = 5604739, upload-time = "2025-09-22T04:03:13.592Z" }, + { url = "https://files.pythonhosted.org/packages/8f/41/2c11916bcac09ed561adccacceaedd2bf0e0b25b297ea92aab99fd03d0fa/lxml-6.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ca59e7e13e5981175b8b3e4ab84d7da57993eeff53c07764dcebda0d0e64ecd", size = 5225119, upload-time = "2025-09-22T04:03:15.408Z" }, + { url = "https://files.pythonhosted.org/packages/99/05/4e5c2873d8f17aa018e6afde417c80cc5d0c33be4854cce3ef5670c49367/lxml-6.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:957448ac63a42e2e49531b9d6c0fa449a1970dbc32467aaad46f11545be9af1d", size = 4633665, upload-time = "2025-09-22T04:03:17.262Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c9/dcc2da1bebd6275cdc723b515f93edf548b82f36a5458cca3578bc899332/lxml-6.0.2-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b7fc49c37f1786284b12af63152fe1d0990722497e2d5817acfe7a877522f9a9", size = 5234997, upload-time = "2025-09-22T04:03:19.14Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e2/5172e4e7468afca64a37b81dba152fc5d90e30f9c83c7c3213d6a02a5ce4/lxml-6.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e19e0643cc936a22e837f79d01a550678da8377d7d801a14487c10c34ee49c7e", size = 5090957, upload-time = "2025-09-22T04:03:21.436Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b3/15461fd3e5cd4ddcb7938b87fc20b14ab113b92312fc97afe65cd7c85de1/lxml-6.0.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:1db01e5cf14345628e0cbe71067204db658e2fb8e51e7f33631f5f4735fefd8d", size = 4764372, upload-time = "2025-09-22T04:03:23.27Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/f310b987c8bf9e61c4dd8e8035c416bd3230098f5e3cfa69fc4232de7059/lxml-6.0.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:875c6b5ab39ad5291588aed6925fac99d0097af0dd62f33c7b43736043d4a2ec", size = 5634653, upload-time = "2025-09-22T04:03:25.767Z" }, + { url = "https://files.pythonhosted.org/packages/70/ff/51c80e75e0bc9382158133bdcf4e339b5886c6ee2418b5199b3f1a61ed6d/lxml-6.0.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cdcbed9ad19da81c480dfd6dd161886db6096083c9938ead313d94b30aadf272", size = 5233795, upload-time = "2025-09-22T04:03:27.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/4d/4856e897df0d588789dd844dbed9d91782c4ef0b327f96ce53c807e13128/lxml-6.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80dadc234ebc532e09be1975ff538d154a7fa61ea5031c03d25178855544728f", size = 5257023, upload-time = "2025-09-22T04:03:30.056Z" }, + { url = "https://files.pythonhosted.org/packages/0f/85/86766dfebfa87bea0ab78e9ff7a4b4b45225df4b4d3b8cc3c03c5cd68464/lxml-6.0.2-cp314-cp314t-win32.whl", hash = "sha256:da08e7bb297b04e893d91087df19638dc7a6bb858a954b0cc2b9f5053c922312", size = 3911420, upload-time = "2025-09-22T04:03:32.198Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1a/b248b355834c8e32614650b8008c69ffeb0ceb149c793961dd8c0b991bb3/lxml-6.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:252a22982dca42f6155125ac76d3432e548a7625d56f5a273ee78a5057216eca", size = 4406837, upload-time = "2025-09-22T04:03:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/92/aa/df863bcc39c5e0946263454aba394de8a9084dbaff8ad143846b0d844739/lxml-6.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:bb4c1847b303835d89d785a18801a883436cdfd5dc3d62947f9c49e24f0f5a2c", size = 3822205, upload-time = "2025-09-22T04:03:36.249Z" }, +] + +[[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/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { 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 = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877, upload-time = "2025-10-06T14:49:20.884Z" }, + { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467, upload-time = "2025-10-06T14:49:22.054Z" }, + { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834, upload-time = "2025-10-06T14:49:23.566Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545, upload-time = "2025-10-06T14:49:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305, upload-time = "2025-10-06T14:49:26.778Z" }, + { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363, upload-time = "2025-10-06T14:49:28.562Z" }, + { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375, upload-time = "2025-10-06T14:49:29.96Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346, upload-time = "2025-10-06T14:49:31.404Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107, upload-time = "2025-10-06T14:49:32.974Z" }, + { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592, upload-time = "2025-10-06T14:49:34.52Z" }, + { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024, upload-time = "2025-10-06T14:49:35.956Z" }, + { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484, upload-time = "2025-10-06T14:49:37.631Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579, upload-time = "2025-10-06T14:49:39.502Z" }, + { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654, upload-time = "2025-10-06T14:49:41.32Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511, upload-time = "2025-10-06T14:49:46.021Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895, upload-time = "2025-10-06T14:49:48.718Z" }, + { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073, upload-time = "2025-10-06T14:49:50.28Z" }, + { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226, upload-time = "2025-10-06T14:49:52.304Z" }, + { url = "https://files.pythonhosted.org/packages/d2/86/33272a544eeb36d66e4d9a920602d1a2f57d4ebea4ef3cdfe5a912574c95/multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6", size = 76135, upload-time = "2025-10-06T14:49:54.26Z" }, + { url = "https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159", size = 45117, upload-time = "2025-10-06T14:49:55.82Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca", size = 43472, upload-time = "2025-10-06T14:49:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/75/3f/e2639e80325af0b6c6febdf8e57cc07043ff15f57fa1ef808f4ccb5ac4cd/multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8", size = 249342, upload-time = "2025-10-06T14:49:58.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/cc/84e0585f805cbeaa9cbdaa95f9a3d6aed745b9d25700623ac89a6ecff400/multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60", size = 257082, upload-time = "2025-10-06T14:49:59.89Z" }, + { url = "https://files.pythonhosted.org/packages/b0/9c/ac851c107c92289acbbf5cfb485694084690c1b17e555f44952c26ddc5bd/multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4", size = 240704, upload-time = "2025-10-06T14:50:01.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/cc/5f93e99427248c09da95b62d64b25748a5f5c98c7c2ab09825a1d6af0e15/multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f", size = 266355, upload-time = "2025-10-06T14:50:02.955Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0c/2ec1d883ceb79c6f7f6d7ad90c919c898f5d1c6ea96d322751420211e072/multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf", size = 267259, upload-time = "2025-10-06T14:50:04.446Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32", size = 254903, upload-time = "2025-10-06T14:50:05.98Z" }, + { url = "https://files.pythonhosted.org/packages/06/c9/11ea263ad0df7dfabcad404feb3c0dd40b131bc7f232d5537f2fb1356951/multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036", size = 252365, upload-time = "2025-10-06T14:50:07.511Z" }, + { url = "https://files.pythonhosted.org/packages/41/88/d714b86ee2c17d6e09850c70c9d310abac3d808ab49dfa16b43aba9d53fd/multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec", size = 250062, upload-time = "2025-10-06T14:50:09.074Z" }, + { url = "https://files.pythonhosted.org/packages/15/fe/ad407bb9e818c2b31383f6131ca19ea7e35ce93cf1310fce69f12e89de75/multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e", size = 249683, upload-time = "2025-10-06T14:50:10.714Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a4/a89abdb0229e533fb925e7c6e5c40201c2873efebc9abaf14046a4536ee6/multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64", size = 261254, upload-time = "2025-10-06T14:50:12.28Z" }, + { url = "https://files.pythonhosted.org/packages/8d/aa/0e2b27bd88b40a4fb8dc53dd74eecac70edaa4c1dd0707eb2164da3675b3/multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd", size = 257967, upload-time = "2025-10-06T14:50:14.16Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8e/0c67b7120d5d5f6d874ed85a085f9dc770a7f9d8813e80f44a9fec820bb7/multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288", size = 250085, upload-time = "2025-10-06T14:50:15.639Z" }, + { url = "https://files.pythonhosted.org/packages/ba/55/b73e1d624ea4b8fd4dd07a3bb70f6e4c7c6c5d9d640a41c6ffe5cdbd2a55/multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17", size = 41713, upload-time = "2025-10-06T14:50:17.066Z" }, + { url = "https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390", size = 45915, upload-time = "2025-10-06T14:50:18.264Z" }, + { url = "https://files.pythonhosted.org/packages/31/2a/8987831e811f1184c22bc2e45844934385363ee61c0a2dcfa8f71b87e608/multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e", size = 43077, upload-time = "2025-10-06T14:50:19.853Z" }, + { url = "https://files.pythonhosted.org/packages/e8/68/7b3a5170a382a340147337b300b9eb25a9ddb573bcdfff19c0fa3f31ffba/multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00", size = 83114, upload-time = "2025-10-06T14:50:21.223Z" }, + { url = "https://files.pythonhosted.org/packages/55/5c/3fa2d07c84df4e302060f555bbf539310980362236ad49f50eeb0a1c1eb9/multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb", size = 48442, upload-time = "2025-10-06T14:50:22.871Z" }, + { url = "https://files.pythonhosted.org/packages/fc/56/67212d33239797f9bd91962bb899d72bb0f4c35a8652dcdb8ed049bef878/multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b", size = 46885, upload-time = "2025-10-06T14:50:24.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/d1/908f896224290350721597a61a69cd19b89ad8ee0ae1f38b3f5cd12ea2ac/multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c", size = 242588, upload-time = "2025-10-06T14:50:25.716Z" }, + { url = "https://files.pythonhosted.org/packages/ab/67/8604288bbd68680eee0ab568fdcb56171d8b23a01bcd5cb0c8fedf6e5d99/multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1", size = 249966, upload-time = "2025-10-06T14:50:28.192Z" }, + { url = "https://files.pythonhosted.org/packages/20/33/9228d76339f1ba51e3efef7da3ebd91964d3006217aae13211653193c3ff/multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b", size = 228618, upload-time = "2025-10-06T14:50:29.82Z" }, + { url = "https://files.pythonhosted.org/packages/f8/2d/25d9b566d10cab1c42b3b9e5b11ef79c9111eaf4463b8c257a3bd89e0ead/multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5", size = 257539, upload-time = "2025-10-06T14:50:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b1/8d1a965e6637fc33de3c0d8f414485c2b7e4af00f42cab3d84e7b955c222/multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad", size = 256345, upload-time = "2025-10-06T14:50:33.26Z" }, + { url = "https://files.pythonhosted.org/packages/ba/0c/06b5a8adbdeedada6f4fb8d8f193d44a347223b11939b42953eeb6530b6b/multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c", size = 247934, upload-time = "2025-10-06T14:50:34.808Z" }, + { url = "https://files.pythonhosted.org/packages/8f/31/b2491b5fe167ca044c6eb4b8f2c9f3b8a00b24c432c365358eadac5d7625/multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5", size = 245243, upload-time = "2025-10-06T14:50:36.436Z" }, + { url = "https://files.pythonhosted.org/packages/61/1a/982913957cb90406c8c94f53001abd9eafc271cb3e70ff6371590bec478e/multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10", size = 235878, upload-time = "2025-10-06T14:50:37.953Z" }, + { url = "https://files.pythonhosted.org/packages/be/c0/21435d804c1a1cf7a2608593f4d19bca5bcbd7a81a70b253fdd1c12af9c0/multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754", size = 243452, upload-time = "2025-10-06T14:50:39.574Z" }, + { url = "https://files.pythonhosted.org/packages/54/0a/4349d540d4a883863191be6eb9a928846d4ec0ea007d3dcd36323bb058ac/multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c", size = 252312, upload-time = "2025-10-06T14:50:41.612Z" }, + { url = "https://files.pythonhosted.org/packages/26/64/d5416038dbda1488daf16b676e4dbfd9674dde10a0cc8f4fc2b502d8125d/multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762", size = 246935, upload-time = "2025-10-06T14:50:43.972Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8c/8290c50d14e49f35e0bd4abc25e1bc7711149ca9588ab7d04f886cdf03d9/multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6", size = 243385, upload-time = "2025-10-06T14:50:45.648Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a0/f83ae75e42d694b3fbad3e047670e511c138be747bc713cf1b10d5096416/multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d", size = 47777, upload-time = "2025-10-06T14:50:47.154Z" }, + { url = "https://files.pythonhosted.org/packages/dc/80/9b174a92814a3830b7357307a792300f42c9e94664b01dee8e457551fa66/multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6", size = 53104, upload-time = "2025-10-06T14:50:48.851Z" }, + { url = "https://files.pythonhosted.org/packages/cc/28/04baeaf0428d95bb7a7bea0e691ba2f31394338ba424fb0679a9ed0f4c09/multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792", size = 45503, upload-time = "2025-10-06T14:50:50.16Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b1/3da6934455dd4b261d4c72f897e3a5728eba81db59959f3a639245891baa/multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842", size = 75128, upload-time = "2025-10-06T14:50:51.92Z" }, + { url = "https://files.pythonhosted.org/packages/14/2c/f069cab5b51d175a1a2cb4ccdf7a2c2dabd58aa5bd933fa036a8d15e2404/multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b", size = 44410, upload-time = "2025-10-06T14:50:53.275Z" }, + { url = "https://files.pythonhosted.org/packages/42/e2/64bb41266427af6642b6b128e8774ed84c11b80a90702c13ac0a86bb10cc/multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38", size = 43205, upload-time = "2025-10-06T14:50:54.911Z" }, + { url = "https://files.pythonhosted.org/packages/02/68/6b086fef8a3f1a8541b9236c594f0c9245617c29841f2e0395d979485cde/multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128", size = 245084, upload-time = "2025-10-06T14:50:56.369Z" }, + { url = "https://files.pythonhosted.org/packages/15/ee/f524093232007cd7a75c1d132df70f235cfd590a7c9eaccd7ff422ef4ae8/multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34", size = 252667, upload-time = "2025-10-06T14:50:57.991Z" }, + { url = "https://files.pythonhosted.org/packages/02/a5/eeb3f43ab45878f1895118c3ef157a480db58ede3f248e29b5354139c2c9/multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99", size = 233590, upload-time = "2025-10-06T14:50:59.589Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/76d02f8270b97269d7e3dbd45644b1785bda457b474315f8cf999525a193/multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202", size = 264112, upload-time = "2025-10-06T14:51:01.183Z" }, + { url = "https://files.pythonhosted.org/packages/76/0b/c28a70ecb58963847c2a8efe334904cd254812b10e535aefb3bcce513918/multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1", size = 261194, upload-time = "2025-10-06T14:51:02.794Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/2ab26e4209773223159b83aa32721b4021ffb08102f8ac7d689c943fded1/multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3", size = 248510, upload-time = "2025-10-06T14:51:04.724Z" }, + { url = "https://files.pythonhosted.org/packages/93/cd/06c1fa8282af1d1c46fd55c10a7930af652afdce43999501d4d68664170c/multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d", size = 248395, upload-time = "2025-10-06T14:51:06.306Z" }, + { url = "https://files.pythonhosted.org/packages/99/ac/82cb419dd6b04ccf9e7e61befc00c77614fc8134362488b553402ecd55ce/multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6", size = 239520, upload-time = "2025-10-06T14:51:08.091Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f3/a0f9bf09493421bd8716a362e0cd1d244f5a6550f5beffdd6b47e885b331/multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7", size = 245479, upload-time = "2025-10-06T14:51:10.365Z" }, + { url = "https://files.pythonhosted.org/packages/8d/01/476d38fc73a212843f43c852b0eee266b6971f0e28329c2184a8df90c376/multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb", size = 258903, upload-time = "2025-10-06T14:51:12.466Z" }, + { url = "https://files.pythonhosted.org/packages/49/6d/23faeb0868adba613b817d0e69c5f15531b24d462af8012c4f6de4fa8dc3/multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f", size = 252333, upload-time = "2025-10-06T14:51:14.48Z" }, + { url = "https://files.pythonhosted.org/packages/1e/cc/48d02ac22b30fa247f7dad82866e4b1015431092f4ba6ebc7e77596e0b18/multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f", size = 243411, upload-time = "2025-10-06T14:51:16.072Z" }, + { url = "https://files.pythonhosted.org/packages/4a/03/29a8bf5a18abf1fe34535c88adbdfa88c9fb869b5a3b120692c64abe8284/multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885", size = 40940, upload-time = "2025-10-06T14:51:17.544Z" }, + { url = "https://files.pythonhosted.org/packages/82/16/7ed27b680791b939de138f906d5cf2b4657b0d45ca6f5dd6236fdddafb1a/multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c", size = 45087, upload-time = "2025-10-06T14:51:18.875Z" }, + { url = "https://files.pythonhosted.org/packages/cd/3c/e3e62eb35a1950292fe39315d3c89941e30a9d07d5d2df42965ab041da43/multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000", size = 42368, upload-time = "2025-10-06T14:51:20.225Z" }, + { url = "https://files.pythonhosted.org/packages/8b/40/cd499bd0dbc5f1136726db3153042a735fffd0d77268e2ee20d5f33c010f/multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63", size = 82326, upload-time = "2025-10-06T14:51:21.588Z" }, + { url = "https://files.pythonhosted.org/packages/13/8a/18e031eca251c8df76daf0288e6790561806e439f5ce99a170b4af30676b/multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718", size = 48065, upload-time = "2025-10-06T14:51:22.93Z" }, + { url = "https://files.pythonhosted.org/packages/40/71/5e6701277470a87d234e433fb0a3a7deaf3bcd92566e421e7ae9776319de/multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2", size = 46475, upload-time = "2025-10-06T14:51:24.352Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6a/bab00cbab6d9cfb57afe1663318f72ec28289ea03fd4e8236bb78429893a/multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e", size = 239324, upload-time = "2025-10-06T14:51:25.822Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5f/8de95f629fc22a7769ade8b41028e3e5a822c1f8904f618d175945a81ad3/multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064", size = 246877, upload-time = "2025-10-06T14:51:27.604Z" }, + { url = "https://files.pythonhosted.org/packages/23/b4/38881a960458f25b89e9f4a4fdcb02ac101cfa710190db6e5528841e67de/multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e", size = 225824, upload-time = "2025-10-06T14:51:29.664Z" }, + { url = "https://files.pythonhosted.org/packages/1e/39/6566210c83f8a261575f18e7144736059f0c460b362e96e9cf797a24b8e7/multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd", size = 253558, upload-time = "2025-10-06T14:51:31.684Z" }, + { url = "https://files.pythonhosted.org/packages/00/a3/67f18315100f64c269f46e6c0319fa87ba68f0f64f2b8e7fd7c72b913a0b/multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a", size = 252339, upload-time = "2025-10-06T14:51:33.699Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2a/1cb77266afee2458d82f50da41beba02159b1d6b1f7973afc9a1cad1499b/multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96", size = 244895, upload-time = "2025-10-06T14:51:36.189Z" }, + { url = "https://files.pythonhosted.org/packages/dd/72/09fa7dd487f119b2eb9524946ddd36e2067c08510576d43ff68469563b3b/multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e", size = 241862, upload-time = "2025-10-06T14:51:41.291Z" }, + { url = "https://files.pythonhosted.org/packages/65/92/bc1f8bd0853d8669300f732c801974dfc3702c3eeadae2f60cef54dc69d7/multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599", size = 232376, upload-time = "2025-10-06T14:51:43.55Z" }, + { url = "https://files.pythonhosted.org/packages/09/86/ac39399e5cb9d0c2ac8ef6e10a768e4d3bc933ac808d49c41f9dc23337eb/multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394", size = 240272, upload-time = "2025-10-06T14:51:45.265Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b6/fed5ac6b8563ec72df6cb1ea8dac6d17f0a4a1f65045f66b6d3bf1497c02/multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38", size = 248774, upload-time = "2025-10-06T14:51:46.836Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8d/b954d8c0dc132b68f760aefd45870978deec6818897389dace00fcde32ff/multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9", size = 242731, upload-time = "2025-10-06T14:51:48.541Z" }, + { url = "https://files.pythonhosted.org/packages/16/9d/a2dac7009125d3540c2f54e194829ea18ac53716c61b655d8ed300120b0f/multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0", size = 240193, upload-time = "2025-10-06T14:51:50.355Z" }, + { url = "https://files.pythonhosted.org/packages/39/ca/c05f144128ea232ae2178b008d5011d4e2cea86e4ee8c85c2631b1b94802/multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13", size = 48023, upload-time = "2025-10-06T14:51:51.883Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8f/0a60e501584145588be1af5cc829265701ba3c35a64aec8e07cbb71d39bb/multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd", size = 53507, upload-time = "2025-10-06T14:51:53.672Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/3148b988a9c6239903e786eac19c889fab607c31d6efa7fb2147e5680f23/multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827", size = 44804, upload-time = "2025-10-06T14:51:55.415Z" }, + { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, +] + +[[package]] +name = "mypy" +version = "1.18.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" }, + { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" }, + { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" }, + { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" }, + { url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" }, + { url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" }, + { url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" }, + { url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" }, + { url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" }, + { url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775, upload-time = "2025-09-19T00:10:03.814Z" }, + { url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852, upload-time = "2025-09-19T00:10:51.631Z" }, + { url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242, upload-time = "2025-09-19T00:11:07.955Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683, upload-time = "2025-09-19T00:09:55.572Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749, upload-time = "2025-09-19T00:10:44.827Z" }, + { url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959, upload-time = "2025-09-19T00:10:37.344Z" }, + { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "networkx" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload-time = "2025-10-15T16:18:11.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/7a/02420400b736f84317e759291b8edaeee9dc921f72b045475a9cbdb26b17/numpy-2.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef1b5a3e808bc40827b5fa2c8196151a4c5abe110e1726949d7abddfe5c7ae11", size = 20957727, upload-time = "2025-10-15T16:15:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/18/90/a014805d627aa5750f6f0e878172afb6454552da929144b3c07fcae1bb13/numpy-2.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2f91f496a87235c6aaf6d3f3d89b17dba64996abadccb289f48456cff931ca9", size = 14187262, upload-time = "2025-10-15T16:15:47.761Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e4/0a94b09abe89e500dc748e7515f21a13e30c5c3fe3396e6d4ac108c25fca/numpy-2.3.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f77e5b3d3da652b474cc80a14084927a5e86a5eccf54ca8ca5cbd697bf7f2667", size = 5115992, upload-time = "2025-10-15T16:15:50.144Z" }, + { url = "https://files.pythonhosted.org/packages/88/dd/db77c75b055c6157cbd4f9c92c4458daef0dd9cbe6d8d2fe7f803cb64c37/numpy-2.3.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ab1c5f5ee40d6e01cbe96de5863e39b215a4d24e7d007cad56c7184fdf4aeef", size = 6648672, upload-time = "2025-10-15T16:15:52.442Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e6/e31b0d713719610e406c0ea3ae0d90760465b086da8783e2fd835ad59027/numpy-2.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77b84453f3adcb994ddbd0d1c5d11db2d6bda1a2b7fd5ac5bd4649d6f5dc682e", size = 14284156, upload-time = "2025-10-15T16:15:54.351Z" }, + { url = "https://files.pythonhosted.org/packages/f9/58/30a85127bfee6f108282107caf8e06a1f0cc997cb6b52cdee699276fcce4/numpy-2.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4121c5beb58a7f9e6dfdee612cb24f4df5cd4db6e8261d7f4d7450a997a65d6a", size = 16641271, upload-time = "2025-10-15T16:15:56.67Z" }, + { url = "https://files.pythonhosted.org/packages/06/f2/2e06a0f2adf23e3ae29283ad96959267938d0efd20a2e25353b70065bfec/numpy-2.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65611ecbb00ac9846efe04db15cbe6186f562f6bb7e5e05f077e53a599225d16", size = 16059531, upload-time = "2025-10-15T16:15:59.412Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e7/b106253c7c0d5dc352b9c8fab91afd76a93950998167fa3e5afe4ef3a18f/numpy-2.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dabc42f9c6577bcc13001b8810d300fe814b4cfbe8a92c873f269484594f9786", size = 18578983, upload-time = "2025-10-15T16:16:01.804Z" }, + { url = "https://files.pythonhosted.org/packages/73/e3/04ecc41e71462276ee867ccbef26a4448638eadecf1bc56772c9ed6d0255/numpy-2.3.4-cp312-cp312-win32.whl", hash = "sha256:a49d797192a8d950ca59ee2d0337a4d804f713bb5c3c50e8db26d49666e351dc", size = 6291380, upload-time = "2025-10-15T16:16:03.938Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a8/566578b10d8d0e9955b1b6cd5db4e9d4592dd0026a941ff7994cedda030a/numpy-2.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:985f1e46358f06c2a09921e8921e2c98168ed4ae12ccd6e5e87a4f1857923f32", size = 12787999, upload-time = "2025-10-15T16:16:05.801Z" }, + { url = "https://files.pythonhosted.org/packages/58/22/9c903a957d0a8071b607f5b1bff0761d6e608b9a965945411f867d515db1/numpy-2.3.4-cp312-cp312-win_arm64.whl", hash = "sha256:4635239814149e06e2cb9db3dd584b2fa64316c96f10656983b8026a82e6e4db", size = 10197412, upload-time = "2025-10-15T16:16:07.854Z" }, + { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335, upload-time = "2025-10-15T16:16:10.304Z" }, + { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878, upload-time = "2025-10-15T16:16:12.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673, upload-time = "2025-10-15T16:16:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438, upload-time = "2025-10-15T16:16:16.805Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290, upload-time = "2025-10-15T16:16:18.764Z" }, + { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543, upload-time = "2025-10-15T16:16:21.072Z" }, + { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117, upload-time = "2025-10-15T16:16:23.369Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788, upload-time = "2025-10-15T16:16:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620, upload-time = "2025-10-15T16:16:29.811Z" }, + { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672, upload-time = "2025-10-15T16:16:31.589Z" }, + { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702, upload-time = "2025-10-15T16:16:33.902Z" }, + { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003, upload-time = "2025-10-15T16:16:36.101Z" }, + { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980, upload-time = "2025-10-15T16:16:39.124Z" }, + { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472, upload-time = "2025-10-15T16:16:41.168Z" }, + { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342, upload-time = "2025-10-15T16:16:43.777Z" }, + { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338, upload-time = "2025-10-15T16:16:46.081Z" }, + { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392, upload-time = "2025-10-15T16:16:48.455Z" }, + { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998, upload-time = "2025-10-15T16:16:51.114Z" }, + { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574, upload-time = "2025-10-15T16:16:53.429Z" }, + { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135, upload-time = "2025-10-15T16:16:55.992Z" }, + { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582, upload-time = "2025-10-15T16:16:57.943Z" }, + { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691, upload-time = "2025-10-15T16:17:00.048Z" }, + { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580, upload-time = "2025-10-15T16:17:02.509Z" }, + { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056, upload-time = "2025-10-15T16:17:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555, upload-time = "2025-10-15T16:17:07.499Z" }, + { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581, upload-time = "2025-10-15T16:17:09.774Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186, upload-time = "2025-10-15T16:17:11.937Z" }, + { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601, upload-time = "2025-10-15T16:17:14.391Z" }, + { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219, upload-time = "2025-10-15T16:17:17.058Z" }, + { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702, upload-time = "2025-10-15T16:17:19.379Z" }, + { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136, upload-time = "2025-10-15T16:17:22.886Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542, upload-time = "2025-10-15T16:17:24.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213, upload-time = "2025-10-15T16:17:26.935Z" }, + { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280, upload-time = "2025-10-15T16:17:29.638Z" }, + { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930, upload-time = "2025-10-15T16:17:32.384Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504, upload-time = "2025-10-15T16:17:34.515Z" }, + { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405, upload-time = "2025-10-15T16:17:36.128Z" }, + { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866, upload-time = "2025-10-15T16:17:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296, upload-time = "2025-10-15T16:17:41.564Z" }, + { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046, upload-time = "2025-10-15T16:17:43.901Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691, upload-time = "2025-10-15T16:17:46.247Z" }, + { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782, upload-time = "2025-10-15T16:17:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301, upload-time = "2025-10-15T16:17:50.938Z" }, + { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532, upload-time = "2025-10-15T16:17:53.48Z" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, +] + +[[package]] +name = "nvidia-ml-py" +version = "13.580.82" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/6c/4a533f2c0185027c465adb6063086bc3728301e95f483665bfa9ebafb2d3/nvidia_ml_py-13.580.82.tar.gz", hash = "sha256:0c028805dc53a0e2a6985ea801888197765ac2ef8f1c9e29a7bf0d3616a5efc7", size = 47999, upload-time = "2025-09-11T16:44:56.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/96/d6d25a4c307d6645f4a9b91d620c0151c544ad38b5e371313a87d2761004/nvidia_ml_py-13.580.82-py3-none-any.whl", hash = "sha256:4361db337b0c551e2d101936dae2e9a60f957af26818e8c0c3a1f32b8db8d0a7", size = 49008, upload-time = "2025-09-11T16:44:54.915Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.3.20" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145, upload-time = "2025-08-04T20:25:19.995Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, +] + +[[package]] +name = "openai" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/44/303deb97be7c1c9b53118b52825cbd1557aeeff510f3a52566b1fa66f6a2/openai-2.6.1.tar.gz", hash = "sha256:27ae704d190615fca0c0fc2b796a38f8b5879645a3a52c9c453b23f97141bb49", size = 593043, upload-time = "2025-10-24T13:29:52.79Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/0e/331df43df633e6105ff9cf45e0ce57762bd126a45ac16b25a43f6738d8a2/openai-2.6.1-py3-none-any.whl", hash = "sha256:904e4b5254a8416746a2f05649594fa41b19d799843cd134dac86167e094edef", size = 1005551, upload-time = "2025-10-24T13:29:50.973Z" }, +] + +[[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 = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "phrasetree" +version = "0.0.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/31/b9d9df3cddabd2f9da83f38f5521cc0cd74d204487ef1bd020f48d7ea737/phrasetree-0.0.9.tar.gz", hash = "sha256:cd0fc8f64be43b4742953f4a8172e369c805b5bdd70a6325f532465032b506e7", size = 42176, upload-time = "2024-03-23T02:44:25.348Z" } + +[[package]] +name = "platformdirs" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "pycparser" +version = "2.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/1e/4f0a3233767010308f2fd6bd0814597e3f63f1dc98304a9112b8759df4ff/pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74", size = 819383, upload-time = "2025-10-17T15:04:21.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/6b/83661fa77dcefa195ad5f8cd9af3d1a7450fd57cc883ad04d65446ac2029/pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf", size = 462431, upload-time = "2025-10-17T15:04:19.346Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/18/d0944e8eaaa3efd0a91b0f1fc537d3be55ad35091b6a87638211ba691964/pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5", size = 457557, upload-time = "2025-10-14T10:23:47.909Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/81/d3b3e95929c4369d30b2a66a91db63c8ed0a98381ae55a45da2cd1cc1288/pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887", size = 2099043, upload-time = "2025-10-14T10:20:28.561Z" }, + { url = "https://files.pythonhosted.org/packages/58/da/46fdac49e6717e3a94fc9201403e08d9d61aa7a770fab6190b8740749047/pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2", size = 1910699, upload-time = "2025-10-14T10:20:30.217Z" }, + { url = "https://files.pythonhosted.org/packages/1e/63/4d948f1b9dd8e991a5a98b77dd66c74641f5f2e5225fee37994b2e07d391/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999", size = 1952121, upload-time = "2025-10-14T10:20:32.246Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a7/e5fc60a6f781fc634ecaa9ecc3c20171d238794cef69ae0af79ac11b89d7/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4", size = 2041590, upload-time = "2025-10-14T10:20:34.332Z" }, + { url = "https://files.pythonhosted.org/packages/70/69/dce747b1d21d59e85af433428978a1893c6f8a7068fa2bb4a927fba7a5ff/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f", size = 2219869, upload-time = "2025-10-14T10:20:35.965Z" }, + { url = "https://files.pythonhosted.org/packages/83/6a/c070e30e295403bf29c4df1cb781317b6a9bac7cd07b8d3acc94d501a63c/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b", size = 2345169, upload-time = "2025-10-14T10:20:37.627Z" }, + { url = "https://files.pythonhosted.org/packages/f0/83/06d001f8043c336baea7fd202a9ac7ad71f87e1c55d8112c50b745c40324/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47", size = 2070165, upload-time = "2025-10-14T10:20:39.246Z" }, + { url = "https://files.pythonhosted.org/packages/14/0a/e567c2883588dd12bcbc110232d892cf385356f7c8a9910311ac997ab715/pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970", size = 2189067, upload-time = "2025-10-14T10:20:41.015Z" }, + { url = "https://files.pythonhosted.org/packages/f4/1d/3d9fca34273ba03c9b1c5289f7618bc4bd09c3ad2289b5420481aa051a99/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed", size = 2132997, upload-time = "2025-10-14T10:20:43.106Z" }, + { url = "https://files.pythonhosted.org/packages/52/70/d702ef7a6cd41a8afc61f3554922b3ed8d19dd54c3bd4bdbfe332e610827/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8", size = 2307187, upload-time = "2025-10-14T10:20:44.849Z" }, + { url = "https://files.pythonhosted.org/packages/68/4c/c06be6e27545d08b802127914156f38d10ca287a9e8489342793de8aae3c/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431", size = 2305204, upload-time = "2025-10-14T10:20:46.781Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e5/35ae4919bcd9f18603419e23c5eaf32750224a89d41a8df1a3704b69f77e/pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd", size = 1972536, upload-time = "2025-10-14T10:20:48.39Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c2/49c5bb6d2a49eb2ee3647a93e3dae7080c6409a8a7558b075027644e879c/pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff", size = 2031132, upload-time = "2025-10-14T10:20:50.421Z" }, + { url = "https://files.pythonhosted.org/packages/06/23/936343dbcba6eec93f73e95eb346810fc732f71ba27967b287b66f7b7097/pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8", size = 1969483, upload-time = "2025-10-14T10:20:52.35Z" }, + { url = "https://files.pythonhosted.org/packages/13/d0/c20adabd181a029a970738dfe23710b52a31f1258f591874fcdec7359845/pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746", size = 2105688, upload-time = "2025-10-14T10:20:54.448Z" }, + { url = "https://files.pythonhosted.org/packages/00/b6/0ce5c03cec5ae94cca220dfecddc453c077d71363b98a4bbdb3c0b22c783/pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced", size = 1910807, upload-time = "2025-10-14T10:20:56.115Z" }, + { url = "https://files.pythonhosted.org/packages/68/3e/800d3d02c8beb0b5c069c870cbb83799d085debf43499c897bb4b4aaff0d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a", size = 1956669, upload-time = "2025-10-14T10:20:57.874Z" }, + { url = "https://files.pythonhosted.org/packages/60/a4/24271cc71a17f64589be49ab8bd0751f6a0a03046c690df60989f2f95c2c/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02", size = 2051629, upload-time = "2025-10-14T10:21:00.006Z" }, + { url = "https://files.pythonhosted.org/packages/68/de/45af3ca2f175d91b96bfb62e1f2d2f1f9f3b14a734afe0bfeff079f78181/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1", size = 2224049, upload-time = "2025-10-14T10:21:01.801Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/ae4e1ff84672bf869d0a77af24fd78387850e9497753c432875066b5d622/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2", size = 2342409, upload-time = "2025-10-14T10:21:03.556Z" }, + { url = "https://files.pythonhosted.org/packages/18/62/273dd70b0026a085c7b74b000394e1ef95719ea579c76ea2f0cc8893736d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84", size = 2069635, upload-time = "2025-10-14T10:21:05.385Z" }, + { url = "https://files.pythonhosted.org/packages/30/03/cf485fff699b4cdaea469bc481719d3e49f023241b4abb656f8d422189fc/pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d", size = 2194284, upload-time = "2025-10-14T10:21:07.122Z" }, + { url = "https://files.pythonhosted.org/packages/f9/7e/c8e713db32405dfd97211f2fc0a15d6bf8adb7640f3d18544c1f39526619/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d", size = 2137566, upload-time = "2025-10-14T10:21:08.981Z" }, + { url = "https://files.pythonhosted.org/packages/04/f7/db71fd4cdccc8b75990f79ccafbbd66757e19f6d5ee724a6252414483fb4/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2", size = 2316809, upload-time = "2025-10-14T10:21:10.805Z" }, + { url = "https://files.pythonhosted.org/packages/76/63/a54973ddb945f1bca56742b48b144d85c9fc22f819ddeb9f861c249d5464/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab", size = 2311119, upload-time = "2025-10-14T10:21:12.583Z" }, + { url = "https://files.pythonhosted.org/packages/f8/03/5d12891e93c19218af74843a27e32b94922195ded2386f7b55382f904d2f/pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c", size = 1981398, upload-time = "2025-10-14T10:21:14.584Z" }, + { url = "https://files.pythonhosted.org/packages/be/d8/fd0de71f39db91135b7a26996160de71c073d8635edfce8b3c3681be0d6d/pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4", size = 2030735, upload-time = "2025-10-14T10:21:16.432Z" }, + { url = "https://files.pythonhosted.org/packages/72/86/c99921c1cf6650023c08bfab6fe2d7057a5142628ef7ccfa9921f2dda1d5/pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564", size = 1973209, upload-time = "2025-10-14T10:21:18.213Z" }, + { url = "https://files.pythonhosted.org/packages/36/0d/b5706cacb70a8414396efdda3d72ae0542e050b591119e458e2490baf035/pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4", size = 1877324, upload-time = "2025-10-14T10:21:20.363Z" }, + { url = "https://files.pythonhosted.org/packages/de/2d/cba1fa02cfdea72dfb3a9babb067c83b9dff0bbcb198368e000a6b756ea7/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2", size = 1884515, upload-time = "2025-10-14T10:21:22.339Z" }, + { url = "https://files.pythonhosted.org/packages/07/ea/3df927c4384ed9b503c9cc2d076cf983b4f2adb0c754578dfb1245c51e46/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf", size = 2042819, upload-time = "2025-10-14T10:21:26.683Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ee/df8e871f07074250270a3b1b82aad4cd0026b588acd5d7d3eb2fcb1471a3/pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2", size = 1995866, upload-time = "2025-10-14T10:21:28.951Z" }, + { url = "https://files.pythonhosted.org/packages/fc/de/b20f4ab954d6d399499c33ec4fafc46d9551e11dc1858fb7f5dca0748ceb/pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89", size = 1970034, upload-time = "2025-10-14T10:21:30.869Z" }, + { url = "https://files.pythonhosted.org/packages/54/28/d3325da57d413b9819365546eb9a6e8b7cbd9373d9380efd5f74326143e6/pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1", size = 2102022, upload-time = "2025-10-14T10:21:32.809Z" }, + { url = "https://files.pythonhosted.org/packages/9e/24/b58a1bc0d834bf1acc4361e61233ee217169a42efbdc15a60296e13ce438/pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac", size = 1905495, upload-time = "2025-10-14T10:21:34.812Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a4/71f759cc41b7043e8ecdaab81b985a9b6cad7cec077e0b92cff8b71ecf6b/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554", size = 1956131, upload-time = "2025-10-14T10:21:36.924Z" }, + { url = "https://files.pythonhosted.org/packages/b0/64/1e79ac7aa51f1eec7c4cda8cbe456d5d09f05fdd68b32776d72168d54275/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e", size = 2052236, upload-time = "2025-10-14T10:21:38.927Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e3/a3ffc363bd4287b80f1d43dc1c28ba64831f8dfc237d6fec8f2661138d48/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616", size = 2223573, upload-time = "2025-10-14T10:21:41.574Z" }, + { url = "https://files.pythonhosted.org/packages/28/27/78814089b4d2e684a9088ede3790763c64693c3d1408ddc0a248bc789126/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af", size = 2342467, upload-time = "2025-10-14T10:21:44.018Z" }, + { url = "https://files.pythonhosted.org/packages/92/97/4de0e2a1159cb85ad737e03306717637842c88c7fd6d97973172fb183149/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12", size = 2063754, upload-time = "2025-10-14T10:21:46.466Z" }, + { url = "https://files.pythonhosted.org/packages/0f/50/8cb90ce4b9efcf7ae78130afeb99fd1c86125ccdf9906ef64b9d42f37c25/pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d", size = 2196754, upload-time = "2025-10-14T10:21:48.486Z" }, + { url = "https://files.pythonhosted.org/packages/34/3b/ccdc77af9cd5082723574a1cc1bcae7a6acacc829d7c0a06201f7886a109/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad", size = 2137115, upload-time = "2025-10-14T10:21:50.63Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ba/e7c7a02651a8f7c52dc2cff2b64a30c313e3b57c7d93703cecea76c09b71/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a", size = 2317400, upload-time = "2025-10-14T10:21:52.959Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ba/6c533a4ee8aec6b812c643c49bb3bd88d3f01e3cebe451bb85512d37f00f/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025", size = 2312070, upload-time = "2025-10-14T10:21:55.419Z" }, + { url = "https://files.pythonhosted.org/packages/22/ae/f10524fcc0ab8d7f96cf9a74c880243576fd3e72bd8ce4f81e43d22bcab7/pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e", size = 1982277, upload-time = "2025-10-14T10:21:57.474Z" }, + { url = "https://files.pythonhosted.org/packages/b4/dc/e5aa27aea1ad4638f0c3fb41132f7eb583bd7420ee63204e2d4333a3bbf9/pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894", size = 2024608, upload-time = "2025-10-14T10:21:59.557Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/51d89cc2612bd147198e120a13f150afbf0bcb4615cddb049ab10b81b79e/pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d", size = 1967614, upload-time = "2025-10-14T10:22:01.847Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c2/472f2e31b95eff099961fa050c376ab7156a81da194f9edb9f710f68787b/pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da", size = 1876904, upload-time = "2025-10-14T10:22:04.062Z" }, + { url = "https://files.pythonhosted.org/packages/4a/07/ea8eeb91173807ecdae4f4a5f4b150a520085b35454350fc219ba79e66a3/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e", size = 1882538, upload-time = "2025-10-14T10:22:06.39Z" }, + { url = "https://files.pythonhosted.org/packages/1e/29/b53a9ca6cd366bfc928823679c6a76c7a4c69f8201c0ba7903ad18ebae2f/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa", size = 2041183, upload-time = "2025-10-14T10:22:08.812Z" }, + { url = "https://files.pythonhosted.org/packages/c7/3d/f8c1a371ceebcaf94d6dd2d77c6cf4b1c078e13a5837aee83f760b4f7cfd/pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d", size = 1993542, upload-time = "2025-10-14T10:22:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0", size = 1973897, upload-time = "2025-10-14T10:22:13.444Z" }, + { url = "https://files.pythonhosted.org/packages/c4/48/ae937e5a831b7c0dc646b2ef788c27cd003894882415300ed21927c21efa/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537", size = 2112087, upload-time = "2025-10-14T10:22:56.818Z" }, + { url = "https://files.pythonhosted.org/packages/5e/db/6db8073e3d32dae017da7e0d16a9ecb897d0a4d92e00634916e486097961/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94", size = 1920387, upload-time = "2025-10-14T10:22:59.342Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c1/dd3542d072fcc336030d66834872f0328727e3b8de289c662faa04aa270e/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c", size = 1951495, upload-time = "2025-10-14T10:23:02.089Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c6/db8d13a1f8ab3f1eb08c88bd00fd62d44311e3456d1e85c0e59e0a0376e7/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335", size = 2139008, upload-time = "2025-10-14T10:23:04.539Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/72/8259b2bccfe4673330cea843ab23f86858a419d8f1493f66d413a76c7e3b/PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de", size = 78313, upload-time = "2023-07-18T20:02:22.594Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/4f/e04a8067c7c96c364cef7ef73906504e2f40d690811c021e1a1901473a19/PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320", size = 22591, upload-time = "2023-07-18T20:02:21.561Z" }, +] + +[[package]] +name = "pynvml" +version = "13.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-ml-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/57/da7dc63a79f59e082e26a66ac02d87d69ea316b35b35b7a00d82f3ce3d2f/pynvml-13.0.1.tar.gz", hash = "sha256:1245991d9db786b4d2f277ce66869bd58f38ac654e38c9397d18f243c8f6e48f", size = 35226, upload-time = "2025-09-05T20:33:25.377Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/4a/cac76c174bb439a0c46c9a4413fcbea5c6cabfb01879f7bbdb9fdfaed76c/pynvml-13.0.1-py3-none-any.whl", hash = "sha256:e2b20e0a501eeec951e2455b7ab444759cf048e0e13a57b08049fa2775266aa8", size = 28810, upload-time = "2025-09-05T20:33:24.13Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-docx" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lxml" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, +] + +[[package]] +name = "python-igraph" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "igraph" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/b6/3c1476ec6bf06348ecea1d76af6c6b3a024dddbcd2de120d3a0de1eab29b/python_igraph-1.0.0.tar.gz", hash = "sha256:6da7ac4e9f6a9cf03734798bed6fb082bb9274f2ae288f6099121f0332803018", size = 9726, upload-time = "2025-10-23T12:28:36.266Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/5f/567fa047076d32d321bdafa248905a7186d2acee4916b046b11417af10d9/python_igraph-1.0.0-py3-none-any.whl", hash = "sha256:b0bb8d91cce2a1e0550fe45f9ec925bcd04152dfb5be3fa822b9f5f36eb3f380", size = 9157, upload-time = "2025-10-23T12:28:33.515Z" }, +] + +[[package]] +name = "pytokens" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/c2/dbadcdddb412a267585459142bfd7cc241e6276db69339353ae6e241ab2b/pytokens-0.2.0.tar.gz", hash = "sha256:532d6421364e5869ea57a9523bf385f02586d4662acbcc0342afd69511b4dd43", size = 15368, upload-time = "2025-10-15T08:02:42.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/5a/c269ea6b348b6f2c32686635df89f32dbe05df1088dd4579302a6f8f99af/pytokens-0.2.0-py3-none-any.whl", hash = "sha256:74d4b318c67f4295c13782ddd9abcb7e297ec5630ad060eb90abf7ebbefe59f8", size = 12038, upload-time = "2025-10-15T08:02:41.694Z" }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +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/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { 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 = "2025.10.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/c8/1d2160d36b11fbe0a61acb7c3c81ab032d9ec8ad888ac9e0a61b85ab99dd/regex-2025.10.23.tar.gz", hash = "sha256:8cbaf8ceb88f96ae2356d01b9adf5e6306fa42fa6f7eab6b97794e37c959ac26", size = 401266, upload-time = "2025-10-21T15:58:20.23Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/57/eeb274d83ab189d02d778851b1ac478477522a92b52edfa6e2ae9ff84679/regex-2025.10.23-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7a44d9c00f7a0a02d3b777429281376370f3d13d2c75ae74eb94e11ebcf4a7fc", size = 489187, upload-time = "2025-10-21T15:55:18.322Z" }, + { url = "https://files.pythonhosted.org/packages/55/5c/7dad43a9b6ea88bf77e0b8b7729a4c36978e1043165034212fd2702880c6/regex-2025.10.23-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b83601f84fde939ae3478bb32a3aef36f61b58c3208d825c7e8ce1a735f143f2", size = 291122, upload-time = "2025-10-21T15:55:20.2Z" }, + { url = "https://files.pythonhosted.org/packages/66/21/38b71e6f2818f0f4b281c8fba8d9d57cfca7b032a648fa59696e0a54376a/regex-2025.10.23-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec13647907bb9d15fd192bbfe89ff06612e098a5709e7d6ecabbdd8f7908fc45", size = 288797, upload-time = "2025-10-21T15:55:21.932Z" }, + { url = "https://files.pythonhosted.org/packages/be/95/888f069c89e7729732a6d7cca37f76b44bfb53a1e35dda8a2c7b65c1b992/regex-2025.10.23-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78d76dd2957d62501084e7012ddafc5fcd406dd982b7a9ca1ea76e8eaaf73e7e", size = 798442, upload-time = "2025-10-21T15:55:23.747Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/4f903c608faf786627a8ee17c06e0067b5acade473678b69c8094b248705/regex-2025.10.23-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8668e5f067e31a47699ebb354f43aeb9c0ef136f915bd864243098524482ac43", size = 864039, upload-time = "2025-10-21T15:55:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/62/19/2df67b526bf25756c7f447dde554fc10a220fd839cc642f50857d01e4a7b/regex-2025.10.23-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a32433fe3deb4b2d8eda88790d2808fed0dc097e84f5e683b4cd4f42edef6cca", size = 912057, upload-time = "2025-10-21T15:55:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/99/14/9a39b7c9e007968411bc3c843cc14cf15437510c0a9991f080cab654fd16/regex-2025.10.23-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d97d73818c642c938db14c0668167f8d39520ca9d983604575ade3fda193afcc", size = 803374, upload-time = "2025-10-21T15:55:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f7/3495151dd3ca79949599b6d069b72a61a2c5e24fc441dccc79dcaf708fe6/regex-2025.10.23-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bca7feecc72ee33579e9f6ddf8babbe473045717a0e7dbc347099530f96e8b9a", size = 787714, upload-time = "2025-10-21T15:55:30.628Z" }, + { url = "https://files.pythonhosted.org/packages/28/65/ee882455e051131869957ee8597faea45188c9a98c0dad724cfb302d4580/regex-2025.10.23-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7e24af51e907d7457cc4a72691ec458320b9ae67dc492f63209f01eecb09de32", size = 858392, upload-time = "2025-10-21T15:55:32.322Z" }, + { url = "https://files.pythonhosted.org/packages/53/25/9287fef5be97529ebd3ac79d256159cb709a07eb58d4be780d1ca3885da8/regex-2025.10.23-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d10bcde58bbdf18146f3a69ec46dd03233b94a4a5632af97aa5378da3a47d288", size = 850484, upload-time = "2025-10-21T15:55:34.037Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b4/b49b88b4fea2f14dc73e5b5842755e782fc2e52f74423d6f4adc130d5880/regex-2025.10.23-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:44383bc0c933388516c2692c9a7503e1f4a67e982f20b9a29d2fb70c6494f147", size = 789634, upload-time = "2025-10-21T15:55:35.958Z" }, + { url = "https://files.pythonhosted.org/packages/b6/3c/2f8d199d0e84e78bcd6bdc2be9b62410624f6b796e2893d1837ae738b160/regex-2025.10.23-cp312-cp312-win32.whl", hash = "sha256:6040a86f95438a0114bba16e51dfe27f1bc004fd29fe725f54a586f6d522b079", size = 266060, upload-time = "2025-10-21T15:55:37.902Z" }, + { url = "https://files.pythonhosted.org/packages/d7/67/c35e80969f6ded306ad70b0698863310bdf36aca57ad792f45ddc0e2271f/regex-2025.10.23-cp312-cp312-win_amd64.whl", hash = "sha256:436b4c4352fe0762e3bfa34a5567079baa2ef22aa9c37cf4d128979ccfcad842", size = 276931, upload-time = "2025-10-21T15:55:39.502Z" }, + { url = "https://files.pythonhosted.org/packages/f5/a1/4ed147de7d2b60174f758412c87fa51ada15cd3296a0ff047f4280aaa7ca/regex-2025.10.23-cp312-cp312-win_arm64.whl", hash = "sha256:f4b1b1991617055b46aff6f6db24888c1f05f4db9801349d23f09ed0714a9335", size = 270103, upload-time = "2025-10-21T15:55:41.24Z" }, + { url = "https://files.pythonhosted.org/packages/28/c6/195a6217a43719d5a6a12cc192a22d12c40290cecfa577f00f4fb822f07d/regex-2025.10.23-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b7690f95404a1293923a296981fd943cca12c31a41af9c21ba3edd06398fc193", size = 488956, upload-time = "2025-10-21T15:55:42.887Z" }, + { url = "https://files.pythonhosted.org/packages/4c/93/181070cd1aa2fa541ff2d3afcf763ceecd4937b34c615fa92765020a6c90/regex-2025.10.23-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1a32d77aeaea58a13230100dd8797ac1a84c457f3af2fdf0d81ea689d5a9105b", size = 290997, upload-time = "2025-10-21T15:55:44.53Z" }, + { url = "https://files.pythonhosted.org/packages/b6/c5/9d37fbe3a40ed8dda78c23e1263002497540c0d1522ed75482ef6c2000f0/regex-2025.10.23-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b24b29402f264f70a3c81f45974323b41764ff7159655360543b7cabb73e7d2f", size = 288686, upload-time = "2025-10-21T15:55:46.186Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e7/db610ff9f10c2921f9b6ac0c8d8be4681b28ddd40fc0549429366967e61f/regex-2025.10.23-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:563824a08c7c03d96856d84b46fdb3bbb7cfbdf79da7ef68725cda2ce169c72a", size = 798466, upload-time = "2025-10-21T15:55:48.24Z" }, + { url = "https://files.pythonhosted.org/packages/90/10/aab883e1fa7fe2feb15ac663026e70ca0ae1411efa0c7a4a0342d9545015/regex-2025.10.23-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0ec8bdd88d2e2659c3518087ee34b37e20bd169419ffead4240a7004e8ed03b", size = 863996, upload-time = "2025-10-21T15:55:50.478Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b0/8f686dd97a51f3b37d0238cd00a6d0f9ccabe701f05b56de1918571d0d61/regex-2025.10.23-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b577601bfe1d33913fcd9276d7607bbac827c4798d9e14d04bf37d417a6c41cb", size = 912145, upload-time = "2025-10-21T15:55:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ca/639f8cd5b08797bca38fc5e7e07f76641a428cf8c7fca05894caf045aa32/regex-2025.10.23-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c9f2c68ac6cb3de94eea08a437a75eaa2bd33f9e97c84836ca0b610a5804368", size = 803370, upload-time = "2025-10-21T15:55:53.944Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1e/a40725bb76959eddf8abc42a967bed6f4851b39f5ac4f20e9794d7832aa5/regex-2025.10.23-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89f8b9ea3830c79468e26b0e21c3585f69f105157c2154a36f6b7839f8afb351", size = 787767, upload-time = "2025-10-21T15:55:56.004Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d8/8ee9858062936b0f99656dce390aa667c6e7fb0c357b1b9bf76fb5e2e708/regex-2025.10.23-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:98fd84c4e4ea185b3bb5bf065261ab45867d8875032f358a435647285c722673", size = 858335, upload-time = "2025-10-21T15:55:58.185Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0a/ed5faaa63fa8e3064ab670e08061fbf09e3a10235b19630cf0cbb9e48c0a/regex-2025.10.23-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1e11d3e5887b8b096f96b4154dfb902f29c723a9556639586cd140e77e28b313", size = 850402, upload-time = "2025-10-21T15:56:00.023Z" }, + { url = "https://files.pythonhosted.org/packages/79/14/d05f617342f4b2b4a23561da500ca2beab062bfcc408d60680e77ecaf04d/regex-2025.10.23-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f13450328a6634348d47a88367e06b64c9d84980ef6a748f717b13f8ce64e87", size = 789739, upload-time = "2025-10-21T15:56:01.967Z" }, + { url = "https://files.pythonhosted.org/packages/f9/7b/e8ce8eef42a15f2c3461f8b3e6e924bbc86e9605cb534a393aadc8d3aff8/regex-2025.10.23-cp313-cp313-win32.whl", hash = "sha256:37be9296598a30c6a20236248cb8b2c07ffd54d095b75d3a2a2ee5babdc51df1", size = 266054, upload-time = "2025-10-21T15:56:05.291Z" }, + { url = "https://files.pythonhosted.org/packages/71/2d/55184ed6be6473187868d2f2e6a0708195fc58270e62a22cbf26028f2570/regex-2025.10.23-cp313-cp313-win_amd64.whl", hash = "sha256:ea7a3c283ce0f06fe789365841e9174ba05f8db16e2fd6ae00a02df9572c04c0", size = 276917, upload-time = "2025-10-21T15:56:07.303Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d4/927eced0e2bd45c45839e556f987f8c8f8683268dd3c00ad327deb3b0172/regex-2025.10.23-cp313-cp313-win_arm64.whl", hash = "sha256:d9a4953575f300a7bab71afa4cd4ac061c7697c89590a2902b536783eeb49a4f", size = 270105, upload-time = "2025-10-21T15:56:09.857Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b3/95b310605285573341fc062d1d30b19a54f857530e86c805f942c4ff7941/regex-2025.10.23-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7d6606524fa77b3912c9ef52a42ef63c6cfbfc1077e9dc6296cd5da0da286044", size = 491850, upload-time = "2025-10-21T15:56:11.685Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8f/207c2cec01e34e56db1eff606eef46644a60cf1739ecd474627db90ad90b/regex-2025.10.23-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c037aadf4d64bdc38af7db3dbd34877a057ce6524eefcb2914d6d41c56f968cc", size = 292537, upload-time = "2025-10-21T15:56:13.963Z" }, + { url = "https://files.pythonhosted.org/packages/98/3b/025240af4ada1dc0b5f10d73f3e5122d04ce7f8908ab8881e5d82b9d61b6/regex-2025.10.23-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:99018c331fb2529084a0c9b4c713dfa49fafb47c7712422e49467c13a636c656", size = 290904, upload-time = "2025-10-21T15:56:16.016Z" }, + { url = "https://files.pythonhosted.org/packages/81/8e/104ac14e2d3450c43db18ec03e1b96b445a94ae510b60138f00ce2cb7ca1/regex-2025.10.23-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd8aba965604d70306eb90a35528f776e59112a7114a5162824d43b76fa27f58", size = 807311, upload-time = "2025-10-21T15:56:17.818Z" }, + { url = "https://files.pythonhosted.org/packages/19/63/78aef90141b7ce0be8a18e1782f764f6997ad09de0e05251f0d2503a914a/regex-2025.10.23-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:238e67264b4013e74136c49f883734f68656adf8257bfa13b515626b31b20f8e", size = 873241, upload-time = "2025-10-21T15:56:19.941Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a8/80eb1201bb49ae4dba68a1b284b4211ed9daa8e74dc600018a10a90399fb/regex-2025.10.23-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b2eb48bd9848d66fd04826382f5e8491ae633de3233a3d64d58ceb4ecfa2113a", size = 914794, upload-time = "2025-10-21T15:56:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d5/1984b6ee93281f360a119a5ca1af6a8ca7d8417861671388bf750becc29b/regex-2025.10.23-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d36591ce06d047d0c0fe2fc5f14bfbd5b4525d08a7b6a279379085e13f0e3d0e", size = 812581, upload-time = "2025-10-21T15:56:24.319Z" }, + { url = "https://files.pythonhosted.org/packages/c4/39/11ebdc6d9927172a64ae237d16763145db6bd45ebb4055c17b88edab72a7/regex-2025.10.23-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5d4ece8628d6e364302006366cea3ee887db397faebacc5dacf8ef19e064cf8", size = 795346, upload-time = "2025-10-21T15:56:26.232Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b4/89a591bcc08b5e436af43315284bd233ba77daf0cf20e098d7af12f006c1/regex-2025.10.23-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:39a7e8083959cb1c4ff74e483eecb5a65d3b3e1d821b256e54baf61782c906c6", size = 868214, upload-time = "2025-10-21T15:56:28.597Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ff/58ba98409c1dbc8316cdb20dafbc63ed267380a07780cafecaf5012dabc9/regex-2025.10.23-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:842d449a8fefe546f311656cf8c0d6729b08c09a185f1cad94c756210286d6a8", size = 854540, upload-time = "2025-10-21T15:56:30.875Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f2/4a9e9338d67626e2071b643f828a482712ad15889d7268e11e9a63d6f7e9/regex-2025.10.23-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d614986dc68506be8f00474f4f6960e03e4ca9883f7df47744800e7d7c08a494", size = 799346, upload-time = "2025-10-21T15:56:32.725Z" }, + { url = "https://files.pythonhosted.org/packages/63/be/543d35c46bebf6f7bf2be538cca74d6585f25714700c36f37f01b92df551/regex-2025.10.23-cp313-cp313t-win32.whl", hash = "sha256:a5b7a26b51a9df473ec16a1934d117443a775ceb7b39b78670b2e21893c330c9", size = 268657, upload-time = "2025-10-21T15:56:34.577Z" }, + { url = "https://files.pythonhosted.org/packages/14/9f/4dd6b7b612037158bb2c9bcaa710e6fb3c40ad54af441b9c53b3a137a9f1/regex-2025.10.23-cp313-cp313t-win_amd64.whl", hash = "sha256:ce81c5544a5453f61cb6f548ed358cfb111e3b23f3cd42d250a4077a6be2a7b6", size = 280075, upload-time = "2025-10-21T15:56:36.767Z" }, + { url = "https://files.pythonhosted.org/packages/81/7a/5bd0672aa65d38c8da6747c17c8b441bdb53d816c569e3261013af8e83cf/regex-2025.10.23-cp313-cp313t-win_arm64.whl", hash = "sha256:e9bf7f6699f490e4e43c44757aa179dab24d1960999c84ab5c3d5377714ed473", size = 271219, upload-time = "2025-10-21T15:56:39.033Z" }, + { url = "https://files.pythonhosted.org/packages/73/f6/0caf29fec943f201fbc8822879c99d31e59c1d51a983d9843ee5cf398539/regex-2025.10.23-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5b5cb5b6344c4c4c24b2dc87b0bfee78202b07ef7633385df70da7fcf6f7cec6", size = 488960, upload-time = "2025-10-21T15:56:40.849Z" }, + { url = "https://files.pythonhosted.org/packages/8e/7d/ebb7085b8fa31c24ce0355107cea2b92229d9050552a01c5d291c42aecea/regex-2025.10.23-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a6ce7973384c37bdf0f371a843f95a6e6f4e1489e10e0cf57330198df72959c5", size = 290932, upload-time = "2025-10-21T15:56:42.875Z" }, + { url = "https://files.pythonhosted.org/packages/27/41/43906867287cbb5ca4cee671c3cc8081e15deef86a8189c3aad9ac9f6b4d/regex-2025.10.23-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2ee3663f2c334959016b56e3bd0dd187cbc73f948e3a3af14c3caaa0c3035d10", size = 288766, upload-time = "2025-10-21T15:56:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/ab/9e/ea66132776700fc77a39b1056e7a5f1308032fead94507e208dc6716b7cd/regex-2025.10.23-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2003cc82a579107e70d013482acce8ba773293f2db534fb532738395c557ff34", size = 798884, upload-time = "2025-10-21T15:56:47.178Z" }, + { url = "https://files.pythonhosted.org/packages/d5/99/aed1453687ab63819a443930770db972c5c8064421f0d9f5da9ad029f26b/regex-2025.10.23-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:182c452279365a93a9f45874f7f191ec1c51e1f1eb41bf2b16563f1a40c1da3a", size = 864768, upload-time = "2025-10-21T15:56:49.793Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/732fe747a1304805eb3853ce6337eea16b169f7105a0d0dd9c6a5ffa9948/regex-2025.10.23-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b1249e9ff581c5b658c8f0437f883b01f1edcf424a16388591e7c05e5e9e8b0c", size = 911394, upload-time = "2025-10-21T15:56:52.186Z" }, + { url = "https://files.pythonhosted.org/packages/5e/48/58a1f6623466522352a6efa153b9a3714fc559d9f930e9bc947b4a88a2c3/regex-2025.10.23-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b841698f93db3ccc36caa1900d2a3be281d9539b822dc012f08fc80b46a3224", size = 803145, upload-time = "2025-10-21T15:56:55.142Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f6/7dea79be2681a5574ab3fc237aa53b2c1dfd6bd2b44d4640b6c76f33f4c1/regex-2025.10.23-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:956d89e0c92d471e8f7eee73f73fdff5ed345886378c45a43175a77538a1ffe4", size = 787831, upload-time = "2025-10-21T15:56:57.203Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ad/07b76950fbbe65f88120ca2d8d845047c401450f607c99ed38862904671d/regex-2025.10.23-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5c259cb363299a0d90d63b5c0d7568ee98419861618a95ee9d91a41cb9954462", size = 859162, upload-time = "2025-10-21T15:56:59.195Z" }, + { url = "https://files.pythonhosted.org/packages/41/87/374f3b2021b22aa6a4fc0b750d63f9721e53d1631a238f7a1c343c1cd288/regex-2025.10.23-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:185d2b18c062820b3a40d8fefa223a83f10b20a674bf6e8c4a432e8dfd844627", size = 849899, upload-time = "2025-10-21T15:57:01.747Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/7f7bb17c5a5a9747249807210e348450dab9212a46ae6d23ebce86ba6a2b/regex-2025.10.23-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:281d87fa790049c2b7c1b4253121edd80b392b19b5a3d28dc2a77579cb2a58ec", size = 789372, upload-time = "2025-10-21T15:57:04.018Z" }, + { url = "https://files.pythonhosted.org/packages/c9/dd/9c7728ff544fea09bbc8635e4c9e7c423b11c24f1a7a14e6ac4831466709/regex-2025.10.23-cp314-cp314-win32.whl", hash = "sha256:63b81eef3656072e4ca87c58084c7a9c2b81d41a300b157be635a8a675aacfb8", size = 271451, upload-time = "2025-10-21T15:57:06.266Z" }, + { url = "https://files.pythonhosted.org/packages/48/f8/ef7837ff858eb74079c4804c10b0403c0b740762e6eedba41062225f7117/regex-2025.10.23-cp314-cp314-win_amd64.whl", hash = "sha256:0967c5b86f274800a34a4ed862dfab56928144d03cb18821c5153f8777947796", size = 280173, upload-time = "2025-10-21T15:57:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d0/d576e1dbd9885bfcd83d0e90762beea48d9373a6f7ed39170f44ed22e336/regex-2025.10.23-cp314-cp314-win_arm64.whl", hash = "sha256:c70dfe58b0a00b36aa04cdb0f798bf3e0adc31747641f69e191109fd8572c9a9", size = 273206, upload-time = "2025-10-21T15:57:10.367Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d0/2025268315e8b2b7b660039824cb7765a41623e97d4cd421510925400487/regex-2025.10.23-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1f5799ea1787aa6de6c150377d11afad39a38afd033f0c5247aecb997978c422", size = 491854, upload-time = "2025-10-21T15:57:12.526Z" }, + { url = "https://files.pythonhosted.org/packages/44/35/5681c2fec5e8b33454390af209c4353dfc44606bf06d714b0b8bd0454ffe/regex-2025.10.23-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a9639ab7540cfea45ef57d16dcbea2e22de351998d614c3ad2f9778fa3bdd788", size = 292542, upload-time = "2025-10-21T15:57:15.158Z" }, + { url = "https://files.pythonhosted.org/packages/5d/17/184eed05543b724132e4a18149e900f5189001fcfe2d64edaae4fbaf36b4/regex-2025.10.23-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:08f52122c352eb44c3421dab78b9b73a8a77a282cc8314ae576fcaa92b780d10", size = 290903, upload-time = "2025-10-21T15:57:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/25/d0/5e3347aa0db0de382dddfa133a7b0ae72f24b4344f3989398980b44a3924/regex-2025.10.23-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ebf1baebef1c4088ad5a5623decec6b52950f0e4d7a0ae4d48f0a99f8c9cb7d7", size = 807546, upload-time = "2025-10-21T15:57:19.179Z" }, + { url = "https://files.pythonhosted.org/packages/d2/bb/40c589bbdce1be0c55e9f8159789d58d47a22014f2f820cf2b517a5cd193/regex-2025.10.23-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:16b0f1c2e2d566c562d5c384c2b492646be0a19798532fdc1fdedacc66e3223f", size = 873322, upload-time = "2025-10-21T15:57:21.36Z" }, + { url = "https://files.pythonhosted.org/packages/fe/56/a7e40c01575ac93360e606278d359f91829781a9f7fb6e5aa435039edbda/regex-2025.10.23-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7ada5d9dceafaab92646aa00c10a9efd9b09942dd9b0d7c5a4b73db92cc7e61", size = 914855, upload-time = "2025-10-21T15:57:24.044Z" }, + { url = "https://files.pythonhosted.org/packages/5c/4b/d55587b192763db3163c3f508b3b67b31bb6f5e7a0e08b83013d0a59500a/regex-2025.10.23-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a36b4005770044bf08edecc798f0e41a75795b9e7c9c12fe29da8d792ef870c", size = 812724, upload-time = "2025-10-21T15:57:26.123Z" }, + { url = "https://files.pythonhosted.org/packages/33/20/18bac334955fbe99d17229f4f8e98d05e4a501ac03a442be8facbb37c304/regex-2025.10.23-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:af7b2661dcc032da1fae82069b5ebf2ac1dfcd5359ef8b35e1367bfc92181432", size = 795439, upload-time = "2025-10-21T15:57:28.497Z" }, + { url = "https://files.pythonhosted.org/packages/67/46/c57266be9df8549c7d85deb4cb82280cb0019e46fff677534c5fa1badfa4/regex-2025.10.23-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:1cb976810ac1416a67562c2e5ba0accf6f928932320fef302e08100ed681b38e", size = 868336, upload-time = "2025-10-21T15:57:30.867Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f3/bd5879e41ef8187fec5e678e94b526a93f99e7bbe0437b0f2b47f9101694/regex-2025.10.23-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:1a56a54be3897d62f54290190fbcd754bff6932934529fbf5b29933da28fcd43", size = 854567, upload-time = "2025-10-21T15:57:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/e6/57/2b6bbdbd2f24dfed5b028033aa17ad8f7d86bb28f1a892cac8b3bc89d059/regex-2025.10.23-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8f3e6d202fb52c2153f532043bbcf618fd177df47b0b306741eb9b60ba96edc3", size = 799565, upload-time = "2025-10-21T15:57:35.153Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ba/a6168f542ba73b151ed81237adf6b869c7b2f7f8d51618111296674e20ee/regex-2025.10.23-cp314-cp314t-win32.whl", hash = "sha256:1fa1186966b2621b1769fd467c7b22e317e6ba2d2cdcecc42ea3089ef04a8521", size = 274428, upload-time = "2025-10-21T15:57:37.996Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a0/c84475e14a2829e9b0864ebf77c3f7da909df9d8acfe2bb540ff0072047c/regex-2025.10.23-cp314-cp314t-win_amd64.whl", hash = "sha256:08a15d40ce28362eac3e78e83d75475147869c1ff86bc93285f43b4f4431a741", size = 284140, upload-time = "2025-10-21T15:57:40.027Z" }, + { url = "https://files.pythonhosted.org/packages/51/33/6a08ade0eee5b8ba79386869fa6f77afeb835b60510f3525db987e2fffc4/regex-2025.10.23-cp314-cp314t-win_arm64.whl", hash = "sha256:a93e97338e1c8ea2649e130dcfbe8cd69bba5e1e163834752ab64dcb4de6d5ed", size = 274497, upload-time = "2025-10-21T15:57:42.389Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "ruff" +version = "0.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/62/50b7727004dfe361104dfbf898c45a9a2fdfad8c72c04ae62900224d6ecf/ruff-0.14.3.tar.gz", hash = "sha256:4ff876d2ab2b161b6de0aa1f5bd714e8e9b4033dc122ee006925fbacc4f62153", size = 5558687, upload-time = "2025-10-31T00:26:26.878Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/8e/0c10ff1ea5d4360ab8bfca4cb2c9d979101a391f3e79d2616c9bf348cd26/ruff-0.14.3-py3-none-linux_armv6l.whl", hash = "sha256:876b21e6c824f519446715c1342b8e60f97f93264012de9d8d10314f8a79c371", size = 12535613, upload-time = "2025-10-31T00:25:44.302Z" }, + { url = "https://files.pythonhosted.org/packages/d3/c8/6724f4634c1daf52409fbf13fefda64aa9c8f81e44727a378b7b73dc590b/ruff-0.14.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6fd8c79b457bedd2abf2702b9b472147cd860ed7855c73a5247fa55c9117654", size = 12855812, upload-time = "2025-10-31T00:25:47.793Z" }, + { url = "https://files.pythonhosted.org/packages/de/03/db1bce591d55fd5f8a08bb02517fa0b5097b2ccabd4ea1ee29aa72b67d96/ruff-0.14.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:71ff6edca490c308f083156938c0c1a66907151263c4abdcb588602c6e696a14", size = 11944026, upload-time = "2025-10-31T00:25:49.657Z" }, + { url = "https://files.pythonhosted.org/packages/0b/75/4f8dbd48e03272715d12c87dc4fcaaf21b913f0affa5f12a4e9c6f8a0582/ruff-0.14.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:786ee3ce6139772ff9272aaf43296d975c0217ee1b97538a98171bf0d21f87ed", size = 12356818, upload-time = "2025-10-31T00:25:51.949Z" }, + { url = "https://files.pythonhosted.org/packages/ec/9b/506ec5b140c11d44a9a4f284ea7c14ebf6f8b01e6e8917734a3325bff787/ruff-0.14.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cd6291d0061811c52b8e392f946889916757610d45d004e41140d81fb6cd5ddc", size = 12336745, upload-time = "2025-10-31T00:25:54.248Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e1/c560d254048c147f35e7f8131d30bc1f63a008ac61595cf3078a3e93533d/ruff-0.14.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a497ec0c3d2c88561b6d90f9c29f5ae68221ac00d471f306fa21fa4264ce5fcd", size = 13101684, upload-time = "2025-10-31T00:25:56.253Z" }, + { url = "https://files.pythonhosted.org/packages/a5/32/e310133f8af5cd11f8cc30f52522a3ebccc5ea5bff4b492f94faceaca7a8/ruff-0.14.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e231e1be58fc568950a04fbe6887c8e4b85310e7889727e2b81db205c45059eb", size = 14535000, upload-time = "2025-10-31T00:25:58.397Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a1/7b0470a22158c6d8501eabc5e9b6043c99bede40fa1994cadf6b5c2a61c7/ruff-0.14.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:469e35872a09c0e45fecf48dd960bfbce056b5db2d5e6b50eca329b4f853ae20", size = 14156450, upload-time = "2025-10-31T00:26:00.889Z" }, + { url = "https://files.pythonhosted.org/packages/0a/96/24bfd9d1a7f532b560dcee1a87096332e461354d3882124219bcaff65c09/ruff-0.14.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d6bc90307c469cb9d28b7cfad90aaa600b10d67c6e22026869f585e1e8a2db0", size = 13568414, upload-time = "2025-10-31T00:26:03.291Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e7/138b883f0dfe4ad5b76b58bf4ae675f4d2176ac2b24bdd81b4d966b28c61/ruff-0.14.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2f8a0bbcffcfd895df39c9a4ecd59bb80dca03dc43f7fb63e647ed176b741e", size = 13315293, upload-time = "2025-10-31T00:26:05.708Z" }, + { url = "https://files.pythonhosted.org/packages/33/f4/c09bb898be97b2eb18476b7c950df8815ef14cf956074177e9fbd40b7719/ruff-0.14.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:678fdd7c7d2d94851597c23ee6336d25f9930b460b55f8598e011b57c74fd8c5", size = 13539444, upload-time = "2025-10-31T00:26:08.09Z" }, + { url = "https://files.pythonhosted.org/packages/9c/aa/b30a1db25fc6128b1dd6ff0741fa4abf969ded161599d07ca7edd0739cc0/ruff-0.14.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1ec1ac071e7e37e0221d2f2dbaf90897a988c531a8592a6a5959f0603a1ecf5e", size = 12252581, upload-time = "2025-10-31T00:26:10.297Z" }, + { url = "https://files.pythonhosted.org/packages/da/13/21096308f384d796ffe3f2960b17054110a9c3828d223ca540c2b7cc670b/ruff-0.14.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afcdc4b5335ef440d19e7df9e8ae2ad9f749352190e96d481dc501b753f0733e", size = 12307503, upload-time = "2025-10-31T00:26:12.646Z" }, + { url = "https://files.pythonhosted.org/packages/cb/cc/a350bac23f03b7dbcde3c81b154706e80c6f16b06ff1ce28ed07dc7b07b0/ruff-0.14.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7bfc42f81862749a7136267a343990f865e71fe2f99cf8d2958f684d23ce3dfa", size = 12675457, upload-time = "2025-10-31T00:26:15.044Z" }, + { url = "https://files.pythonhosted.org/packages/cb/76/46346029fa2f2078826bc88ef7167e8c198e58fe3126636e52f77488cbba/ruff-0.14.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a65e448cfd7e9c59fae8cf37f9221585d3354febaad9a07f29158af1528e165f", size = 13403980, upload-time = "2025-10-31T00:26:17.81Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a4/35f1ef68c4e7b236d4a5204e3669efdeefaef21f0ff6a456792b3d8be438/ruff-0.14.3-py3-none-win32.whl", hash = "sha256:f3d91857d023ba93e14ed2d462ab62c3428f9bbf2b4fbac50a03ca66d31991f7", size = 12500045, upload-time = "2025-10-31T00:26:20.503Z" }, + { url = "https://files.pythonhosted.org/packages/03/15/51960ae340823c9859fb60c63301d977308735403e2134e17d1d2858c7fb/ruff-0.14.3-py3-none-win_amd64.whl", hash = "sha256:d7b7006ac0756306db212fd37116cce2bd307e1e109375e1c6c106002df0ae5f", size = 13594005, upload-time = "2025-10-31T00:26:22.533Z" }, + { url = "https://files.pythonhosted.org/packages/b7/73/4de6579bac8e979fca0a77e54dec1f1e011a0d268165eb8a9bc0982a6564/ruff-0.14.3-py3-none-win_arm64.whl", hash = "sha256:26eb477ede6d399d898791d01961e16b86f02bc2486d0d1a7a9bb2379d055dc1", size = 12590017, upload-time = "2025-10-31T00:26:24.52Z" }, +] + +[[package]] +name = "safetensors" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/cc/738f3011628920e027a11754d9cae9abec1aed00f7ae860abbf843755233/safetensors-0.6.2.tar.gz", hash = "sha256:43ff2aa0e6fa2dc3ea5524ac7ad93a9839256b8703761e76e2d0b2a3fa4f15d9", size = 197968, upload-time = "2025-08-08T13:13:58.654Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/b1/3f5fd73c039fc87dba3ff8b5d528bfc5a32b597fea8e7a6a4800343a17c7/safetensors-0.6.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9c85ede8ec58f120bad982ec47746981e210492a6db876882aa021446af8ffba", size = 454797, upload-time = "2025-08-08T13:13:52.066Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c9/bb114c158540ee17907ec470d01980957fdaf87b4aa07914c24eba87b9c6/safetensors-0.6.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d6675cf4b39c98dbd7d940598028f3742e0375a6b4d4277e76beb0c35f4b843b", size = 432206, upload-time = "2025-08-08T13:13:50.931Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8e/f70c34e47df3110e8e0bb268d90db8d4be8958a54ab0336c9be4fe86dac8/safetensors-0.6.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d2d2b3ce1e2509c68932ca03ab8f20570920cd9754b05063d4368ee52833ecd", size = 473261, upload-time = "2025-08-08T13:13:41.259Z" }, + { url = "https://files.pythonhosted.org/packages/2a/f5/be9c6a7c7ef773e1996dc214e73485286df1836dbd063e8085ee1976f9cb/safetensors-0.6.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:93de35a18f46b0f5a6a1f9e26d91b442094f2df02e9fd7acf224cfec4238821a", size = 485117, upload-time = "2025-08-08T13:13:43.506Z" }, + { url = "https://files.pythonhosted.org/packages/c9/55/23f2d0a2c96ed8665bf17a30ab4ce5270413f4d74b6d87dd663258b9af31/safetensors-0.6.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89a89b505f335640f9120fac65ddeb83e40f1fd081cb8ed88b505bdccec8d0a1", size = 616154, upload-time = "2025-08-08T13:13:45.096Z" }, + { url = "https://files.pythonhosted.org/packages/98/c6/affb0bd9ce02aa46e7acddbe087912a04d953d7a4d74b708c91b5806ef3f/safetensors-0.6.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc4d0d0b937e04bdf2ae6f70cd3ad51328635fe0e6214aa1fc811f3b576b3bda", size = 520713, upload-time = "2025-08-08T13:13:46.25Z" }, + { url = "https://files.pythonhosted.org/packages/fe/5d/5a514d7b88e310c8b146e2404e0dc161282e78634d9358975fd56dfd14be/safetensors-0.6.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8045db2c872db8f4cbe3faa0495932d89c38c899c603f21e9b6486951a5ecb8f", size = 485835, upload-time = "2025-08-08T13:13:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/7a/7b/4fc3b2ba62c352b2071bea9cfbad330fadda70579f617506ae1a2f129cab/safetensors-0.6.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:81e67e8bab9878bb568cffbc5f5e655adb38d2418351dc0859ccac158f753e19", size = 521503, upload-time = "2025-08-08T13:13:47.651Z" }, + { url = "https://files.pythonhosted.org/packages/5a/50/0057e11fe1f3cead9254315a6c106a16dd4b1a19cd247f7cc6414f6b7866/safetensors-0.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0e4d029ab0a0e0e4fdf142b194514695b1d7d3735503ba700cf36d0fc7136ce", size = 652256, upload-time = "2025-08-08T13:13:53.167Z" }, + { url = "https://files.pythonhosted.org/packages/e9/29/473f789e4ac242593ac1656fbece6e1ecd860bb289e635e963667807afe3/safetensors-0.6.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:fa48268185c52bfe8771e46325a1e21d317207bcabcb72e65c6e28e9ffeb29c7", size = 747281, upload-time = "2025-08-08T13:13:54.656Z" }, + { url = "https://files.pythonhosted.org/packages/68/52/f7324aad7f2df99e05525c84d352dc217e0fa637a4f603e9f2eedfbe2c67/safetensors-0.6.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:d83c20c12c2d2f465997c51b7ecb00e407e5f94d7dec3ea0cc11d86f60d3fde5", size = 692286, upload-time = "2025-08-08T13:13:55.884Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/cad1d9762868c7c5dc70c8620074df28ebb1a8e4c17d4c0cb031889c457e/safetensors-0.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d944cea65fad0ead848b6ec2c37cc0b197194bec228f8020054742190e9312ac", size = 655957, upload-time = "2025-08-08T13:13:57.029Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/e2158e17bbe57d104f0abbd95dff60dda916cf277c9f9663b4bf9bad8b6e/safetensors-0.6.2-cp38-abi3-win32.whl", hash = "sha256:cab75ca7c064d3911411461151cb69380c9225798a20e712b102edda2542ddb1", size = 308926, upload-time = "2025-08-08T13:14:01.095Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c3/c0be1135726618dc1e28d181b8c442403d8dbb9e273fd791de2d4384bcdd/safetensors-0.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:c7b214870df923cbc1593c3faee16bec59ea462758699bd3fee399d00aac072c", size = 320192, upload-time = "2025-08-08T13:13:59.467Z" }, +] + +[[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/4a/be/32ce495aa1d0e0c323dcb1ba87096037358edee539cac5baf8755a6bd396/sentencepiece-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57cae326c8727de58c85977b175af132a7138d84c764635d7e71bbee7e774133", size = 1943152, upload-time = "2025-08-12T06:59:40.048Z" }, + { url = "https://files.pythonhosted.org/packages/88/7e/ff23008899a58678e98c6ff592bf4d368eee5a71af96d0df6b38a039dd4f/sentencepiece-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:56dd39a3c4d6493db3cdca7e8cc68c6b633f0d4195495cbadfcf5af8a22d05a6", size = 1325651, upload-time = "2025-08-12T06:59:41.536Z" }, + { url = "https://files.pythonhosted.org/packages/19/84/42eb3ce4796777a1b5d3699dfd4dca85113e68b637f194a6c8d786f16a04/sentencepiece-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9381351182ff9888cc80e41c632e7e274b106f450de33d67a9e8f6043da6f76", size = 1253645, upload-time = "2025-08-12T06:59:42.903Z" }, + { url = "https://files.pythonhosted.org/packages/89/fa/d3d5ebcba3cb9e6d3775a096251860c41a6bc53a1b9461151df83fe93255/sentencepiece-0.2.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99f955df238021bf11f0fc37cdb54fd5e5b5f7fd30ecc3d93fb48b6815437167", size = 1316273, upload-time = "2025-08-12T06:59:44.476Z" }, + { url = "https://files.pythonhosted.org/packages/04/88/14f2f4a2b922d8b39be45bf63d79e6cd3a9b2f248b2fcb98a69b12af12f5/sentencepiece-0.2.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cdfecef430d985f1c2bcbfff3defd1d95dae876fbd0173376012d2d7d24044b", size = 1387881, upload-time = "2025-08-12T06:59:46.09Z" }, + { url = "https://files.pythonhosted.org/packages/fd/b8/903e5ccb77b4ef140605d5d71b4f9e0ad95d456d6184688073ed11712809/sentencepiece-0.2.1-cp312-cp312-win32.whl", hash = "sha256:a483fd29a34c3e34c39ac5556b0a90942bec253d260235729e50976f5dba1068", size = 999540, upload-time = "2025-08-12T06:59:48.023Z" }, + { url = "https://files.pythonhosted.org/packages/2d/81/92df5673c067148c2545b1bfe49adfd775bcc3a169a047f5a0e6575ddaca/sentencepiece-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4cdc7c36234fda305e85c32949c5211faaf8dd886096c7cea289ddc12a2d02de", size = 1054671, upload-time = "2025-08-12T06:59:49.895Z" }, + { url = "https://files.pythonhosted.org/packages/fe/02/c5e3bc518655d714622bec87d83db9cdba1cd0619a4a04e2109751c4f47f/sentencepiece-0.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:daeb5e9e9fcad012324807856113708614d534f596d5008638eb9b40112cd9e4", size = 1033923, upload-time = "2025-08-12T06:59:51.952Z" }, + { 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 = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +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 = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tenacity" +version = "9.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, +] + +[[package]] +name = "termcolor" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/56/ab275c2b56a5e2342568838f0d5e3e66a32354adcc159b495e374cda43f5/termcolor-3.2.0.tar.gz", hash = "sha256:610e6456feec42c4bcd28934a8c87a06c3fa28b01561d46aa09a9881b8622c58", size = 14423, upload-time = "2025-10-25T19:11:42.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/d5/141f53d7c1eb2a80e6d3e9a390228c3222c27705cbe7f048d3623053f3ca/termcolor-3.2.0-py3-none-any.whl", hash = "sha256:a10343879eba4da819353c55cb8049b0933890c2ebf9ad5d3ecd2bb32ea96ea6", size = 7698, upload-time = "2025-10-25T19:11:41.536Z" }, +] + +[[package]] +name = "texttable" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/dc/0aff23d6036a4d3bf4f1d8c8204c5c79c4437e25e0ae94ffe4bbb55ee3c2/texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638", size = 12831, upload-time = "2023-10-03T09:48:12.272Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917", size = 10768, upload-time = "2023-10-03T09:48:10.434Z" }, +] + +[[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 = "toposort" +version = "1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/d8/9bc1598ddf74410beba243ffeaee8d0b3ca7e9ac5cefe77367da497433e1/toposort-1.5.tar.gz", hash = "sha256:dba5ae845296e3bf37b042c640870ffebcdeb8cd4df45adaa01d8c5476c557dd", size = 10294, upload-time = "2016-10-25T01:05:40.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/8a/321cd8ea5f4a22a06e3ba30ef31ec33bea11a3443eeb1d89807640ee6ed4/toposort-1.5-py2.py3-none-any.whl", hash = "sha256:d80128b83b411d503b0cdb4a8f172998bc1d3b434b6402a349b8ebd734d51a80", size = 7602, upload-time = "2016-10-25T01:05:38.621Z" }, +] + +[[package]] +name = "torch" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d3/3985739f3b8e88675127bf70f82b3a48ae083e39cda56305dbd90398fec0/torch-2.9.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e5f7af1dc4c0a7c4a260c2534f41ddaf209714f7c89145e644c44712fbd6b642", size = 104107898, upload-time = "2025-10-15T15:46:20.883Z" }, + { url = "https://files.pythonhosted.org/packages/a5/4b/f4bb2e6c25d0272f798cd6d7a04ed315da76cec68c602d87040c7847287f/torch-2.9.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:01cff95ecd9a212ea2f141db28acccdceb6a4c54f64e6c51091146f5e2a772c6", size = 899738273, upload-time = "2025-10-15T15:50:04.188Z" }, + { url = "https://files.pythonhosted.org/packages/66/11/c1c5ba6691cda6279087c35bd626536e4fd29521fe740abf5008377a9a02/torch-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4582b162f541651f0cb184d3e291c05c2f556c7117c64a9873e2ee158d40062b", size = 109280887, upload-time = "2025-10-15T15:46:26.228Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5f/b85bd8c05312d71de9402bf5868d217c38827cfd09d8f8514e5be128a52b/torch-2.9.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:33f58e9a102a91259af289d50525c30323b5c9ae1d31322b6447c0814da68695", size = 74478983, upload-time = "2025-10-15T15:46:39.406Z" }, + { url = "https://files.pythonhosted.org/packages/c2/1c/90eb13833cdf4969ea9707586d7b57095c3b6e2b223a7256bf111689bcb8/torch-2.9.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c30a17fc83eeab346913e237c64b15b5ba6407fff812f6c541e322e19bc9ea0e", size = 104111330, upload-time = "2025-10-15T15:46:35.238Z" }, + { url = "https://files.pythonhosted.org/packages/0e/21/2254c54b8d523592c25ef4434769aa23e29b1e6bf5f4c0ad9e27bf442927/torch-2.9.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8f25033b8667b57857dfd01458fbf2a9e6a6df1f8def23aef0dc46292f6aa642", size = 899750243, upload-time = "2025-10-15T15:48:57.459Z" }, + { url = "https://files.pythonhosted.org/packages/b7/a5/5cb94fa4fd1e78223455c23c200f30f6dc10c6d4a2bcc8f6e7f2a2588370/torch-2.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:d037f1b4ffd25013be4a7bf3651a0a910c68554956c7b2c92ebe87c76475dece", size = 109284513, upload-time = "2025-10-15T15:46:45.061Z" }, + { url = "https://files.pythonhosted.org/packages/66/e8/fc414d8656250ee46120b44836ffbb3266343db424b3e18ca79ebbf69d4f/torch-2.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e4e5b5cba837a2a8d1a497ba9a58dae46fa392593eaa13b871c42f71847503a5", size = 74830362, upload-time = "2025-10-15T15:46:48.983Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5f/9474c98fc5ae0cd04b9466035428cd360e6611a86b8352a0fc2fa504acdc/torch-2.9.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:64693568f5dc4dbd5f880a478b1cea0201cc6b510d91d1bc54fea86ac5d1a637", size = 104144940, upload-time = "2025-10-15T15:47:29.076Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5a/8e0c1cf57830172c109d4bd6be2708cabeaf550983eee7029291322447a0/torch-2.9.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:f8ed31ddd7d10bfb3fbe0b9fe01b1243577f13d75e6f4a0839a283915ce3791e", size = 899744054, upload-time = "2025-10-15T15:48:29.864Z" }, + { url = "https://files.pythonhosted.org/packages/6d/28/82c28b30fcb4b7c9cdd995763d18bbb830d6521356712faebbad92ffa61d/torch-2.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:eff527d4e4846e6f70d2afd8058b73825761203d66576a7e04ea2ecfebcb4ab8", size = 109517546, upload-time = "2025-10-15T15:47:33.395Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a91f96ec74347fa5fd24453fa514bc61c61ecc79196fa760b012a1873d96/torch-2.9.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:f8877779cf56d1ce431a7636703bdb13307f5960bb1af49716d8b179225e0e6a", size = 74480732, upload-time = "2025-10-15T15:47:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/9f70af34b334a7e0ef496ceec96b7ec767bd778ea35385ce6f77557534d1/torch-2.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7e614fae699838038d888729f82b687c03413c5989ce2a9481f9a7e7a396e0bb", size = 74433037, upload-time = "2025-10-15T15:47:41.894Z" }, + { url = "https://files.pythonhosted.org/packages/b7/84/37cf88625901934c97109e583ecc21777d21c6f54cda97a7e5bbad1ee2f2/torch-2.9.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:dfb5b8cd310ba3436c7e14e8b7833ef658cf3045e50d2bdaed23c8fc517065eb", size = 104116482, upload-time = "2025-10-15T15:47:46.266Z" }, + { url = "https://files.pythonhosted.org/packages/56/8e/ca8b17866943a8d4f4664d402ea84210aa274588b4c5d89918f5caa24eec/torch-2.9.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b3d29524993a478e46f5d598b249cd824b7ed98d7fba538bd9c4cde6c803948f", size = 899746916, upload-time = "2025-10-15T15:50:40.294Z" }, + { url = "https://files.pythonhosted.org/packages/43/65/3b17c0fbbdab6501c5b320a52a648628d0d44e7379f64e27d9eef701b6bf/torch-2.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:71c7578984f5ec0eb645eb4816ac8435fcf3e3e2ae1901bcd2f519a9cafb5125", size = 109275151, upload-time = "2025-10-15T15:49:20.715Z" }, + { url = "https://files.pythonhosted.org/packages/83/36/74f8c051f785500396e42f93542422422dfd874a174f21f8d955d36e5d64/torch-2.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:71d9309aee457bbe0b164bce2111cd911c4ed4e847e65d5077dbbcd3aba6befc", size = 74823353, upload-time = "2025-10-15T15:49:16.59Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/dc3b4e2f9ba98ae27238f0153ca098bf9340b2dafcc67fde645d496dfc2a/torch-2.9.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c08fb654d783899e204a32cca758a7ce8a45b2d78eeb89517cc937088316f78e", size = 104140340, upload-time = "2025-10-15T15:50:19.67Z" }, + { url = "https://files.pythonhosted.org/packages/c0/8d/b00657f8141ac16af7bb6cda2e67de18499a3263b78d516b9a93fcbc98e3/torch-2.9.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ec8feb0099b2daa5728fbc7abb0b05730fd97e0f359ff8bda09865aaa7bd7d4b", size = 899731750, upload-time = "2025-10-15T15:49:36.673Z" }, + { url = "https://files.pythonhosted.org/packages/fc/29/bd361e0cbb2c79ce6450f42643aaf6919956f89923a50571b0ebfe92d142/torch-2.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:695ba920f234ad4170c9c50e28d56c848432f8f530e6bc7f88fcb15ddf338e75", size = 109503850, upload-time = "2025-10-15T15:50:24.118Z" }, +] + +[[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.1" +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/d6/68/a39307bcc4116a30b2106f2e689130a48de8bd8a1e635b5e1030e46fcd9e/transformers-4.57.1.tar.gz", hash = "sha256:f06c837959196c75039809636cd964b959f6604b75b8eeec6fdfc0440b89cc55", size = 10142511, upload-time = "2025-10-14T15:39:26.18Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/d3/c16c3b3cf7655a67db1144da94b021c200ac1303f82428f2beef6c2e72bb/transformers-4.57.1-py3-none-any.whl", hash = "sha256:b10d05da8fa67dc41644dbbf9bc45a44cb86ae33da6f9295f5fbf5b7890bd267", size = 11990925, upload-time = "2025-10-14T15:39:23.085Z" }, +] + +[[package]] +name = "triton" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/3a/e991574f3102147b642e49637e0281e9bb7c4ba254edb2bab78247c85e01/triton-3.5.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9e71db82261c4ffa3921cd050cd5faa18322d2d405c30eb56084afaff3b0833", size = 170476535, upload-time = "2025-10-13T16:38:05.18Z" }, + { url = "https://files.pythonhosted.org/packages/6c/29/10728de8a6e932e517c10773486b8e99f85d1b1d9dd87d9a9616e1fef4a1/triton-3.5.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e6bb9aa5519c084a333acdba443789e50012a4b851cd486c54f0b8dc2a8d3a12", size = 170487289, upload-time = "2025-10-13T16:38:11.662Z" }, + { url = "https://files.pythonhosted.org/packages/5c/38/db80e48b9220c9bce872b0f616ad0446cdf554a40b85c7865cbca99ab3c2/triton-3.5.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c83f2343e1a220a716c7b3ab9fccfcbe3ad4020d189549200e2d2e8d5868bed9", size = 170577179, upload-time = "2025-10-13T16:38:17.865Z" }, + { url = "https://files.pythonhosted.org/packages/ff/60/1810655d1d856c9a4fcc90ee8966d85f552d98c53a6589f95ab2cbe27bb8/triton-3.5.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da0fa67ccd76c3dcfb0bffe1b1c57c685136a6bd33d141c24d9655d4185b1289", size = 170487949, upload-time = "2025-10-13T16:38:24.881Z" }, + { url = "https://files.pythonhosted.org/packages/fb/b7/1dec8433ac604c061173d0589d99217fe7bf90a70bdc375e745d044b8aad/triton-3.5.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:317fe477ea8fd4524a6a8c499fb0a36984a56d0b75bf9c9cb6133a1c56d5a6e7", size = 170580176, upload-time = "2025-10-13T16:38:31.14Z" }, +] + +[[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 = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "volcengine-python-sdk" +version = "4.0.31" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "python-dateutil" }, + { name = "six" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/c1/0796bd741370624c8509a4c50b8b872ff0a148e6c65011aff82e5f818e61/volcengine_python_sdk-4.0.31.tar.gz", hash = "sha256:1581c75ba353d9c32b59d03c9c6d10dcb1b91247a4191ebc21cd674204e75ca2", size = 6737192, upload-time = "2025-10-30T14:31:57.574Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/42/8a4f96d62ce58b2066d890343002f466f86542f1667cf01f6ff809f74841/volcengine_python_sdk-4.0.31-py2.py3-none-any.whl", hash = "sha256:bd5bd79ccb73d8db30bfad48394808d0b0c08017aec425f119c6e05f859e2f49", size = 26413143, upload-time = "2025-10-30T14:31:53.104Z" }, +] + +[[package]] +name = "websocket-client" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +] + +[[package]] +name = "zhipuai" +version = "2.1.5.20250825" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "pydantic-core" }, + { name = "pyjwt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/9b/972de785a9859bbb5ee6425e011f154001886207860acdedebd174b7f2c2/zhipuai-2.1.5.20250825.tar.gz", hash = "sha256:50fc3982565ee631bd640b1166a1d223de227958026a74a1dd0e26dbd58d729c", size = 69899, upload-time = "2025-08-25T10:58:52.796Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/95/22fc274f670e4531da20cc607991c34e7f8f4de5baa589e454e77e2492fa/zhipuai-2.1.5.20250825-py3-none-any.whl", hash = "sha256:aaad19881e514a4682598f07503b82d60b8b9a91cd83f9bcda988b94853c830d", size = 119097, upload-time = "2025-08-25T10:58:51.518Z" }, +] diff --git a/dofile/kg_project/.gitignore b/dofile/kg_project/.gitignore new file mode 100644 index 0000000..08b15e9 --- /dev/null +++ b/dofile/kg_project/.gitignore @@ -0,0 +1,10 @@ +.venv/ +__pycache__/ +*.pyc +*.pyo +config/api_keys.yaml +output/ +logs/ +*.log +.DS_Store +Thumbs.db diff --git a/dofile/kg_project/config/deep_extraction_config.yaml b/dofile/kg_project/config/deep_extraction_config.yaml new file mode 100644 index 0000000..c0a2614 --- /dev/null +++ b/dofile/kg_project/config/deep_extraction_config.yaml @@ -0,0 +1,122 @@ +llm: + model: "deepseek-chat" + temperature: 0.0 + max_tokens: 8192 + max_retries: 3 + retry_delay: 2 + request_timeout: 120 + batch_timeout: 600 + +batch_processing: + batch_size: 10 + concurrent_requests: 5 + test_batch_size: 5 + incremental_save: true + +deduplication: + similarity_threshold: 0.85 + enable_fuzzy_matching: true + text_normalization: true + +validation: + enable_entity_validation: true + enable_relationship_validation: true + min_confidence_score: 0.7 + strict_mode: false + filter_generic_concepts: true + +output: + nodes_file: "output/nodes_llm_v2.csv" + relationships_file: "output/rels_llm_v2.csv" + report_file: "output/extraction_report_v2.md" + encoding: "utf-8-sig" + +progress: + progress_file: "output/progress_v2.json" + checkpoint_interval: 10 + +logging: + log_file: "logs/deep_extraction_v2.log" + level: "INFO" + format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + +# ============================================================ +# 第二轮本体:城市更新法规政策工具箱 (21实体 + 34关系) +# ============================================================ + +entity_type_prompts: + Agency: "主体机构:发布主体、实施主体、监管主体、责任主体,包括政府部门(全国人大常委会、国务院、自然资源部、住建部、生态环境部、发改委、地方政府)、实施机构、平台公司、社区组织、第三方机构等。属性:level(国家/省级/市级/县级/乡镇街道/社区)、agency_type(立法机关/行政机关/主管部门/实施主体/监管主体/责任主体/市场主体/社区组织/第三方机构)" + LegalObject: "法规事项:法规政策所规制的事项,回答'管什么事项'。如规划编制、土地利用、建设管理、历史文化保护、生态保护、财政支持、金融支持、产权登记、公众参与、行政审批、监督管理、城市更新、公共服务、基础设施、安全韧性等。属性:category" + SpatialObject: "空间对象:城市更新中的具体空间对象,回答'作用于什么空间'。如老旧小区、城中村、低效用地、工业遗存、历史文化街区、历史建筑、公共空间、公共服务设施、市政基础设施、存量建筑、建设用地、城市更新单元、生态保护红线、永久基本农田、城镇开发边界等。属性:spatial_type" + RenewalScene: "更新场景:城市更新应用场景,回答'用于什么更新场景'。如老旧小区改造、城中村改造、低效用地再开发、工业遗存更新、历史文化保护更新、完整社区建设、公共空间提升、基础设施补短板、城市安全韧性提升、城市生态修复等。属性:scene_type、derivation_method(原文提取/语义归纳/人工标注/模型推理)" + PolicyTool: "具体政策工具:城市更新中用于实现治理目标的具体政策、制度、措施或治理手段。属性:tool_type(命令型/激励型/劝诫型/能力型/系统变革型)、mandatory_level(强制/引导/鼓励/支持/禁止/限制)、source_text" + ToolCategory: "政策工具类别:归类政策工具的分类节点。属性:classification_system(一般政策工具分类/城市更新工具分类)、urban_renewal_category(规划引导/土地配置/开发激励/财政金融/权益协调/实施管理/监督反馈)" + Procedure: "程序:审批、备案、评估、论证、公示、听证、入库、验收、监督、征收、补偿等行政或治理程序。属性:procedure_type、time_limit" + Obligation: "权利义务:法规规定的权利、义务、禁止或责任。属性:obligation_type(权利/义务/禁止/职责/鼓励/支持)、modality(应当/可以/不得/禁止/鼓励/支持/负责/依法)" + Condition: "适用条件:政策工具或程序适用时的前置条件。如'经批准后''符合XX条件'。属性:condition_type(规划条件/审批条件/权属条件/安全条件/环保条件/公众参与条件/程序条件/资金条件)" + Constraint: "约束条件:对行为设置的边界性、底线性或限制性要求。如'不得突破''严守''控制线'。属性:constraint_type(用途管制/空间边界/强制标准/历史保护/生态保护/权益保护/安全底线/公共利益)" + Penalty: "法律责任:违反法规后的处罚。属性:penalty_type(行政处罚/刑事处罚/民事责任/行政处分/信用惩戒/整改责任)、amount_or_measure" + TimePoint: "时间节点:生效、修订、废止、截止、过渡期等时间信息。属性:time_type(发布日期/生效日期/修订日期/废止日期/截止日期/过渡期)" + Region: "适用区域:全国、省、市、县、特定区域等。属性:level(全国/区域/省/市/县/乡镇街道/社区/特定区域)" + +relationship_type_prompts: + # 文档层次 + has_chapter: "文档包含章" + has_section: "章包含节" + has_article: "文件/章/节包含条文" + has_clause: "条文包含款" + article_in_document: "条文或款属于某一法规文件" + # 法条依据 + cites: "引用其他法规:如'根据《城乡规划法》第X条'、'依据《土地管理法》规定'、'按照国务院有关规定'" + implements: "实施上位法:如'为实施《城乡规划法》制定本办法'、'根据《土地管理法》授权'" + amends: "修正/修订:如'对《XX法》作如下修改'、'将第X条修改为'" + replaces: "替代/废止:如'本法自施行之日起,XX法同时废止'" + supplements: "补充规定:如'在XX基础上补充规定'" + basis_for_planning: "法规或条文作为规划编制、审批、实施或政策工具设立的依据" + # 主体关系 + issued_by: "法规文件由某主体发布或制定" + implemented_by: "条文、政策工具、程序或义务由某主体负责实施" + assigned_to: "监管职责、管理职责、实施任务分配给某主体" + # 语义规制 + regulates: "条文对某法规事项进行规制(准入、禁止、限制、鼓励、保护等)" + defines_object: "条文对法规事项、空间对象或更新场景进行定义或界定" + applies_to: "条文、程序或政策工具适用于某法规事项、空间对象、更新场景或区域" + sets_obligation: "条文设定权利、义务、禁止、职责或支持性要求" + sets_condition: "条文、政策工具或程序设置适用条件(前置条件)" + sets_constraint: "条文、政策工具或程序设置边界性、底线性或限制性要求" + prescribes_penalty: "条文规定了违法行为的法律后果" + # 政策工具 + extracts_tool: "从条文中识别出具体政策工具" + belongs_to_category: "具体政策工具属于某类政策工具类别" + applies_to_scene: "政策工具适用于某类城市更新场景" + targets_object: "政策工具作用于某一法规事项或空间对象" + coordinates_with: "两个政策工具之间存在并列协同或组合使用关系" + supports_tool: "一个政策工具对另一个形成前置支撑或条件支撑" + # 程序 + requires_procedure: "条文、政策工具或法规事项要求履行某类程序" + procedure_for: "某一程序对应某类法规事项、空间对象、更新场景或政策工具" + precedes_procedure: "一个程序在时序上先于另一个程序" + # 辅助 + governs_region: "法规政策或条文适用于某一区域" + effective_timeline: "法规文件关联时间节点(发布/生效/修订/废止)" + standard_for: "标准、规范或条文适用于某一法规事项、空间对象、程序或政策工具" + article_effective_timeline: "条文关联时间节点(生效/截止/过渡期)" + +extraction_tips: + citation_patterns: "注意识别法条引用模式:'根据《XX法》'、'依照第X条规定'、'按照国务院XX规定'、'参照XX标准执行'" + article_classification: "法条类型判断:管理性(应当/必须)、禁止性(不得/禁止)、授权性(可以/有权)、程序性(申请/审批/备案)、处罚性(罚款/责令/没收)" + cross_reference: "特别注意跨文档引用:当条文引用其他法规时,创建cites关系并记录引用上下文" + entity_boundary_rules: | + 实体边界规则(严格遵守): + - LegalObject:法规事项,回答"管什么事项",如规划编制、土地利用、财政支持 + - SpatialObject:空间对象,回答"作用于什么空间",如老旧小区、城中村、低效用地 + - RenewalScene:更新场景,回答"用于什么更新场景",如老旧小区改造、城中村改造 + - Procedure:程序,强调流程步骤(审批/备案/评估/公示/验收/入库) + - PolicyTool:政策工具,强调治理手段、政策措施、制度安排或支持机制 + - Obligation:权利义务,强调主体应当/可以/不得做什么 + - Condition:适用条件,强调前置条件(经批准/符合/满足/具备) + - Constraint:约束条件,强调底线限制(不得突破/严守/控制线/强制性标准) + do_not_extract: | + - 不要新增本体之外的实体类型或关系类型 + - 文本中没有依据时不要强行抽取 + - conflicts_with和has_gap不纳入本阶段抽取 diff --git a/dofile/kg_project/config/legal_config.yaml b/dofile/kg_project/config/legal_config.yaml new file mode 100644 index 0000000..6b8bf62 --- /dev/null +++ b/dofile/kg_project/config/legal_config.yaml @@ -0,0 +1,86 @@ +project_name: "urban_planning_law_kg" +project_description: "城市规划法律法规知识图谱构建" + +data: + source_dir: "E:\\Project\\SI\\2026_KG_PlanningLaw\\data\\城市规划法律法规\\城市更新法规数据库" + total_documents: 296 + categories: + - {id: 0, name: "0知识图谱目录", type: "Index"} + - {id: 1, name: "1法律", type: "Law"} + - {id: 2, name: "2行政法规", type: "AdministrativeRegulation"} + - {id: 3, name: "3部门规章", type: "DepartmentalRule"} + - {id: 4, name: "4党中央国务院文件", type: "PolicyDocument"} + - {id: 5, name: "5国家主管部门文件", type: "PolicyDocument"} + - {id: 6, name: "6主要技术标准", type: "PolicyDocument"} + min_text_length: 50 + +api: + config_file: "config/api_keys.yaml" + model: "deepseek-chat" + max_tokens: 8192 + temperature: 0.0 + +processing: + concurrent_requests: 5 + max_retries: 3 + retry_delay: 2 + batch_size: 10 + +paths: + config_dir: "config" + data_dir: "data" + output_dir: "output" + logs_dir: "logs" + ontology_dir: "ontology" + scripts_dir: "scripts" + src_dir: "src" + +ontology: + ontology_file: "ontology/legal_ontology.json" + entity_ontology_file: "config/legal_ontology.yaml" + +neo4j: + uri: "bolt://localhost:7687" + username: "neo4j" + password: "password" + database: "neo4j" + +extraction: + entity_types: + - Law + - AdministrativeRegulation + - DepartmentalRule + - PolicyDocument + - Chapter + - Section + - Article + - GovernmentBody + - LegalSubject + - SpatialConcept + - AdministrativeProcedure + - Obligation + - Penalty + - TimePoint + - Region + relation_types: + - cites + - implements + - amends + - replaces + - supplements + - issued_by + - implemented_by + - regulates + - defines_spatial + - requires_procedure + - sets_obligation + - prescribes_penalty + - applies_to + - governs_region + confidence_threshold: + entity: 0.8 + relation: 0.7 + +logging: + level: "INFO" + format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" diff --git a/dofile/kg_project/config/legal_ontology.yaml b/dofile/kg_project/config/legal_ontology.yaml new file mode 100644 index 0000000..348ff47 --- /dev/null +++ b/dofile/kg_project/config/legal_ontology.yaml @@ -0,0 +1,246 @@ +entity_types: + Law: + prefix: "LAW" + description: "全国人大及其常委会制定的法律" + required_attributes: [name] + optional_attributes: + - full_title + - promulgation_date + - effective_date + - status + - amendment_dates + + AdministrativeRegulation: + prefix: "REG" + description: "国务院制定的行政法规" + required_attributes: [name] + optional_attributes: + - full_title + - promulgation_date + - effective_date + - status + - order_number + + DepartmentalRule: + prefix: "RULE" + description: "各部委制定的部门规章" + required_attributes: [name] + optional_attributes: + - full_title + - promulgation_date + - effective_date + - status + - order_number + + PolicyDocument: + prefix: "DOC" + description: "党中央/国务院/主管部门文件、技术标准" + required_attributes: [name] + optional_attributes: + - full_title + - issue_date + - document_number + - document_type + - status + + Chapter: + prefix: "CH" + description: "法律文件的章" + required_attributes: [name, number] + optional_attributes: + - title + + Section: + prefix: "SEC" + description: "法律文件的节" + required_attributes: [name, number] + optional_attributes: + - title + + Article: + prefix: "ART" + description: "法律文件的具体条文" + required_attributes: [number, text] + optional_attributes: + - article_type + + GovernmentBody: + prefix: "GOV" + description: "立法/行政/主管部门" + required_attributes: [name] + optional_attributes: + - full_name + - level + - type + + LegalSubject: + prefix: "SUBJ" + description: "法律规制的事项或主体" + required_attributes: [name] + optional_attributes: + - category + - description + + SpatialConcept: + prefix: "SPAT" + description: "国土空间规划中的空间概念" + required_attributes: [name] + optional_attributes: + - type + - definition + + AdministrativeProcedure: + prefix: "PROC" + description: "规划/用地/建设的行政程序" + required_attributes: [name] + optional_attributes: + - type + - time_limit + - applicable_subjects + + Obligation: + prefix: "OBL" + description: "法律条文规定的权利、义务或禁止" + required_attributes: [text] + optional_attributes: + - type + + Penalty: + prefix: "PEN" + description: "违反法律规定的处罚" + required_attributes: [type, description] + optional_attributes: + - applicable_violation + - legal_basis + + TimePoint: + prefix: "TIME" + description: "法规中提到的具体时间" + required_attributes: [description] + optional_attributes: + - year + - time_type + + Region: + prefix: "REGION" + description: "法规适用的区域" + required_attributes: [name] + optional_attributes: + - level + - feature + +relationship_types: + has_chapter: + description: "包含章" + source_types: ["Law", "AdministrativeRegulation", "DepartmentalRule", "PolicyDocument"] + target_types: ["Chapter"] + + has_section: + description: "包含节" + source_types: ["Chapter"] + target_types: ["Section"] + + contains_article: + description: "包含条" + source_types: ["Chapter", "Section"] + target_types: ["Article"] + + article_in_document: + description: "条属于文件" + source_types: ["Article"] + target_types: ["Law", "AdministrativeRegulation", "DepartmentalRule", "PolicyDocument"] + + cites: + description: "引用其他法规" + source_types: ["Article", "PolicyDocument"] + target_types: ["Law", "AdministrativeRegulation", "DepartmentalRule"] + + implements: + description: "实施/落实上位法" + source_types: ["PolicyDocument", "DepartmentalRule"] + target_types: ["Law", "AdministrativeRegulation"] + + amends: + description: "修正/修订" + source_types: ["Law", "AdministrativeRegulation", "DepartmentalRule"] + target_types: ["Law", "AdministrativeRegulation", "DepartmentalRule"] + + replaces: + description: "替代/废止" + source_types: ["Law", "AdministrativeRegulation", "DepartmentalRule"] + target_types: ["Law", "AdministrativeRegulation", "DepartmentalRule"] + + supplements: + description: "补充" + source_types: ["PolicyDocument"] + target_types: ["Law", "AdministrativeRegulation", "DepartmentalRule"] + + issued_by: + description: "发布机关" + source_types: ["Law", "AdministrativeRegulation", "DepartmentalRule", "PolicyDocument"] + target_types: ["GovernmentBody"] + + implemented_by: + description: "实施机关" + source_types: ["Article", "AdministrativeProcedure"] + target_types: ["GovernmentBody"] + + regulates: + description: "规制事项" + source_types: ["Article", "Obligation"] + target_types: ["LegalSubject"] + + defines_spatial: + description: "界定空间概念" + source_types: ["Article"] + target_types: ["SpatialConcept"] + + applies_to: + description: "适用于" + source_types: ["Article", "AdministrativeProcedure"] + target_types: ["LegalSubject"] + + sets_obligation: + description: "定义务" + source_types: ["Article"] + target_types: ["Obligation"] + + prescribes_penalty: + description: "规定处罚" + source_types: ["Article", "Obligation"] + target_types: ["Penalty"] + + requires_procedure: + description: "要求程序" + source_types: ["Article"] + target_types: ["AdministrativeProcedure"] + + procedure_for: + description: "程序对应事项" + source_types: ["AdministrativeProcedure"] + target_types: ["LegalSubject"] + + governs_region: + description: "管辖区域" + source_types: ["Law", "AdministrativeRegulation", "DepartmentalRule"] + target_types: ["Region"] + + effective_timeline: + description: "生效时间线" + source_types: ["Law", "AdministrativeRegulation", "DepartmentalRule"] + target_types: ["TimePoint"] + + related_to: + description: "相关" + source_types: ["Any"] + target_types: ["Any"] + + standard_for: + description: "标准适用于" + source_types: ["PolicyDocument"] + target_types: ["LegalSubject"] + + category_index: + description: "分类索引" + source_types: ["Law", "AdministrativeRegulation", "DepartmentalRule", "PolicyDocument"] + target_types: ["LegalSubject"] diff --git a/dofile/kg_project/neo4j/import_data.py b/dofile/kg_project/neo4j/import_data.py new file mode 100644 index 0000000..936bdb2 --- /dev/null +++ b/dofile/kg_project/neo4j/import_data.py @@ -0,0 +1,139 @@ +""" +Neo4j数据导入脚本 - 从CSV导入法规知识图谱数据 +""" + +import pandas as pd +from pathlib import Path +from neo4j import GraphDatabase +import json +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + + +class Neo4jImporter: + """Neo4j数据导入器""" + + def __init__(self, uri: str, username: str, password: str, database: str = 'neo4j'): + self.driver = GraphDatabase.driver(uri, auth=(username, password)) + self.database = database + logger.info(f"连接Neo4j: {uri}") + + def close(self): + self.driver.close() + + def _run_query(self, query, parameters=None): + with self.driver.session(database=self.database) as session: + result = session.run(query, parameters or {}) + return [record.data() for record in result] + + def create_constraints(self): + """创建约束和索引""" + schema_file = Path(__file__).parent / 'schema.cypher' + if schema_file.exists(): + with open(schema_file, 'r', encoding='utf-8') as f: + content = f.read() + + for line in content.split('\n'): + line = line.strip() + if line.startswith('CREATE ') and not line.startswith('//'): + try: + self._run_query(line) + logger.info(f"执行: {line[:60]}...") + except Exception as e: + if 'already exists' not in str(e): + logger.warning(f"约束跳过: {e}") + + def import_nodes(self, nodes_csv: str): + """导入节点""" + nodes_df = pd.read_csv(nodes_csv, encoding='utf-8-sig') + logger.info(f"导入 {len(nodes_df)} 个节点") + + # 按类型分批导入 + for node_type in nodes_df['type'].unique(): + type_df = nodes_df[nodes_df['type'] == node_type] + label = node_type + + for _, row in type_df.iterrows(): + props = {} + try: + props = json.loads(row.get('properties', '{}')) + except: + pass + props['id'] = row['id'] + props['name'] = row['label'] + + query = f"MERGE (n:{label} {{id: $id}}) SET n += $props" + self._run_query(query, {'id': row['id'], 'props': props}) + + logger.info(f" {label}: {len(type_df)} 个") + + def import_relationships(self, rels_csv: str): + """导入关系""" + rels_df = pd.read_csv(rels_csv, encoding='utf-8-sig') + logger.info(f"导入 {len(rels_df)} 个关系") + + imported = 0 + for _, row in rels_df.iterrows(): + rel_type = row['type'] + props = {} + try: + props = json.loads(row.get('properties', '{}')) + except: + pass + + query = f""" + MATCH (a {{id: $source}}) + MATCH (b {{id: $target}}) + MERGE (a)-[r:{rel_type}]->(b) + SET r += $props + """ + try: + self._run_query(query, { + 'source': row['source'], + 'target': row['target'], + 'props': props + }) + imported += 1 + except Exception as e: + logger.warning(f"关系导入失败: {row['source']} -> {row['target']} ({rel_type}): {e}") + + logger.info(f"成功导入 {imported}/{len(rels_df)} 个关系") + + def import_all(self, nodes_csv: str, rels_csv: str): + """完整导入流程""" + logger.info("开始导入...") + self.create_constraints() + self.import_nodes(nodes_csv) + self.import_relationships(rels_csv) + logger.info("导入完成!") + + +def main(): + import yaml + + config_file = Path(__file__).parent.parent / 'config' / 'legal_config.yaml' + with open(config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + neo4j_config = config['neo4j'] + importer = Neo4jImporter( + neo4j_config['uri'], + neo4j_config['username'], + neo4j_config['password'], + neo4j_config['database'] + ) + + output_dir = Path(__file__).parent.parent / 'output' + nodes_csv = str(output_dir / 'nodes_merged.csv') + rels_csv = str(output_dir / 'rels_merged.csv') + + try: + importer.import_all(nodes_csv, rels_csv) + finally: + importer.close() + + +if __name__ == '__main__': + main() diff --git a/dofile/kg_project/neo4j/schema.cypher b/dofile/kg_project/neo4j/schema.cypher new file mode 100644 index 0000000..73b1710 --- /dev/null +++ b/dofile/kg_project/neo4j/schema.cypher @@ -0,0 +1,62 @@ +// Neo4j数据模型 - 城市规划法律法规知识图谱 + +// ============================================================ +// 约束和索引 +// ============================================================ + +CREATE CONSTRAINT law_id IF NOT EXISTS FOR (l:Law) REQUIRE l.law_id IS UNIQUE; +CREATE CONSTRAINT reg_id IF NOT EXISTS FOR (r:AdministrativeRegulation) REQUIRE r.reg_id IS UNIQUE; +CREATE CONSTRAINT rule_id IF NOT EXISTS FOR (r:DepartmentalRule) REQUIRE r.rule_id IS UNIQUE; +CREATE CONSTRAINT doc_id IF NOT EXISTS FOR (d:PolicyDocument) REQUIRE d.doc_id IS UNIQUE; +CREATE CONSTRAINT chapter_id IF NOT EXISTS FOR (c:Chapter) REQUIRE c.chapter_id IS UNIQUE; +CREATE CONSTRAINT section_id IF NOT EXISTS FOR (s:Section) REQUIRE s.section_id IS UNIQUE; +CREATE CONSTRAINT article_id IF NOT EXISTS FOR (a:Article) REQUIRE a.article_id IS UNIQUE; +CREATE CONSTRAINT body_id IF NOT EXISTS FOR (g:GovernmentBody) REQUIRE g.body_id IS UNIQUE; +CREATE CONSTRAINT subject_id IF NOT EXISTS FOR (s:LegalSubject) REQUIRE s.subject_id IS UNIQUE; +CREATE CONSTRAINT concept_id IF NOT EXISTS FOR (s:SpatialConcept) REQUIRE s.concept_id IS UNIQUE; +CREATE CONSTRAINT proc_id IF NOT EXISTS FOR (p:AdministrativeProcedure) REQUIRE p.proc_id IS UNIQUE; + +// 索引 +CREATE INDEX law_name IF NOT EXISTS FOR (l:Law) ON (l.name); +CREATE INDEX law_status IF NOT EXISTS FOR (l:Law) ON (l.status); +CREATE INDEX article_type IF NOT EXISTS FOR (a:Article) ON (a.article_type); +CREATE INDEX body_name IF NOT EXISTS FOR (g:GovernmentBody) ON (g.name); +CREATE INDEX subject_category IF NOT EXISTS FOR (s:LegalSubject) ON (s.category); + +// 全文搜索 +CREATE FULLTEXT INDEX law_search IF NOT EXISTS FOR (l:Law) ON EACH [l.name, l.full_title]; +CREATE FULLTEXT INDEX article_search IF NOT EXISTS FOR (a:Article) ON EACH [a.text]; + +// ============================================================ +// 导入命令示例 +// ============================================================ + +// LOAD CSV WITH HEADERS FROM 'file:///nodes.csv' AS row +// WITH row WHERE row.type = 'Law' +// CREATE (:Law {law_id: row.id, name: row.label, full_title: apoc.text.replace(row.label, '"', '')}); +// +// LOAD CSV WITH HEADERS FROM 'file:///rels.csv' AS row +// MATCH (a {id: row.source}) +// MATCH (b {id: row.target}) +// CALL apoc.create.relationship(a, row.type, {}, b) YIELD rel +// RETURN rel; + +// ============================================================ +// 常用查询 +// ============================================================ + +// 查询某法律的所有条文 +// MATCH (l:Law {name: '城乡规划法'})-[:HAS_CHAPTER]->(ch)-[:CONTAINS_ARTICLE]->(art:Article) +// RETURN ch.title, art.number, art.text; + +// 查询引用某法律的法规 +// MATCH (d)-[r:CITES]->(l:Law {name: '城乡规划法'}) +// RETURN d.name, type(r), r.context; + +// 查询涉及某主题的所有条文 +// MATCH (art:Article)-[:REGULATES]->(s:LegalSubject {name: '建设用地'}) +// RETURN art.text; + +// 查询某机关发布的所有文件 +// MATCH (d)-[:ISSUED_BY]->(g:GovernmentBody {name: '自然资源部'}) +// RETURN d.name, labels(d); diff --git a/dofile/kg_project/ontology/legal_ontology.json b/dofile/kg_project/ontology/legal_ontology.json new file mode 100644 index 0000000..d5456d3 --- /dev/null +++ b/dofile/kg_project/ontology/legal_ontology.json @@ -0,0 +1,213 @@ +{ + "ontology": { + "name": "Urban Planning Law Knowledge Graph Ontology", + "version": "1.0", + "description": "城市规划法律法规知识图谱本体模型", + "created_date": "2026-05-13", + "concepts": [ + { + "id": "Law", + "name": "法律", + "description": "全国人大及其常委会制定的法律", + "properties": [ + {"name": "law_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "full_title", "type": "string"}, + {"name": "promulgation_date", "type": "string"}, + {"name": "effective_date", "type": "string"}, + {"name": "amendment_dates", "type": "array"}, + {"name": "status", "type": "string", "enum": ["现行有效", "修订", "废止", "草案"]}, + {"name": "legal_hierarchy", "type": "string", "default": "法律"} + ] + }, + { + "id": "AdministrativeRegulation", + "name": "行政法规", + "description": "国务院制定的行政法规", + "properties": [ + {"name": "reg_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "full_title", "type": "string"}, + {"name": "promulgation_date", "type": "string"}, + {"name": "effective_date", "type": "string"}, + {"name": "status", "type": "string"}, + {"name": "order_number", "type": "string"}, + {"name": "legal_hierarchy", "type": "string", "default": "行政法规"} + ] + }, + { + "id": "DepartmentalRule", + "name": "部门规章", + "description": "各部委制定的部门规章", + "properties": [ + {"name": "rule_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "full_title", "type": "string"}, + {"name": "promulgation_date", "type": "string"}, + {"name": "effective_date", "type": "string"}, + {"name": "status", "type": "string"}, + {"name": "order_number", "type": "string"}, + {"name": "legal_hierarchy", "type": "string", "default": "部门规章"} + ] + }, + { + "id": "PolicyDocument", + "name": "政策文件", + "description": "党中央/国务院/主管部门文件、技术标准", + "properties": [ + {"name": "doc_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "full_title", "type": "string"}, + {"name": "issue_date", "type": "string"}, + {"name": "document_number", "type": "string"}, + {"name": "document_type", "type": "string", "enum": ["意见", "通知", "纲要", "指南", "标准", "规范", "办法", "规定", "决定", "批复", "函", "其他"]}, + {"name": "status", "type": "string"}, + {"name": "legal_hierarchy", "type": "string", "default": "政策文件"} + ] + }, + { + "id": "Chapter", + "name": "章", + "description": "法律文件的章", + "properties": [ + {"name": "chapter_id", "type": "string", "required": true, "unique": true}, + {"name": "number", "type": "string"}, + {"name": "title", "type": "string"} + ] + }, + { + "id": "Section", + "name": "节", + "description": "法律文件的节", + "properties": [ + {"name": "section_id", "type": "string", "required": true, "unique": true}, + {"name": "number", "type": "string"}, + {"name": "title", "type": "string"} + ] + }, + { + "id": "Article", + "name": "条", + "description": "法律文件的具体条文", + "properties": [ + {"name": "article_id", "type": "string", "required": true, "unique": true}, + {"name": "number", "type": "string"}, + {"name": "text", "type": "string"}, + {"name": "article_type", "type": "string", "enum": ["管理性", "禁止性", "授权性", "程序性", "处罚性", "定义性"]} + ] + }, + { + "id": "GovernmentBody", + "name": "政府机构", + "description": "立法/行政/主管部门", + "properties": [ + {"name": "body_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "full_name", "type": "string"}, + {"name": "level", "type": "string", "enum": ["国家", "省级", "市级", "县级"]}, + {"name": "type", "type": "string", "enum": ["立法机关", "行政机关", "主管部门", "地方机关"]} + ] + }, + { + "id": "LegalSubject", + "name": "法定事项", + "description": "法律规制的事项或主体", + "properties": [ + {"name": "subject_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "category", "type": "string", "enum": ["土地", "规划", "建设", "环境", "文物", "交通", "防灾", "生态", "基础设施", "更新", "保护", "测绘", "登记", "处罚", "住房", "市政", "园林", "水资源", "矿业"]}, + {"name": "description", "type": "string"} + ] + }, + { + "id": "SpatialConcept", + "name": "空间概念", + "description": "国土空间规划中的空间概念", + "properties": [ + {"name": "concept_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "type", "type": "string", "enum": ["国土空间", "生态红线", "永久基本农田", "城镇开发边界", "规划区", "建设用地", "农用地", "未利用地", "历史文化保护区", "控制线", "城市更新单元"]}, + {"name": "definition", "type": "string"} + ] + }, + { + "id": "AdministrativeProcedure", + "name": "行政程序", + "description": "规划/用地/建设的行政程序", + "properties": [ + {"name": "proc_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "type", "type": "string", "enum": ["审批", "核准", "备案", "登记", "许可", "验收", "监督", "听证", "征收", "拆迁", "出让", "转让"]}, + {"name": "time_limit", "type": "string"} + ] + }, + { + "id": "Obligation", + "name": "权利义务", + "description": "法律条文规定的权利、义务或禁止", + "properties": [ + {"name": "oblig_id", "type": "string", "required": true, "unique": true}, + {"name": "text", "type": "string"}, + {"name": "type", "type": "string", "enum": ["权利", "义务", "禁止", "限制", "鼓励"]} + ] + }, + { + "id": "Penalty", + "name": "法律责任", + "description": "违反法律规定的处罚", + "properties": [ + {"name": "penalty_id", "type": "string", "required": true, "unique": true}, + {"name": "type", "type": "string", "enum": ["行政处罚", "刑事处罚", "民事责任", "行政处分"]}, + {"name": "description", "type": "string"} + ] + }, + { + "id": "TimePoint", + "name": "时间节点", + "description": "法规中提到的具体时间", + "properties": [ + {"name": "time_id", "type": "string", "required": true, "unique": true}, + {"name": "year", "type": "integer"}, + {"name": "description", "type": "string"}, + {"name": "time_type", "type": "string", "enum": ["生效日期", "废止日期", "修订日期", "过渡期", "截止日期"]} + ] + }, + { + "id": "Region", + "name": "区域", + "description": "法规适用的区域", + "properties": [ + {"name": "region_id", "type": "string", "required": true, "unique": true}, + {"name": "name", "type": "string", "required": true}, + {"name": "level", "type": "string", "enum": ["全国", "区域", "省", "市", "县", "特定区域"]} + ] + } + ], + "relationships": [ + {"name": "has_chapter", "description": "包含章", "from": "Law/AdministrativeRegulation/DepartmentalRule/PolicyDocument", "to": "Chapter", "properties": [{"name": "order_number", "type": "integer"}]}, + {"name": "has_section", "description": "包含节", "from": "Chapter", "to": "Section", "properties": [{"name": "order_number", "type": "integer"}]}, + {"name": "contains_article", "description": "包含条", "from": "Chapter/Section", "to": "Article", "properties": [{"name": "order_number", "type": "integer"}]}, + {"name": "article_in_document", "description": "条属于文件", "from": "Article", "to": "Law/AdministrativeRegulation/DepartmentalRule/PolicyDocument", "properties": []}, + {"name": "cites", "description": "引用", "from": "Article/PolicyDocument", "to": "Law/AdministrativeRegulation/DepartmentalRule", "properties": [{"name": "context", "type": "string"}]}, + {"name": "implements", "description": "实施/落实", "from": "PolicyDocument/DepartmentalRule", "to": "Law/AdministrativeRegulation", "properties": [{"name": "scope", "type": "string"}]}, + {"name": "amends", "description": "修正/修订", "from": "Law/Regulation/Rule", "to": "Law/Regulation/Rule", "properties": [{"name": "amendment_date", "type": "string"}]}, + {"name": "replaces", "description": "替代/废止", "from": "Law/Regulation/Rule", "to": "Law/Regulation/Rule", "properties": [{"name": "replacement_date", "type": "string"}]}, + {"name": "supplements", "description": "补充", "from": "PolicyDocument", "to": "Law/Regulation/Rule", "properties": [{"name": "scope", "type": "string"}]}, + {"name": "issued_by", "description": "发布机关", "from": "Law/Regulation/Rule/PolicyDocument", "to": "GovernmentBody", "properties": [{"name": "role", "type": "string"}]}, + {"name": "implemented_by", "description": "实施机关", "from": "Article/AdministrativeProcedure", "to": "GovernmentBody", "properties": [{"name": "role", "type": "string"}]}, + {"name": "regulates", "description": "规制", "from": "Article/Obligation", "to": "LegalSubject", "properties": [{"name": "regulation_type", "type": "string"}]}, + {"name": "defines_spatial", "description": "界定空间", "from": "Article", "to": "SpatialConcept", "properties": [{"name": "scope", "type": "string"}]}, + {"name": "applies_to", "description": "适用于", "from": "Article/AdministrativeProcedure", "to": "LegalSubject", "properties": [{"name": "context", "type": "string"}]}, + {"name": "sets_obligation", "description": "定义务", "from": "Article", "to": "Obligation", "properties": [{"name": "obligation_type", "type": "string"}]}, + {"name": "prescribes_penalty", "description": "规定处罚", "from": "Article/Obligation", "to": "Penalty", "properties": [{"name": "conditions", "type": "string"}]}, + {"name": "requires_procedure", "description": "要求程序", "from": "Article", "to": "AdministrativeProcedure", "properties": [{"name": "mandatory", "type": "string"}]}, + {"name": "procedure_for", "description": "程序对应事项", "from": "AdministrativeProcedure", "to": "LegalSubject", "properties": [{"name": "purpose", "type": "string"}]}, + {"name": "procedure_step", "description": "程序步骤", "from": "AdministrativeProcedure", "to": "AdministrativeProcedure", "properties": [{"name": "step_order", "type": "integer"}]}, + {"name": "governs_region", "description": "管辖区域", "from": "Law/Regulation/Rule", "to": "Region", "properties": [{"name": "scope", "type": "string"}]}, + {"name": "effective_timeline", "description": "生效时间线", "from": "Law/Regulation/Rule", "to": "TimePoint", "properties": [{"name": "timeline_type", "type": "string"}]}, + {"name": "related_to", "description": "相关", "from": "Any", "to": "Any", "properties": [{"name": "description", "type": "string"}]}, + {"name": "standard_for", "description": "标准适用于", "from": "PolicyDocument", "to": "LegalSubject", "properties": [{"name": "standard_type", "type": "string"}]}, + {"name": "category_index", "description": "分类索引", "from": "Law/Regulation/Rule/PolicyDocument", "to": "LegalSubject", "properties": [{"name": "source", "type": "string"}]} + ] + } +} diff --git a/dofile/kg_project/ontology/urban_renewal_ontology_21_entities_32_relations.json b/dofile/kg_project/ontology/urban_renewal_ontology_21_entities_32_relations.json new file mode 100644 index 0000000..f9a329f --- /dev/null +++ b/dofile/kg_project/ontology/urban_renewal_ontology_21_entities_32_relations.json @@ -0,0 +1,1973 @@ +{ + "ontology": { + "name": "Urban Renewal Regulation Policy Toolbox Knowledge Graph Ontology", + "name_cn": "城市更新法规政策工具箱知识图谱本体", + "version": "1.0", + "description": "面向城市更新法规政策工具箱构建的知识图谱本体模型,用于表达法规政策文件、条文结构、主体、法规事项、空间对象、更新场景、政策工具、程序条件约束以及辅助信息之间的关系。", + "created_date": "2026-05-14", + "language": "zh-CN", + "entity_count": 21, + "relationship_count": 34, + "design_principle": "以Article和Clause作为法规文本抽取的基本单元,通过政策工具、更新场景、空间对象、程序、条件和约束等节点,将法规条文转译为城市更新政策工具体系。", + "concept_groups": [ + { + "group_id": "document_entities", + "name": "文档实体", + "count": 4, + "entities": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ] + }, + { + "group_id": "structure_entities", + "name": "结构实体", + "count": 4, + "entities": [ + "Chapter", + "Section", + "Article", + "Clause" + ] + }, + { + "group_id": "agency_entities", + "name": "主体实体", + "count": 1, + "entities": [ + "Agency" + ] + }, + { + "group_id": "semantic_object_entities", + "name": "语义对象", + "count": 3, + "entities": [ + "LegalObject", + "SpatialObject", + "RenewalScene" + ] + }, + { + "group_id": "tool_entities", + "name": "工具实体", + "count": 2, + "entities": [ + "PolicyTool", + "ToolCategory" + ] + }, + { + "group_id": "procedure_constraint_entities", + "name": "程序与约束实体", + "count": 5, + "entities": [ + "Procedure", + "Obligation", + "Condition", + "Constraint", + "Penalty" + ] + }, + { + "group_id": "auxiliary_entities", + "name": "辅助实体", + "count": 2, + "entities": [ + "TimePoint", + "Region" + ] + } + ], + "relationship_groups": [ + { + "group_id": "document_hierarchy_relations", + "name": "文档层次关系", + "count": 5, + "relationships": [ + "has_chapter", + "has_section", + "has_article", + "has_clause", + "article_in_document" + ] + }, + { + "group_id": "legal_basis_relations", + "name": "法条依据关系", + "count": 6, + "relationships": [ + "cites", + "implements", + "amends", + "replaces", + "supplements", + "basis_for_planning" + ] + }, + { + "group_id": "agency_relations", + "name": "主体关系", + "count": 3, + "relationships": [ + "issued_by", + "implemented_by", + "assigned_to" + ] + }, + { + "group_id": "semantic_regulation_relations", + "name": "语义规制关系", + "count": 7, + "relationships": [ + "regulates", + "defines_object", + "applies_to", + "sets_obligation", + "sets_condition", + "sets_constraint", + "prescribes_penalty" + ] + }, + { + "group_id": "policy_tool_relations", + "name": "政策关系", + "count": 6, + "relationships": [ + "extracts_tool", + "belongs_to_category", + "applies_to_scene", + "targets_object", + "coordinates_with", + "supports_tool" + ] + }, + { + "group_id": "procedure_relations", + "name": "程序关系", + "count": 3, + "relationships": [ + "requires_procedure", + "procedure_for", + "precedes_procedure" + ] + }, + { + "group_id": "auxiliary_relations", + "name": "辅助关系", + "count": 4, + "relationships": [ + "governs_region", + "effective_timeline", + "standard_for", + "article_effective_timeline" + ] + } + ], + "concepts": [ + { + "id": "Law", + "name": "法律", + "description": "全国人民代表大会及其常务委员会制定的法律。", + "group": "文档实体", + "properties": [ + { + "name": "law_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "full_title", + "type": "string" + }, + { + "name": "promulgation_date", + "type": "string" + }, + { + "name": "effective_date", + "type": "string" + }, + { + "name": "amendment_dates", + "type": "array" + }, + { + "name": "status", + "type": "string", + "enum": [ + "现行有效", + "已修改", + "已废止", + "草案", + "未知" + ] + }, + { + "name": "legal_hierarchy", + "type": "string", + "default": "法律" + } + ] + }, + { + "id": "AdministrativeRegulation", + "name": "行政法规", + "description": "国务院制定或发布的行政法规。", + "group": "文档实体", + "properties": [ + { + "name": "reg_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "full_title", + "type": "string" + }, + { + "name": "promulgation_date", + "type": "string" + }, + { + "name": "effective_date", + "type": "string" + }, + { + "name": "order_number", + "type": "string" + }, + { + "name": "status", + "type": "string", + "enum": [ + "现行有效", + "已修改", + "已废止", + "草案", + "未知" + ] + }, + { + "name": "legal_hierarchy", + "type": "string", + "default": "行政法规" + } + ] + }, + { + "id": "DepartmentalRule", + "name": "部门规章", + "description": "国务院部门、直属机构等制定的部门规章。", + "group": "文档实体", + "properties": [ + { + "name": "rule_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "full_title", + "type": "string" + }, + { + "name": "promulgation_date", + "type": "string" + }, + { + "name": "effective_date", + "type": "string" + }, + { + "name": "order_number", + "type": "string" + }, + { + "name": "status", + "type": "string", + "enum": [ + "现行有效", + "已修改", + "已废止", + "草案", + "未知" + ] + }, + { + "name": "legal_hierarchy", + "type": "string", + "default": "部门规章" + } + ] + }, + { + "id": "PolicyDocument", + "name": "政策文件", + "description": "党中央、国务院、主管部门或地方政府发布的意见、通知、办法、规定、指南、技术标准、技术导则、规范、工作方案等政策性或标准性文件。", + "group": "文档实体", + "properties": [ + { + "name": "doc_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "full_title", + "type": "string" + }, + { + "name": "issue_date", + "type": "string" + }, + { + "name": "document_number", + "type": "string" + }, + { + "name": "document_type", + "type": "string", + "enum": [ + "意见", + "通知", + "办法", + "规定", + "指南", + "技术标准", + "技术导则", + "规范", + "工作方案", + "实施方案", + "规划", + "其他" + ] + }, + { + "name": "status", + "type": "string", + "enum": [ + "现行有效", + "已修改", + "已废止", + "草案", + "未知" + ] + }, + { + "name": "legal_hierarchy", + "type": "string", + "default": "政策文件" + } + ] + }, + { + "id": "Chapter", + "name": "章", + "description": "法规政策文件中的章级结构。", + "group": "结构实体", + "properties": [ + { + "name": "chapter_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "number", + "type": "string" + }, + { + "name": "title", + "type": "string" + }, + { + "name": "order_number", + "type": "integer" + } + ] + }, + { + "id": "Section", + "name": "节", + "description": "法规政策文件中的节级结构。", + "group": "结构实体", + "properties": [ + { + "name": "section_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "number", + "type": "string" + }, + { + "name": "title", + "type": "string" + }, + { + "name": "order_number", + "type": "integer" + } + ] + }, + { + "id": "Article", + "name": "条", + "description": "法规政策文件中的条文,是本体抽取的主要文本单元。", + "group": "结构实体", + "properties": [ + { + "name": "article_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "number", + "type": "string" + }, + { + "name": "title", + "type": "string" + }, + { + "name": "text", + "type": "string", + "required": true + }, + { + "name": "article_type", + "type": "string", + "enum": [ + "定义性", + "原则性", + "管理性", + "授权性", + "程序性", + "禁止性", + "处罚性", + "保障性", + "其他" + ] + }, + { + "name": "order_number", + "type": "integer" + } + ] + }, + { + "id": "Clause", + "name": "款", + "description": "条文中的款级结构。当条文包含多款或多个并列规则时,可将Clause作为更细的抽取单位。", + "group": "结构实体", + "properties": [ + { + "name": "clause_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "number", + "type": "string" + }, + { + "name": "text", + "type": "string", + "required": true + }, + { + "name": "order_number", + "type": "integer" + } + ] + }, + { + "id": "Agency", + "name": "主体", + "description": "发布主体、实施主体、监管主体、责任主体的统称,包括政府部门、主管机关、实施机构、平台公司、社区组织、第三方机构等。", + "group": "主体实体", + "properties": [ + { + "name": "agency_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "full_name", + "type": "string" + }, + { + "name": "level", + "type": "string", + "enum": [ + "国家", + "省级", + "市级", + "县级", + "乡镇街道", + "社区", + "其他" + ] + }, + { + "name": "agency_type", + "type": "string", + "enum": [ + "立法机关", + "行政机关", + "主管部门", + "实施主体", + "监管主体", + "责任主体", + "市场主体", + "社区组织", + "第三方机构", + "其他" + ] + }, + { + "name": "description", + "type": "string" + } + ] + }, + { + "id": "LegalObject", + "name": "法规事项", + "description": "法规政策所规制的事项或治理对象,强调“管什么事项”。", + "group": "语义对象", + "properties": [ + { + "name": "object_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "category", + "type": "string", + "enum": [ + "规划编制", + "土地利用", + "建设管理", + "历史文化保护", + "生态保护", + "财政支持", + "金融支持", + "产权登记", + "公众参与", + "行政审批", + "监督管理", + "城市更新", + "公共服务", + "基础设施", + "安全韧性", + "其他" + ] + }, + { + "name": "description", + "type": "string" + } + ] + }, + { + "id": "SpatialObject", + "name": "空间对象", + "description": "城市更新和国土空间治理中的具体空间对象,强调“作用于什么空间”。", + "group": "语义对象", + "properties": [ + { + "name": "spatial_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "spatial_type", + "type": "string", + "enum": [ + "老旧小区", + "城中村", + "低效用地", + "工业遗存", + "历史文化街区", + "历史建筑", + "公共空间", + "公共服务设施", + "市政基础设施", + "存量建筑", + "建设用地", + "城市更新单元", + "生态保护红线", + "永久基本农田", + "城镇开发边界", + "其他" + ] + }, + { + "name": "definition", + "type": "string" + } + ] + }, + { + "id": "RenewalScene", + "name": "更新场景", + "description": "根据城市更新对象、治理任务和政策目标归纳形成的应用场景,强调“用于什么更新场景”。", + "group": "语义对象", + "properties": [ + { + "name": "scene_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "scene_type", + "type": "string", + "enum": [ + "老旧小区改造", + "城中村改造", + "低效用地再开发", + "工业遗存更新", + "历史文化保护更新", + "完整社区建设", + "公共空间提升", + "基础设施补短板", + "城市安全韧性提升", + "城市生态修复", + "其他" + ] + }, + { + "name": "derivation_method", + "type": "string", + "enum": [ + "原文提取", + "语义归纳", + "人工标注", + "模型推理" + ] + }, + { + "name": "description", + "type": "string" + } + ] + }, + { + "id": "PolicyTool", + "name": "具体政策工具", + "description": "城市更新法规政策中用于实现治理目标的具体政策、制度、措施或治理手段。", + "group": "工具实体", + "properties": [ + { + "name": "tool_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "tool_type", + "type": "string", + "enum": [ + "命令型", + "激励型", + "劝诫型", + "能力型", + "系统变革型", + "其他" + ] + }, + { + "name": "mandatory_level", + "type": "string", + "enum": [ + "强制", + "引导", + "鼓励", + "支持", + "禁止", + "限制", + "其他" + ] + }, + { + "name": "description", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "id": "ToolCategory", + "name": "政策工具类别", + "description": "用于归类城市更新政策工具的分类节点。", + "group": "工具实体", + "properties": [ + { + "name": "category_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "classification_system", + "type": "string", + "enum": [ + "一般政策工具分类", + "城市更新工具分类" + ] + }, + { + "name": "urban_renewal_category", + "type": "string", + "enum": [ + "规划引导", + "土地配置", + "开发激励", + "财政金融", + "权益协调", + "实施管理", + "监督反馈", + "其他" + ] + }, + { + "name": "description", + "type": "string" + } + ] + }, + { + "id": "Procedure", + "name": "程序", + "description": "法规政策要求履行的审批、备案、评估、公示、验收、入库等行政或治理程序。", + "group": "程序与约束实体", + "properties": [ + { + "name": "procedure_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "procedure_type", + "type": "string", + "enum": [ + "审批", + "核准", + "备案", + "登记", + "许可", + "评估", + "论证", + "公示", + "听证", + "入库", + "验收", + "监督", + "征收", + "补偿", + "其他" + ] + }, + { + "name": "time_limit", + "type": "string" + }, + { + "name": "description", + "type": "string" + } + ] + }, + { + "id": "Obligation", + "name": "权利义务", + "description": "法规政策规定的权利、义务、禁止或责任表达,强调主体应当做什么或不得做什么。", + "group": "程序与约束实体", + "properties": [ + { + "name": "obligation_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "text", + "type": "string", + "required": true + }, + { + "name": "obligation_type", + "type": "string", + "enum": [ + "权利", + "义务", + "禁止", + "职责", + "鼓励", + "支持", + "其他" + ] + }, + { + "name": "modality", + "type": "string", + "enum": [ + "应当", + "可以", + "不得", + "禁止", + "鼓励", + "支持", + "负责", + "依法", + "其他" + ] + } + ] + }, + { + "id": "Condition", + "name": "适用条件", + "description": "政策工具、程序或行为适用时需要满足的前置条件。", + "group": "程序与约束实体", + "properties": [ + { + "name": "condition_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "text", + "type": "string", + "required": true + }, + { + "name": "condition_type", + "type": "string", + "enum": [ + "规划条件", + "审批条件", + "权属条件", + "安全条件", + "环保条件", + "公众参与条件", + "程序条件", + "资金条件", + "其他" + ] + } + ] + }, + { + "id": "Constraint", + "name": "约束条件", + "description": "对政策工具、空间对象或行为设置的边界性、底线性或限制性要求。", + "group": "程序与约束实体", + "properties": [ + { + "name": "constraint_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "text", + "type": "string", + "required": true + }, + { + "name": "constraint_type", + "type": "string", + "enum": [ + "用途管制", + "空间边界", + "强制标准", + "历史保护", + "生态保护", + "权益保护", + "安全底线", + "公共利益", + "其他" + ] + } + ] + }, + { + "id": "Penalty", + "name": "法律责任", + "description": "违反法规政策规定后需要承担的法律责任、行政责任或其他责任。", + "group": "程序与约束实体", + "properties": [ + { + "name": "penalty_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "penalty_type", + "type": "string", + "enum": [ + "行政处罚", + "刑事处罚", + "民事责任", + "行政处分", + "信用惩戒", + "整改责任", + "其他" + ] + }, + { + "name": "description", + "type": "string" + }, + { + "name": "amount_or_measure", + "type": "string" + } + ] + }, + { + "id": "TimePoint", + "name": "时间节点", + "description": "法规政策中涉及的生效、修订、废止、截止、过渡期等时间信息。", + "group": "辅助实体", + "properties": [ + { + "name": "time_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "date", + "type": "string" + }, + { + "name": "year", + "type": "integer" + }, + { + "name": "time_type", + "type": "string", + "enum": [ + "发布日期", + "生效日期", + "修订日期", + "废止日期", + "截止日期", + "过渡期", + "其他" + ] + }, + { + "name": "description", + "type": "string" + } + ] + }, + { + "id": "Region", + "name": "区域", + "description": "法规政策适用的行政区域、空间范围或特定地区。", + "group": "辅助实体", + "properties": [ + { + "name": "region_id", + "type": "string", + "required": true, + "unique": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "level", + "type": "string", + "enum": [ + "全国", + "区域", + "省", + "市", + "县", + "乡镇街道", + "社区", + "特定区域", + "其他" + ] + }, + { + "name": "description", + "type": "string" + } + ] + } + ], + "relationships": [ + { + "name": "has_chapter", + "name_cn": "包含章", + "group": "文档层次关系", + "from": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "to": [ + "Chapter" + ], + "description": "法规政策文件包含章。", + "properties": [ + { + "name": "order_number", + "type": "integer" + } + ] + }, + { + "name": "has_section", + "name_cn": "包含节", + "group": "文档层次关系", + "from": [ + "Chapter" + ], + "to": [ + "Section" + ], + "description": "章包含节。", + "properties": [ + { + "name": "order_number", + "type": "integer" + } + ] + }, + { + "name": "has_article", + "name_cn": "包含条", + "group": "文档层次关系", + "from": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument", + "Chapter", + "Section" + ], + "to": [ + "Article" + ], + "description": "文件、章或节包含条文。", + "properties": [ + { + "name": "order_number", + "type": "integer" + } + ] + }, + { + "name": "has_clause", + "name_cn": "包含款", + "group": "文档层次关系", + "from": [ + "Article" + ], + "to": [ + "Clause" + ], + "description": "条文包含款。", + "properties": [ + { + "name": "order_number", + "type": "integer" + } + ] + }, + { + "name": "article_in_document", + "name_cn": "条属于文件", + "group": "文档层次关系", + "from": [ + "Article", + "Clause" + ], + "to": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "description": "条文或款属于某一法规政策文件。", + "properties": [] + }, + { + "name": "cites", + "name_cn": "引用", + "group": "法条依据关系", + "from": [ + "Article", + "Clause", + "PolicyDocument" + ], + "to": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "description": "条文、款或文件引用其他法规政策文件。", + "properties": [ + { + "name": "context", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "implements", + "name_cn": "实施落实", + "group": "法条依据关系", + "from": [ + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "to": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "description": "下位法规政策落实或实施上位法规政策。", + "properties": [ + { + "name": "scope", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "amends", + "name_cn": "修订", + "group": "法条依据关系", + "from": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "to": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "description": "某一法规政策文件修订、修改另一法规政策文件。", + "properties": [ + { + "name": "amendment_date", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "replaces", + "name_cn": "替代废止", + "group": "法条依据关系", + "from": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "to": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "description": "某一法规政策文件替代、废止或取代另一法规政策文件。", + "properties": [ + { + "name": "replacement_date", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "supplements", + "name_cn": "补充", + "group": "法条依据关系", + "from": [ + "PolicyDocument", + "DepartmentalRule", + "AdministrativeRegulation" + ], + "to": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "description": "某一法规政策文件对另一法规政策文件进行补充规定。", + "properties": [ + { + "name": "scope", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "basis_for_planning", + "name_cn": "作为规划依据", + "group": "法条依据关系", + "from": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument", + "Article", + "Clause" + ], + "to": [ + "LegalObject", + "Procedure", + "PolicyTool" + ], + "description": "法规政策或条文作为规划编制、审批、实施或政策工具设立的依据。", + "properties": [ + { + "name": "basis_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "issued_by", + "name_cn": "发布主体", + "group": "主体关系", + "from": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "to": [ + "Agency" + ], + "description": "法规政策文件由某主体发布、制定或印发。", + "properties": [ + { + "name": "role", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "implemented_by", + "name_cn": "实施主体", + "group": "主体关系", + "from": [ + "Article", + "Clause", + "PolicyTool", + "Procedure", + "Obligation" + ], + "to": [ + "Agency" + ], + "description": "条文、政策工具、程序或义务由某主体负责实施。", + "properties": [ + { + "name": "role", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "assigned_to", + "name_cn": "职责分配", + "group": "主体关系", + "from": [ + "Obligation", + "Procedure", + "PolicyTool", + "LegalObject" + ], + "to": [ + "Agency" + ], + "description": "监管职责、管理职责、实施任务或工作责任分配给某主体。", + "properties": [ + { + "name": "duty_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "regulates", + "name_cn": "规制", + "group": "语义规制关系", + "from": [ + "Article", + "Clause", + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "to": [ + "LegalObject" + ], + "description": "法规政策或条文规制某类法规事项。", + "properties": [ + { + "name": "regulation_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "defines_object", + "name_cn": "界定对象", + "group": "语义规制关系", + "from": [ + "Article", + "Clause" + ], + "to": [ + "LegalObject", + "SpatialObject", + "RenewalScene" + ], + "description": "条文或款对法规事项、空间对象或更新场景进行定义或界定。", + "properties": [ + { + "name": "definition_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "applies_to", + "name_cn": "适用于", + "group": "语义规制关系", + "from": [ + "Article", + "Clause", + "Procedure", + "PolicyTool" + ], + "to": [ + "LegalObject", + "SpatialObject", + "RenewalScene", + "Region" + ], + "description": "条文、款、程序或政策工具适用于某类法规事项、空间对象、更新场景或区域。", + "properties": [ + { + "name": "context", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "sets_obligation", + "name_cn": "设定义务", + "group": "语义规制关系", + "from": [ + "Article", + "Clause" + ], + "to": [ + "Obligation" + ], + "description": "条文或款设定权利、义务、禁止、职责或支持性要求。", + "properties": [ + { + "name": "obligation_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "sets_condition", + "name_cn": "设置条件", + "group": "语义规制关系", + "from": [ + "Article", + "Clause", + "PolicyTool", + "Procedure" + ], + "to": [ + "Condition" + ], + "description": "条文、款、政策工具或程序设置适用条件。", + "properties": [ + { + "name": "condition_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "sets_constraint", + "name_cn": "设置约束", + "group": "语义规制关系", + "from": [ + "Article", + "Clause", + "PolicyTool", + "Procedure" + ], + "to": [ + "Constraint" + ], + "description": "条文、款、政策工具或程序设置边界性、底线性或限制性要求。", + "properties": [ + { + "name": "constraint_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "extracts_tool", + "name_cn": "抽取政策工具", + "group": "政策关系", + "from": [ + "Article", + "Clause" + ], + "to": [ + "PolicyTool" + ], + "description": "从条文或款中识别出具体政策工具。", + "properties": [ + { + "name": "extraction_method", + "type": "string" + }, + { + "name": "confidence", + "type": "float" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "belongs_to_category", + "name_cn": "属于工具类别", + "group": "政策关系", + "from": [ + "PolicyTool" + ], + "to": [ + "ToolCategory" + ], + "description": "具体政策工具属于某类政策工具类别。", + "properties": [ + { + "name": "classification_basis", + "type": "string" + }, + { + "name": "confidence", + "type": "float" + } + ] + }, + { + "name": "applies_to_scene", + "name_cn": "适用于更新场景", + "group": "政策关系", + "from": [ + "PolicyTool" + ], + "to": [ + "RenewalScene" + ], + "description": "政策工具适用于某类城市更新场景。", + "properties": [ + { + "name": "applicability", + "type": "string" + }, + { + "name": "confidence", + "type": "float" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "targets_object", + "name_cn": "作用对象", + "group": "政策关系", + "from": [ + "PolicyTool" + ], + "to": [ + "LegalObject", + "SpatialObject" + ], + "description": "政策工具作用于某一法规事项或空间对象。", + "properties": [ + { + "name": "target_type", + "type": "string" + }, + { + "name": "confidence", + "type": "float" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "coordinates_with", + "name_cn": "协同关系", + "group": "政策关系", + "from": [ + "PolicyTool" + ], + "to": [ + "PolicyTool" + ], + "description": "两个政策工具之间存在并列协同、共同作用或组合使用关系。", + "properties": [ + { + "name": "coordination_type", + "type": "string" + }, + { + "name": "confidence", + "type": "float" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "supports_tool", + "name_cn": "工具支撑", + "group": "政策关系", + "from": [ + "PolicyTool" + ], + "to": [ + "PolicyTool" + ], + "description": "一个政策工具对另一个政策工具形成前置支撑、条件支撑或实施支撑。", + "properties": [ + { + "name": "support_type", + "type": "string" + }, + { + "name": "confidence", + "type": "float" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "requires_procedure", + "name_cn": "要求程序", + "group": "程序关系", + "from": [ + "Article", + "Clause", + "PolicyTool", + "LegalObject" + ], + "to": [ + "Procedure" + ], + "description": "条文、款、政策工具或法规事项要求履行某类程序。", + "properties": [ + { + "name": "mandatory", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "procedure_for", + "name_cn": "程序对应事项", + "group": "程序关系", + "from": [ + "Procedure" + ], + "to": [ + "LegalObject", + "SpatialObject", + "RenewalScene", + "PolicyTool" + ], + "description": "某一程序对应某类法规事项、空间对象、更新场景或政策工具。", + "properties": [ + { + "name": "purpose", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "precedes_procedure", + "name_cn": "程序先后", + "group": "程序关系", + "from": [ + "Procedure" + ], + "to": [ + "Procedure" + ], + "description": "一个程序在时序上先于另一个程序。", + "properties": [ + { + "name": "sequence_basis", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "governs_region", + "name_cn": "管辖区域", + "group": "辅助关系", + "from": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument", + "Article", + "Clause" + ], + "to": [ + "Region" + ], + "description": "法规政策或条文适用于某一行政区域或特定空间范围。", + "properties": [ + { + "name": "scope", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "effective_timeline", + "name_cn": "时间线", + "group": "辅助关系", + "from": [ + "Law", + "AdministrativeRegulation", + "DepartmentalRule", + "PolicyDocument" + ], + "to": [ + "TimePoint" + ], + "description": "法规政策文件关联发布、生效、修订、废止或截止等时间节点。", + "properties": [ + { + "name": "timeline_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "standard_for", + "name_cn": "标准适用于", + "group": "辅助关系", + "from": [ + "PolicyDocument", + "Article", + "Clause" + ], + "to": [ + "LegalObject", + "SpatialObject", + "Procedure", + "PolicyTool" + ], + "description": "标准、规范、导则或相关条文适用于某一法规事项、空间对象、程序或政策工具。", + "properties": [ + { + "name": "standard_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "prescribes_penalty", + "name_cn": "规定处罚", + "group": "语义规制关系", + "from": [ + "Article", + "Clause" + ], + "to": [ + "Penalty" + ], + "description": "条文或款规定了违法行为的法律后果。", + "properties": [ + { + "name": "penalty_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + }, + { + "name": "article_effective_timeline", + "name_cn": "条文时间线", + "group": "辅助关系", + "from": [ + "Article", + "Clause" + ], + "to": [ + "TimePoint" + ], + "description": "条文或款关联生效、截止、过渡期等时间节点。", + "properties": [ + { + "name": "timeline_type", + "type": "string" + }, + { + "name": "source_text", + "type": "string" + } + ] + } + ], + "extraction_guidelines": { + "basic_unit": "以Article和Clause作为抽取基本单元。条文较短时以Article抽取,条文包含多款或多个并列规则时以Clause抽取。", + "do_not_extract": [ + "不要新增本体之外的实体类型。", + "不要新增本体之外的关系类型。", + "文本中没有依据时不要强行抽取。", + "conflicts_with和has_gap不纳入本阶段抽取,留待图谱分析阶段识别。" + ], + "entity_boundary_rules": { + "LegalObject": "法规事项,表示法规规制的事项,回答“管什么事项”,如规划编制、土地利用、财政支持、公众参与。", + "SpatialObject": "空间对象,表示政策作用的具体空间,回答“作用于什么空间”,如老旧小区、城中村、低效用地、历史文化街区。", + "RenewalScene": "更新场景,表示城市更新应用场景,回答“用于什么更新场景”,如老旧小区改造、城中村改造、完整社区建设。", + "Procedure": "程序,强调流程、步骤、审批、备案、评估、公示、验收、入库等办理环节。", + "PolicyTool": "政策工具,强调治理手段、政策措施、制度安排或支持机制。", + "Obligation": "权利义务,强调主体应当做什么、可以做什么、不得做什么或负责什么。", + "Condition": "适用条件,强调符合、经批准、满足、具备、在……前提下等前置条件。", + "Constraint": "约束条件,强调不得突破、不得损害、严守、控制线、底线、强制性标准等边界性限制。" + }, + "relationship_boundary_rules": { + "applies_to": "用于表达条文、程序或工具的适用范围。", + "targets_object": "用于表达政策工具的作用对象。", + "coordinates_with": "用于表达两个政策工具并列协同、共同作用。", + "supports_tool": "用于表达一个政策工具为另一个政策工具提供前置支撑或条件支撑。", + "requires_procedure": "用于表达条文、款、政策工具或法规事项要求履行某类程序。" + }, + "output_requirements_for_llm": { + "format": "JSON", + "required_fields_for_entities": [ + "id", + "type", + "name", + "properties" + ], + "required_fields_for_relations": [ + "source", + "relation", + "target", + "properties" + ], + "required_relation_properties": [ + "source_text", + "confidence" + ] + } + } + } +} \ No newline at end of file diff --git a/dofile/kg_project/requirements.txt b/dofile/kg_project/requirements.txt new file mode 100644 index 0000000..819426c --- /dev/null +++ b/dofile/kg_project/requirements.txt @@ -0,0 +1,29 @@ +# 城市规划法律法规知识图谱构建 - 依赖包 + +# LLM相关 +langchain-deepseek>=0.1.0 +langchain-core>=0.1.0 + +# 文档读取 +python-docx>=1.1.0 + +# Neo4j +neo4j>=5.15.0 + +# 数据处理 +pandas>=2.0.0 +numpy>=1.24.0 + +# 文本相似度 +python-Levenshtein>=0.23.0 + +# 可视化 +networkx>=3.1 +matplotlib>=3.7.0 + +# 配置 +pyyaml>=6.0 +python-dotenv>=1.0.0 + +# 工具 +tqdm>=4.65.0 diff --git a/dofile/kg_project/scripts/docx_reader.py b/dofile/kg_project/scripts/docx_reader.py new file mode 100644 index 0000000..7f77ddd --- /dev/null +++ b/dofile/kg_project/scripts/docx_reader.py @@ -0,0 +1,221 @@ +""" +Word文档读取器 - 读取城市更新法规数据库中所有.docx文件 +""" + +import json +import re +import logging +from pathlib import Path +from datetime import datetime +from typing import List, Dict, Any + +try: + from docx import Document +except ImportError: + print("错误: 请先安装 python-docx") + print("运行: pip install python-docx") + raise + + +class DocxReader: + """Word文档读取器""" + + # 目录类别映射 + CATEGORY_MAP = { + '0知识图谱目录': {'id': 0, 'type': 'Index', 'name': '知识图谱目录'}, + '1法律': {'id': 1, 'type': 'Law', 'name': '法律'}, + '2行政法规': {'id': 2, 'type': 'AdministrativeRegulation', 'name': '行政法规'}, + '3部门规章': {'id': 3, 'type': 'DepartmentalRule', 'name': '部门规章'}, + '4党中央国务院文件': {'id': 4, 'type': 'PolicyDocument', 'name': '党中央国务院文件'}, + '5国家主管部门文件': {'id': 5, 'type': 'PolicyDocument', 'name': '国家主管部门文件'}, + '6主要技术标准': {'id': 6, 'type': 'PolicyDocument', 'name': '主要技术标准'}, + } + + def __init__(self, source_dir: str): + self.source_dir = Path(source_dir) + self.logger = self._setup_logger() + self.documents = [] + + def _setup_logger(self): + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + return logging.getLogger(__name__) + + def _extract_text_from_docx(self, filepath: Path) -> str: + """从Word文档提取纯文本""" + doc = Document(str(filepath)) + paragraphs = [] + for para in doc.paragraphs: + text = para.text.strip() + if text: + paragraphs.append(text) + + # 也提取表格中的文本 + for table in doc.tables: + for row in table.rows: + row_text = [] + for cell in row.cells: + cell_text = cell.text.strip() + if cell_text: + row_text.append(cell_text) + if row_text: + paragraphs.append(' | '.join(row_text)) + + return '\n'.join(paragraphs) + + def _parse_filename_info(self, filename: str) -> Dict[str, Any]: + """从文件名解析基本信息""" + info = { + 'original_filename': filename, + 'document_number': None, + 'document_name': None, + 'is_draft': False, + } + + # 去掉扩展名 + name_without_ext = Path(filename).stem + + # 检测是否为草案/征求意见稿 + if '草案' in name_without_ext or '征求意见稿' in name_without_ext: + info['is_draft'] = True + + # 尝试提取编号前缀 (如 "3-中华人民共和国城乡规划法") + match = re.match(r'^(\d+)[-—]\s*(.+)$', name_without_ext) + if match: + info['file_number'] = int(match.group(1)) + name_part = match.group(2) + else: + name_part = name_without_ext + + # 提取书名号中的名称 + title_match = re.search(r'《(.+?)》', name_part) + if title_match: + info['document_name'] = title_match.group(1) + else: + # 去掉常见前缀 + cleaned = re.sub(r'^(中华人民共和国|国务院|国土资源部|建设部|住房城乡建设部|自然资源部)\s*', '', name_part) + info['document_name'] = cleaned if cleaned else name_part + + return info + + def read_all_documents(self) -> List[Dict[str, Any]]: + """读取所有Word文档""" + self.logger.info(f"开始读取文档目录: {self.source_dir}") + self.documents = [] + errors = [] + + for subdir_name, category_info in self.CATEGORY_MAP.items(): + subdir = self.source_dir / subdir_name + if not subdir.exists(): + self.logger.warning(f"子目录不存在: {subdir}") + continue + + docx_files = sorted(subdir.glob('*.docx')) + self.logger.info(f" {subdir_name}: 发现 {len(docx_files)} 个文件") + + for docx_file in docx_files: + try: + text = self._extract_text_from_docx(docx_file) + filename_info = self._parse_filename_info(docx_file.name) + + doc_record = { + 'doc_id': f"{category_info['type'][:3].upper()}-{len(self.documents):04d}", + 'filename': docx_file.name, + 'category_id': category_info['id'], + 'category_name': category_info['name'], + 'document_type': category_info['type'], + 'subdirectory': subdir_name, + 'raw_text': text, + 'text_length': len(text), + 'conversion_timestamp': datetime.now().isoformat(), + **filename_info, + } + + self.documents.append(doc_record) + + except Exception as e: + error_msg = f"读取失败: {docx_file.name} - {str(e)}" + self.logger.error(error_msg) + errors.append({ + 'filename': docx_file.name, + 'subdirectory': subdir_name, + 'error': str(e) + }) + + self.logger.info(f"读取完成: {len(self.documents)} 个文档, {len(errors)} 个错误") + return self.documents + + def save_to_json(self, output_path: str): + """保存到JSON文件""" + output = Path(output_path) + output.parent.mkdir(parents=True, exist_ok=True) + + result = { + 'metadata': { + 'total_documents': len(self.documents), + 'source_dir': str(self.source_dir), + 'created_at': datetime.now().isoformat(), + 'category_distribution': {}, + }, + 'documents': self.documents, + } + + # 统计分类分布 + for doc in self.documents: + cat = doc['category_name'] + result['metadata']['category_distribution'][cat] = \ + result['metadata']['category_distribution'].get(cat, 0) + 1 + + with open(output, 'w', encoding='utf-8') as f: + json.dump(result, f, indent=2, ensure_ascii=False) + + self.logger.info(f"已保存到: {output}") + return result + + def print_summary(self): + """打印摘要""" + print("\n" + "=" * 60) + print("文档读取摘要") + print("=" * 60) + print(f"总文档数: {len(self.documents)}") + + # 按类别统计 + category_counts = {} + total_chars = 0 + for doc in self.documents: + cat = doc['category_name'] + category_counts[cat] = category_counts.get(cat, 0) + 1 + total_chars += doc['text_length'] + + print("\n类别分布:") + for cat, count in sorted(category_counts.items()): + print(f" {cat}: {count} 个") + + print(f"\n总字符数: {total_chars:,}") + print(f"平均字符数: {total_chars // max(len(self.documents), 1):,}") + + # 文本长度分布 + lengths = [doc['text_length'] for doc in self.documents] + if lengths: + print(f"\n文本长度:") + print(f" 最短: {min(lengths):,} 字符") + print(f" 最长: {max(lengths):,} 字符") + print(f" 中位数: {sorted(lengths)[len(lengths)//2]:,} 字符") + + print("=" * 60) + + +def main(): + source_dir = r"E:\Project\SI\2026_KG_PlanningLaw\data\城市规划法律法规\城市更新法规数据库" + output_path = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\all_documents.json" + + reader = DocxReader(source_dir) + reader.read_all_documents() + reader.save_to_json(output_path) + reader.print_summary() + + +if __name__ == '__main__': + main() diff --git a/dofile/kg_project/scripts/extract_legal_csv.py b/dofile/kg_project/scripts/extract_legal_csv.py new file mode 100644 index 0000000..5c50c86 --- /dev/null +++ b/dofile/kg_project/scripts/extract_legal_csv.py @@ -0,0 +1,314 @@ +""" +从结构化法规数据生成知识图谱CSV文件(nodes.csv + rels.csv) +""" + +import json +import re +import pandas as pd +from pathlib import Path +from typing import Dict, List, Any, Tuple +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +# 节点类型到ID前缀的映射 +TYPE_PREFIX = { + 'Law': 'LAW', + 'AdministrativeRegulation': 'REG', + 'DepartmentalRule': 'RULE', + 'PolicyDocument': 'DOC', + 'Index': 'DOC', + 'Chapter': 'CH', + 'Section': 'SEC', + 'Article': 'ART', + 'GovernmentBody': 'GOV', +} + + +def generate_id(prefix: str, text: str) -> str: + """根据文本哈希生成ID""" + return f"{prefix}-{abs(hash(text)) % 100000:05d}" + + +def build_document_nodes(doc: Dict[str, Any], doc_index: int) -> Tuple[List[Dict], List[Dict]]: + """从单个文档构建节点和关系""" + nodes = [] + rels = [] + rel_id = 0 + + # 文档类型映射 + doc_type = doc['document_type'] + if doc_type == 'Index': + doc_type = 'PolicyDocument' + + # 1. 创建文档节点 + doc_id = generate_id(TYPE_PREFIX.get(doc_type, 'DOC'), doc['title']) + + doc_node = { + 'id': doc_id, + 'label': doc['title'], + 'type': doc_type, + 'properties': json.dumps({ + 'full_title': doc.get('document_name', ''), + 'promulgation_date': doc.get('promulgation_date'), + 'effective_date': doc.get('effective_date'), + 'document_number': doc.get('document_number'), + 'is_draft': doc.get('is_draft', False), + 'category': doc.get('category_name', ''), + 'filename': doc.get('filename', ''), + 'text_length': doc.get('text_length', 0), + 'article_count': doc.get('article_count', 0), + }, ensure_ascii=False) + } + nodes.append(doc_node) + + # 2. 创建发布机关节点和关系 + if doc.get('issuing_body'): + body_name = doc['issuing_body'] + body_id = generate_id('GOV', body_name) + + # 检查是否已存在 + body_node = { + 'id': body_id, + 'label': body_name, + 'type': 'GovernmentBody', + 'properties': json.dumps({}, ensure_ascii=False) + } + nodes.append(body_node) + + rel_id += 1 + rels.append({ + 'source': doc_id, + 'target': body_id, + 'type': 'ISSUED_BY', + 'properties': json.dumps({}, ensure_ascii=False) + }) + + # 3. 创建章节条文节点和层次关系 + for ch_idx, chapter in enumerate(doc.get('chapters', [])): + ch_title = chapter.get('title', f'第{ch_idx+1}章') + ch_id = generate_id('CH', f"{doc_id}_{ch_title}") + + ch_node = { + 'id': ch_id, + 'label': ch_title, + 'type': 'Chapter', + 'properties': json.dumps({ + 'name': chapter.get('name', ''), + 'number': str(ch_idx + 1), + }, ensure_ascii=False) + } + nodes.append(ch_node) + + # 文档→章 + rel_id += 1 + rels.append({ + 'source': doc_id, + 'target': ch_id, + 'type': 'HAS_CHAPTER', + 'properties': json.dumps({'order_number': ch_idx + 1}, ensure_ascii=False) + }) + + # 章下的条文 + for art_idx, article in enumerate(chapter.get('articles', [])): + art_number = article.get('number', str(art_idx + 1)) + art_text = article.get('text', '') + art_id = generate_id('ART', f"{doc_id}_{art_number}") + + art_node = { + 'id': art_id, + 'label': f"第{art_number}条", + 'type': 'Article', + 'properties': json.dumps({ + 'number': art_number, + 'text': art_text[:500], + 'article_type': article.get('article_type', ''), + }, ensure_ascii=False) + } + nodes.append(art_node) + + # 章→条 + rel_id += 1 + rels.append({ + 'source': ch_id, + 'target': art_id, + 'type': 'CONTAINS_ARTICLE', + 'properties': json.dumps({'order_number': art_idx + 1}, ensure_ascii=False) + }) + + # 条→文档 + rel_id += 1 + rels.append({ + 'source': art_id, + 'target': doc_id, + 'type': 'ARTICLE_IN_DOCUMENT', + 'properties': json.dumps({}, ensure_ascii=False) + }) + + # 节 + for sec_idx, section in enumerate(chapter.get('sections', [])): + sec_title = section.get('title', f'第{sec_idx+1}节') + sec_id = generate_id('SEC', f"{doc_id}_{ch_title}_{sec_title}") + + sec_node = { + 'id': sec_id, + 'label': sec_title, + 'type': 'Section', + 'properties': json.dumps({ + 'name': section.get('name', ''), + 'number': str(sec_idx + 1), + }, ensure_ascii=False) + } + nodes.append(sec_node) + + # 章→节 + rel_id += 1 + rels.append({ + 'source': ch_id, + 'target': sec_id, + 'type': 'HAS_SECTION', + 'properties': json.dumps({'order_number': sec_idx + 1}, ensure_ascii=False) + }) + + # 节下的条文 + for art_idx, article in enumerate(section.get('articles', [])): + art_number = article.get('number', str(art_idx + 1)) + art_text = article.get('text', '') + art_id = generate_id('ART', f"{doc_id}_{art_number}") + + art_node = { + 'id': art_id, + 'label': f"第{art_number}条", + 'type': 'Article', + 'properties': json.dumps({ + 'number': art_number, + 'text': art_text[:500], + 'article_type': article.get('article_type', ''), + }, ensure_ascii=False) + } + nodes.append(art_node) + + # 节→条 + rel_id += 1 + rels.append({ + 'source': sec_id, + 'target': art_id, + 'type': 'CONTAINS_ARTICLE', + 'properties': json.dumps({'order_number': art_idx + 1}, ensure_ascii=False) + }) + + # 条→文档 + rel_id += 1 + rels.append({ + 'source': art_id, + 'target': doc_id, + 'type': 'ARTICLE_IN_DOCUMENT', + 'properties': json.dumps({}, ensure_ascii=False) + }) + + return nodes, rels + + +def deduplicate_nodes(nodes: List[Dict]) -> List[Dict]: + """去重节点(基于id)""" + seen = {} + for node in nodes: + if node['id'] not in seen: + seen[node['id']] = node + else: + # 合并properties + existing = seen[node['id']] + if existing['properties'] == '{}' and node['properties'] != '{}': + existing['properties'] = node['properties'] + return list(seen.values()) + + +def generate_report(nodes_df: pd.DataFrame, rels_df: pd.DataFrame, output_dir: Path): + """生成统计报告""" + lines = ["# 结构化CSV提取报告\n"] + lines.append(f"生成时间: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n") + + # 节点统计 + lines.append("## 节点统计\n\n") + lines.append(f"**节点总数**: {len(nodes_df)}\n\n") + + node_type_counts = nodes_df['type'].value_counts().sort_index() + lines.append("| 节点类型 | 数量 | 占比 |\n") + lines.append("|---------|------|------|\n") + for nt, count in node_type_counts.items(): + lines.append(f"| {nt} | {count} | {count/len(nodes_df)*100:.1f}% |\n") + + # 关系统计 + lines.append("\n## 关系统计\n\n") + lines.append(f"**关系总数**: {len(rels_df)}\n\n") + + rel_type_counts = rels_df['type'].value_counts().sort_index() + lines.append("| 关系类型 | 数量 | 占比 |\n") + lines.append("|---------|------|------|\n") + for rt, count in rel_type_counts.items(): + lines.append(f"| {rt} | {count} | {count/len(rels_df)*100:.1f}% |\n") + + report_path = output_dir / 'extraction_report_structured.md' + with open(report_path, 'w', encoding='utf-8') as f: + f.writelines(lines) + + print(f"\n报告已保存: {report_path}") + + +def main(): + input_path = Path(r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\structured_documents.json") + output_dir = Path(r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output") + output_dir.mkdir(parents=True, exist_ok=True) + + logger.info(f"读取结构化文档: {input_path}") + with open(input_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + documents = data['documents'] + logger.info(f"共 {len(documents)} 个文档") + + all_nodes = [] + all_rels = [] + + for i, doc in enumerate(documents): + nodes, rels = build_document_nodes(doc, i) + all_nodes.extend(nodes) + all_rels.extend(rels) + + if (i + 1) % 50 == 0: + logger.info(f" 已处理 {i + 1}/{len(documents)}") + + # 去重 + all_nodes = deduplicate_nodes(all_nodes) + + # 保存 + nodes_df = pd.DataFrame(all_nodes) + rels_df = pd.DataFrame(all_rels) + + nodes_path = output_dir / 'nodes_structured.csv' + rels_path = output_dir / 'rels_structured.csv' + + nodes_df.to_csv(nodes_path, index=False, encoding='utf-8-sig') + rels_df.to_csv(rels_path, index=False, encoding='utf-8-sig') + + logger.info(f"节点已保存: {nodes_path} ({len(nodes_df)} 行)") + logger.info(f"关系已保存: {rels_path} ({len(rels_df)} 行)") + + # 统计 + print(f"\n{'='*60}") + print("结构化CSV提取完成") + print(f"{'='*60}") + print(f"节点总数: {len(nodes_df)}") + for nt, count in nodes_df['type'].value_counts().sort_index().items(): + print(f" - {nt}: {count}") + print(f"关系总数: {len(rels_df)}") + for rt, count in rels_df['type'].value_counts().sort_index().items(): + print(f" - {rt}: {count}") + print(f"{'='*60}") + + generate_report(nodes_df, rels_df, output_dir) + + +if __name__ == '__main__': + main() diff --git a/dofile/kg_project/scripts/legal_metadata_extractor.py b/dofile/kg_project/scripts/legal_metadata_extractor.py new file mode 100644 index 0000000..79eaf0a --- /dev/null +++ b/dofile/kg_project/scripts/legal_metadata_extractor.py @@ -0,0 +1,354 @@ +""" +法规元数据解析器 - 从文档文本中解析章节结构、发布机关、日期等 +""" + +import json +import re +import logging +from pathlib import Path +from datetime import datetime +from typing import Dict, List, Any, Optional + + +class LegalMetadataExtractor: + """法规元数据解析器""" + + def __init__(self): + self.logger = self._setup_logger() + + def _setup_logger(self): + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + return logging.getLogger(__name__) + + def _extract_document_title(self, text: str, filename: str) -> str: + """提取文档标题""" + # 尝试从书名号中提取 + match = re.search(r'《(.+?)》', text[:2000]) + if match: + return match.group(1) + + # 尝试从前几行中提取 + lines = text[:500].split('\n') + for line in lines: + line = line.strip() + if not line: + continue + # 过滤掉编号前缀 + cleaned = re.sub(r'^\d+[-—]\s*', '', line) + if len(cleaned) > 4 and len(cleaned) < 100: + return cleaned + + # 从文件名提取 + name = Path(filename).stem + name = re.sub(r'^\d+[-—]\s*', '', name) + return name + + def _extract_issuing_body(self, text: str) -> Optional[str]: + """提取发布机关""" + # 常见模式:在文末签名块 + patterns = [ + r'(?:发布|公布|印发)[^\n]*?[机关部院会委局厅处]\s*[::]\s*(.+?)(?:\n|$)', + r'^[\s]*(.{4,20}(?:部|委员会|院|局|厅|处|办公室|小组))\s*$', + ] + + # 从后往前搜索(发布机关通常在文末) + text_end = text[-3000:] if len(text) > 3000 else text + lines = text_end.split('\n') + + for line in reversed(lines): + line = line.strip() + if not line: + continue + # 匹配机关名称 + if re.match(r'^.{2,15}(?:部|委员会|院|局|厅|处|办公室|小组|政府|大会|人大常委会)$', line): + return line + + # 从标题区域搜索 + text_start = text[:1000] + body_patterns = [ + r'((?:全国人民代表大会(?:常务委员会)?|国务院|.{2,10}部|.{2,10}委员会|.{2,10}局|.{2,10}厅))\s*(?:令|公告|通知|制定)', + r'(.{2,10}(?:部|委员会|局|厅))\s*(?:令|公告|通知|印发)', + ] + for pattern in body_patterns: + match = re.search(pattern, text_start) + if match: + return match.group(1) + + return None + + def _extract_dates(self, text: str) -> Dict[str, Optional[str]]: + """提取日期信息""" + dates = {'promulgation_date': None, 'effective_date': None} + + # 搜索发布日期 + date_patterns = [ + r'(\d{4})\s*年\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日\s*(?:起?\s*)?(?:施行|实施|生效|执行)', + r'自\s*(\d{4})\s*年\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日\s*起?\s*(?:施行|实施|生效|执行)', + ] + for pattern in date_patterns: + match = re.search(pattern, text) + if match: + dates['effective_date'] = f"{match.group(1)}-{match.group(2).zfill(2)}-{match.group(3).zfill(2)}" + break + + # 搜索公布日期(通常在文末) + pub_patterns = [ + r'(\d{4})\s*年\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日\s*(?:公布|发布|印发)', + r'(?:公布|发布|印发)\s*[::]*\s*(\d{4})\s*年\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日', + ] + text_end = text[-2000:] if len(text) > 2000 else text + for pattern in pub_patterns: + match = re.search(pattern, text_end) + if match: + dates['promulgation_date'] = f"{match.group(1)}-{match.group(2).zfill(2)}-{match.group(3).zfill(2)}" + break + + # 如果没有明确日期,尝试从文末找任意日期 + if not dates['promulgation_date']: + match = re.findall(r'(\d{4})\s*年\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日', text_end) + if match: + last_date = match[-1] + dates['promulgation_date'] = f"{last_date[0]}-{last_date[1].zfill(2)}-{last_date[2].zfill(2)}" + + return dates + + def _extract_document_number(self, text: str) -> Optional[str]: + """提取文号""" + patterns = [ + r'[((]\s*(\d{4})\s*[))]\s*[^\s]*?\s*第?\s*(\d+)\s*号', + r'第?\s*(\d+)\s*号\s*[令公告通知]', + r'([A-Za-z一-鿿]+[〔(]\d{4}[))][^\s]*?号)', + r'([国发|国办发|建发|自然资发|国土资发|建城|建规|住建|建住房|建村|办发|发改委][〔(]\d{4}[))][^\s]*?号)', + ] + for pattern in patterns: + match = re.search(pattern, text[:2000]) + if match: + return match.group(0).strip() + return None + + def _parse_chapter_structure(self, text: str) -> List[Dict[str, Any]]: + """解析章节结构""" + chapters = [] + current_chapter = None + current_section = None + current_articles = [] + + # 按行处理 + lines = text.split('\n') + + def flush_articles(): + nonlocal current_articles + result = current_articles + current_articles = [] + return result + + for line in lines: + line_stripped = line.strip() + if not line_stripped: + continue + + # 检测章标题 + ch_match = re.match(r'^第[一二三四五六七八九十百]+[章节部分]\s*(.*)$', line_stripped) + if ch_match: + # 先保存之前的章/节的文章 + if current_section: + current_section['articles'] = flush_articles() + elif current_chapter: + current_chapter['articles'].extend(flush_articles()) + + current_chapter = { + 'title': line_stripped, + 'name': ch_match.group(1).strip() if ch_match.group(1) else line_stripped, + 'sections': [], + 'articles': [], + } + chapters.append(current_chapter) + current_section = None + continue + + # 检测节标题 + sec_match = re.match(r'^第[一二三四五六七八九十百]+节\s*(.*)$', line_stripped) + if sec_match and current_chapter: + if current_section: + current_section['articles'] = flush_articles() + + current_section = { + 'title': line_stripped, + 'name': sec_match.group(1).strip() if sec_match.group(1) else line_stripped, + 'articles': [], + } + current_chapter['sections'].append(current_section) + continue + + # 检测条文 + art_match = re.match(r'^第[一二三四五六七八九十百零千]+条\s*(.*)$', line_stripped) + if art_match: + article = { + 'number': line_stripped.split('条')[0].replace('第', ''), + 'text': art_match.group(1).strip(), + 'full_text': line_stripped, + } + current_articles.append(article) + continue + + # 续接上一条 + if current_articles and line_stripped: + current_articles[-1]['text'] += ' ' + line_stripped + current_articles[-1]['full_text'] += ' ' + line_stripped + + # 刷新最后的文章 + if current_section: + current_section['articles'].extend(flush_articles()) + elif current_chapter: + current_chapter['articles'].extend(flush_articles()) + + return chapters + + def _classify_article_type(self, text: str) -> str: + """分类条文类型""" + if re.search(r'不得|禁止|严禁', text): + return '禁止性' + elif re.search(r'可以|有权|依法享有', text): + return '授权性' + elif re.search(r'应当|必须|须|应当依法', text): + return '管理性' + elif re.search(r'申请|审批|备案|登记|许可|核准', text): + return '程序性' + elif re.search(r'罚款|责令|没收|吊销|刑事|处分', text): + return '处罚性' + else: + return '定义性' + + def process_document(self, doc: Dict[str, Any]) -> Dict[str, Any]: + """处理单个文档""" + text = doc.get('raw_text', '') + filename = doc.get('filename', '') + + # 提取元数据 + title = self._extract_document_title(text, filename) + issuing_body = self._extract_issuing_body(text) + dates = self._extract_dates(text) + doc_number = self._extract_document_number(text) + + # 解析章节结构 + chapters = self._parse_chapter_structure(text) + + # 统计条文数 + total_articles = 0 + for ch in chapters: + total_articles += len(ch.get('articles', [])) + for sec in ch.get('sections', []): + total_articles += len(sec.get('articles', [])) + + # 分类条文 + for ch in chapters: + for art in ch.get('articles', []): + art['article_type'] = self._classify_article_type(art['text']) + for sec in ch.get('sections', []): + for art in sec.get('articles', []): + art['article_type'] = self._classify_article_type(art['text']) + + result = { + 'doc_id': doc['doc_id'], + 'document_type': doc['document_type'], + 'category_name': doc['category_name'], + 'title': title, + 'document_name': doc.get('document_name', title), + 'issuing_body': issuing_body, + 'promulgation_date': dates['promulgation_date'], + 'effective_date': dates['effective_date'], + 'document_number': doc_number, + 'is_draft': doc.get('is_draft', False), + 'text_length': doc['text_length'], + 'filename': filename, + 'chapter_count': len(chapters), + 'article_count': total_articles, + 'chapters': chapters, + } + + return result + + def process_all(self, input_path: str, output_path: str) -> List[Dict[str, Any]]: + """处理所有文档""" + self.logger.info(f"读取文档数据: {input_path}") + + with open(input_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + documents = data['documents'] + self.logger.info(f"共 {len(documents)} 个文档待处理") + + results = [] + for i, doc in enumerate(documents): + if doc['text_length'] == 0: + self.logger.warning(f"跳过空文档: {doc['filename']}") + continue + + try: + result = self.process_document(doc) + results.append(result) + + if (i + 1) % 50 == 0: + self.logger.info(f" 已处理 {i + 1}/{len(documents)}") + except Exception as e: + self.logger.error(f"处理失败: {doc['filename']} - {str(e)}") + + # 保存 + output = Path(output_path) + output.parent.mkdir(parents=True, exist_ok=True) + + save_data = { + 'metadata': { + 'total_documents': len(results), + 'processed_at': datetime.now().isoformat(), + }, + 'documents': results, + } + + with open(output, 'w', encoding='utf-8') as f: + json.dump(save_data, f, indent=2, ensure_ascii=False) + + self.logger.info(f"已保存到: {output}") + self._print_summary(results) + + return results + + def _print_summary(self, results: List[Dict]): + """打印摘要""" + total_articles = sum(r['article_count'] for r in results) + total_chapters = sum(r['chapter_count'] for r in results) + docs_with_body = sum(1 for r in results if r['issuing_body']) + + print(f"\n{'='*60}") + print("法规元数据解析摘要") + print(f"{'='*60}") + print(f"处理文档数: {len(results)}") + print(f"解析出章数: {total_chapters}") + print(f"解析出条文数: {total_articles}") + print(f"提取发布机关: {docs_with_body}/{len(results)} ({docs_with_body/max(len(results),1)*100:.1f}%)") + + # 按类型统计 + type_counts = {} + for r in results: + t = r['document_type'] + type_counts[t] = type_counts.get(t, 0) + 1 + print("\n按类型统计:") + for t, c in sorted(type_counts.items()): + print(f" {t}: {c} 个") + + print(f"{'='*60}") + + +def main(): + input_path = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\all_documents.json" + output_path = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\structured_documents.json" + + extractor = LegalMetadataExtractor() + extractor.process_all(input_path, output_path) + + +if __name__ == '__main__': + main() diff --git a/dofile/kg_project/scripts/visualize_legal_kg.py b/dofile/kg_project/scripts/visualize_legal_kg.py new file mode 100644 index 0000000..4abf534 --- /dev/null +++ b/dofile/kg_project/scripts/visualize_legal_kg.py @@ -0,0 +1,239 @@ +""" +法规知识图谱可视化工具 +使用networkx和matplotlib绘制知识图谱 +""" + +import pandas as pd +import networkx as nx +import matplotlib.pyplot as plt +import matplotlib.patches as mpatches +from pathlib import Path +import numpy as np +import json + + +def setup_chinese_font(): + """设置中文字体""" + for font in ['Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi']: + try: + plt.rcParams['font.sans-serif'] = [font] + plt.rcParams['axes.unicode_minus'] = False + return + except: + continue + + +# 节点类型颜色 +NODE_COLORS = { + 'Law': '#E74C3C', + 'AdministrativeRegulation': '#E67E22', + 'DepartmentalRule': '#F1C40F', + 'PolicyDocument': '#2ECC71', + 'Chapter': '#3498DB', + 'Section': '#9B59B6', + 'Article': '#1ABC9C', + 'GovernmentBody': '#8E44AD', + 'LegalSubject': '#E91E63', + 'SpatialConcept': '#00BCD4', + 'AdministrativeProcedure': '#FF9800', + 'Obligation': '#F44336', + 'Penalty': '#795548', + 'TimePoint': '#607D8B', + 'Region': '#4CAF50', +} + +# 节点类型大小 +NODE_SIZES = { + 'Law': 600, + 'AdministrativeRegulation': 500, + 'DepartmentalRule': 400, + 'PolicyDocument': 350, + 'Chapter': 250, + 'Section': 200, + 'Article': 100, + 'GovernmentBody': 400, + 'LegalSubject': 300, + 'SpatialConcept': 250, + 'AdministrativeProcedure': 200, + 'Obligation': 150, + 'Penalty': 150, + 'TimePoint': 100, + 'Region': 150, +} + + +def load_data(nodes_csv, rels_csv): + """加载数据""" + nodes_df = pd.read_csv(nodes_csv, encoding='utf-8-sig') + rels_df = pd.read_csv(rels_csv, encoding='utf-8-sig') + print(f"加载节点: {len(nodes_df)}, 关系: {len(rels_df)}") + return nodes_df, rels_df + + +def build_graph(nodes_df, rels_df): + """构建NetworkX图""" + G = nx.DiGraph() + + for _, row in nodes_df.iterrows(): + node_id = row['id'] + label = str(row['label']) + node_type = row['type'] + + display_label = label[:12] + '...' if len(label) > 12 else label + + G.add_node(node_id, + label=display_label, + full_label=label, + node_type=node_type, + color=NODE_COLORS.get(node_type, '#CCCCCC'), + size=NODE_SIZES.get(node_type, 150)) + + for _, row in rels_df.iterrows(): + source = row['source'] + target = row['target'] + rel_type = row['type'] + if source in G.nodes() and target in G.nodes(): + G.add_edge(source, target, rel_type=rel_type) + + print(f"图: {G.number_of_nodes()} 节点, {G.number_of_edges()} 边") + return G + + +def draw_graph(G, output_path, title="法规知识图谱", max_nodes=300): + """绘制图谱""" + if G.number_of_nodes() > max_nodes: + degrees = dict(G.degree()) + top_nodes = sorted(degrees, key=degrees.get, reverse=True)[:max_nodes] + G = G.subgraph(top_nodes).copy() + + plt.figure(figsize=(24, 18)) + pos = nx.spring_layout(G, k=2.5, iterations=50, seed=42) + + # 边 + nx.draw_networkx_edges(G, pos, alpha=0.2, width=0.5, edge_color='gray', + arrows=True, arrowsize=8) + + # 按类型绘制节点 + node_types = {} + for node, data in G.nodes(data=True): + nt = data.get('node_type', 'Unknown') + node_types.setdefault(nt, []).append(node) + + for nt, nodes in node_types.items(): + color = NODE_COLORS.get(nt, '#CCCCCC') + size = NODE_SIZES.get(nt, 150) + nx.draw_networkx_nodes(G, pos, nodelist=nodes, node_color=color, + node_size=size, alpha=0.8, edgecolors='white', linewidths=1) + + # 标签(只显示非Article类型) + labels = {n: d['label'] for n, d in G.nodes(data=True) + if d.get('node_type') != 'Article'} + if len(labels) <= 150: + nx.draw_networkx_labels(G, pos, labels=labels, font_size=7, font_weight='bold') + + # 图例 + patches = [mpatches.Patch(color=NODE_COLORS[nt], label=nt) + for nt in node_types if nt in NODE_COLORS] + plt.legend(handles=patches, loc='upper right', fontsize=10, framealpha=0.9) + + plt.title(title, fontsize=16, fontweight='bold', pad=20) + plt.axis('off') + plt.tight_layout() + plt.savefig(output_path, dpi=150, bbox_inches='tight') + plt.close() + print(f"保存: {output_path}") + + +def draw_subgraphs(G, output_dir): + """绘制子图""" + output_dir = Path(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + # 1. 文档层次图 + doc_types = ['Law', 'AdministrativeRegulation', 'DepartmentalRule', 'PolicyDocument', 'Chapter'] + G1_nodes = [n for n, d in G.nodes(data=True) if d.get('node_type') in doc_types] + if G1_nodes: + G1 = G.subgraph(G1_nodes).copy() + draw_graph(G1, output_dir / 'kg_document_hierarchy.png', '法规文档层次结构') + + # 2. 引用网络 + ref_rels = ['CITES', 'IMPLEMENTS', 'AMENDS', 'REPLACES', 'SUPPLEMENTS'] + ref_edges = [(u, v) for u, v, d in G.edges(data=True) if d.get('rel_type') in ref_rels] + if ref_edges: + G2 = G.edge_subgraph(ref_edges).copy() + draw_graph(G2, output_dir / 'kg_citation_network.png', '法规引用网络') + + # 3. 主题规制图 + subject_types = ['LegalSubject', 'SpatialConcept'] + subject_nodes = [n for n, d in G.nodes(data=True) if d.get('node_type') in subject_types] + if subject_nodes: + neighbors = set(subject_nodes) + for node in subject_nodes: + neighbors.update(G.predecessors(node)) + neighbors.update(G.successors(node)) + G3 = G.subgraph(neighbors).copy() + draw_graph(G3, output_dir / 'kg_subject_regulation.png', '法规主题规制关系') + + # 4. 机构关系图 + gov_nodes = [n for n, d in G.nodes(data=True) if d.get('node_type') == 'GovernmentBody'] + if gov_nodes: + neighbors = set(gov_nodes) + for node in gov_nodes: + neighbors.update(G.predecessors(node)) + neighbors.update(G.successors(node)) + G4 = G.subgraph(neighbors).copy() + draw_graph(G4, output_dir / 'kg_government_body.png', '政府机构关系') + + +def print_statistics(G): + """打印统计""" + print(f"\n{'='*60}") + print("图谱统计") + print(f"{'='*60}") + print(f"节点: {G.number_of_nodes()}, 边: {G.number_of_edges()}") + + type_counts = {} + for _, d in G.nodes(data=True): + nt = d.get('node_type', 'Unknown') + type_counts[nt] = type_counts.get(nt, 0) + 1 + + print("\n节点类型:") + for nt, c in sorted(type_counts.items()): + print(f" {nt}: {c} ({c/G.number_of_nodes()*100:.1f}%)") + + rel_counts = {} + for _, _, d in G.edges(data=True): + rt = d.get('rel_type', 'Unknown') + rel_counts[rt] = rel_counts.get(rt, 0) + 1 + + print("\n关系类型:") + for rt, c in sorted(rel_counts.items()): + print(f" {rt}: {c}") + + degrees = [d for _, d in G.degree()] + print(f"\n平均连接度: {np.mean(degrees):.2f}") + top = sorted(G.degree(), key=lambda x: x[1], reverse=True)[:10] + print("连接度最高:") + for node, deg in top: + data = G.nodes[node] + print(f" [{data.get('node_type')}] {data.get('full_label', node)}: {deg}") + print(f"{'='*60}") + + +def main(): + setup_chinese_font() + + base = Path(r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output") + + # 优先使用合并后的文件 + nodes_csv = base / 'nodes_merged.csv' if (base / 'nodes_merged.csv').exists() else base / 'nodes_structured.csv' + rels_csv = base / 'rels_merged.csv' if (base / 'rels_merged.csv').exists() else base / 'rels_structured.csv' + + nodes_df, rels_df = load_data(nodes_csv, rels_csv) + G = build_graph(nodes_df, rels_df) + print_statistics(G) + draw_subgraphs(G, base / 'visualizations') + + +if __name__ == '__main__': + main() diff --git a/dofile/kg_project/src/__init__.py b/dofile/kg_project/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dofile/kg_project/src/data_processing/__init__.py b/dofile/kg_project/src/data_processing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dofile/kg_project/src/data_processing/entity_normalizer.py b/dofile/kg_project/src/data_processing/entity_normalizer.py new file mode 100644 index 0000000..84e93e3 --- /dev/null +++ b/dofile/kg_project/src/data_processing/entity_normalizer.py @@ -0,0 +1,176 @@ +""" +实体规范化器 - 去重、ID生成和规范化 +""" + +import json +import hashlib +import logging +from typing import Dict, List, Any, Optional +from pathlib import Path + +try: + import yaml +except ImportError: + yaml = None + +logger = logging.getLogger(__name__) + + +class LegalEntityNormalizer: + """法规实体规范化器""" + + # 法律领域常见同义词映射 + SYNONYM_MAP = { + '城乡规划': '城市规划', + '住建部': '住房和城乡建设部', + '建设部': '住房和城乡建设部', + '自然资源部': '自然资源部', + '国土部': '自然资源部', + '国土资源部': '自然资源部', + '环保部': '生态环境部', + '环境保护部': '生态环境部', + '国家发改委': '国家发展和改革委员会', + '发改委': '国家发展和改革委员会', + '国务院': '国务院', + # 第一轮旧名称 → 第二轮新名称 + 'GovernmentBody': 'Agency', + 'LegalSubject': 'LegalObject', + 'SpatialConcept': 'SpatialObject', + 'AdministrativeProcedure': 'Procedure', + # 城市更新领域同义词 + '老旧小区': '老旧小区', + '城中村': '城中村', + '棚户区': '棚户区', + '旧城改造': '城市更新', + '旧区改造': '城市更新', + '三旧改造': '城市更新', + } + + def __init__(self, ontology_file: str = None): + self.entity_registry = {} + self.text_to_id_map = {} + self.type_counters = {} + + if ontology_file: + self._load_ontology(ontology_file) + + logger.info("LegalEntityNormalizer 初始化完成") + + def _load_ontology(self, ontology_file: str): + if yaml is None: + return + path = Path(ontology_file) + if path.exists(): + with open(path, 'r', encoding='utf-8') as f: + self.ontology = yaml.safe_load(f) + + def normalize_text(self, text: str) -> str: + """标准化文本""" + if not text: + return "" + text = text.strip() + text = text.replace(' ', ' ').replace('\xa0', ' ') + return text + + def generate_entity_id(self, entity_type: str, entity_text: str) -> str: + """生成实体ID""" + type_prefixes = { + # 第二轮本体:城市更新法规政策工具箱 + 'Agency': 'AGENCY', + 'LegalObject': 'OBJ', + 'SpatialObject': 'SPAT', + 'RenewalScene': 'SCENE', + 'PolicyTool': 'TOOL', + 'ToolCategory': 'TCAT', + 'Procedure': 'PROC', + 'Obligation': 'OBL', + 'Condition': 'COND', + 'Constraint': 'CONST', + 'Penalty': 'PEN', + 'TimePoint': 'TIME', + 'Region': 'REGION', + # 兼容第一轮旧名称 + 'GovernmentBody': 'AGENCY', + 'LegalSubject': 'OBJ', + 'SpatialConcept': 'SPAT', + 'AdministrativeProcedure': 'PROC', + } + prefix = type_prefixes.get(entity_type, 'ENT') + + if entity_type not in self.type_counters: + self.type_counters[entity_type] = 0 + self.type_counters[entity_type] += 1 + + text_hash = abs(hash(entity_text)) % 100000 + counter = self.type_counters[entity_type] + return f"{prefix}-{text_hash:05d}-{counter}" + + def normalize_entity(self, raw_entity: Dict[str, Any]) -> Optional[str]: + """规范化单个实体""" + entity_text = raw_entity.get('text', '') + entity_type = raw_entity.get('type', '') + attributes = raw_entity.get('attributes', {}) + + if not entity_text or not entity_type: + return None + + # 标准化文本 + normalized_text = self.normalize_text(entity_text) + + # 同义词映射 + if normalized_text in self.SYNONYM_MAP: + normalized_text = self.SYNONYM_MAP[normalized_text] + + # 检查是否已存在 + key = f"{entity_type}:{normalized_text}" + if key in self.text_to_id_map: + return self.text_to_id_map[key] + + # 生成新ID + entity_id = self.generate_entity_id(entity_type, normalized_text) + + # 注册 + self.entity_registry[entity_id] = { + 'canonical_name': normalized_text, + 'display_name': entity_text, + 'type': entity_type, + 'attributes': attributes, + } + self.text_to_id_map[key] = entity_id + + return entity_id + + def normalize_batch(self, extraction_results: List[Dict]) -> Dict[str, str]: + """批量规范化""" + for result in extraction_results: + if not result or 'entities' not in result: + continue + for entity in result.get('entities', []): + self.normalize_entity(entity) + + logger.info(f"规范化完成: {len(self.entity_registry)} 个唯一实体") + return self.text_to_id_map.copy() + + def get_entity_nodes(self) -> List[Dict[str, Any]]: + """获取所有实体节点""" + nodes = [] + for entity_id, entity_data in self.entity_registry.items(): + node = { + 'id': entity_id, + 'label': entity_data['display_name'], + 'type': entity_data['type'], + 'properties': json.dumps(entity_data['attributes'], ensure_ascii=False) + } + nodes.append(node) + return nodes + + def get_statistics(self) -> Dict[str, Any]: + """获取统计信息""" + stats = { + 'total_entities': len(self.entity_registry), + 'entities_by_type': {}, + } + for entity_data in self.entity_registry.values(): + et = entity_data['type'] + stats['entities_by_type'][et] = stats['entities_by_type'].get(et, 0) + 1 + return stats diff --git a/dofile/kg_project/src/data_processing/relationship_builder.py b/dofile/kg_project/src/data_processing/relationship_builder.py new file mode 100644 index 0000000..80a3df4 --- /dev/null +++ b/dofile/kg_project/src/data_processing/relationship_builder.py @@ -0,0 +1,189 @@ +""" +关系构建器 - 从LLM抽取结果构建关系 +""" + +import json +import logging +from typing import Dict, List, Any, Optional + +logger = logging.getLogger(__name__) + + +class RelationshipBuilder: + """关系构建器""" + + # LLM关系类型到大写关系类型的映射 + REL_TYPE_MAP = { + # 文档层次 + 'has_chapter': 'HAS_CHAPTER', + 'has_section': 'HAS_SECTION', + 'has_article': 'HAS_ARTICLE', + 'has_clause': 'HAS_CLAUSE', + 'article_in_document': 'ARTICLE_IN_DOCUMENT', + # 法条依据 + 'cites': 'CITES', + 'implements': 'IMPLEMENTS', + 'amends': 'AMENDS', + 'replaces': 'REPLACES', + 'supplements': 'SUPPLEMENTS', + 'basis_for_planning': 'BASIS_FOR_PLANNING', + # 主体关系 + 'issued_by': 'ISSUED_BY', + 'implemented_by': 'IMPLEMENTED_BY', + 'assigned_to': 'ASSIGNED_TO', + # 语义规制 + 'regulates': 'REGULATES', + 'defines_object': 'DEFINES_OBJECT', + 'applies_to': 'APPLIES_TO', + 'sets_obligation': 'SETS_OBLIGATION', + 'sets_condition': 'SETS_CONDITION', + 'sets_constraint': 'SETS_CONSTRAINT', + 'prescribes_penalty': 'PRESCRIBES_PENALTY', + # 政策工具 + 'extracts_tool': 'EXTRACTS_TOOL', + 'belongs_to_category': 'BELONGS_TO_CATEGORY', + 'applies_to_scene': 'APPLIES_TO_SCENE', + 'targets_object': 'TARGETS_OBJECT', + 'coordinates_with': 'COORDINATES_WITH', + 'supports_tool': 'SUPPORTS_TOOL', + # 程序 + 'requires_procedure': 'REQUIRES_PROCEDURE', + 'procedure_for': 'PROCEDURE_FOR', + 'precedes_procedure': 'PRECEDES_PROCEDURE', + # 辅助 + 'governs_region': 'GOVERNS_REGION', + 'effective_timeline': 'EFFECTIVE_TIMELINE', + 'standard_for': 'STANDARD_FOR', + 'article_effective_timeline': 'ARTICLE_EFFECTIVE_TIMELINE', + # 兼容第一轮旧名称 + 'defines_spatial': 'DEFINES_OBJECT', + } + + def __init__(self): + self.relationships = [] + + def build_from_extraction( + self, + extraction_results: List[Dict], + entity_id_map: Dict[str, str], + source_doc_id: str = None + ) -> List[Dict[str, Any]]: + """ + 从LLM抽取结果构建关系 + + Args: + extraction_results: LLM抽取结果列表 + entity_id_map: 文本到ID的映射 + source_doc_id: 源文档ID + """ + relationships = [] + rel_counter = 0 + + for result in extraction_results: + if not result or 'relationships' not in result: + continue + + # 建立实体文本到ID的快速查找 + local_entity_map = {} + if 'entities' in result: + for entity in result['entities']: + text = entity.get('text', '') + etype = entity.get('type', '') + key = f"{etype}:{text}" + if key in entity_id_map: + local_entity_map[text] = entity_id_map[key] + + for rel in result.get('relationships', []): + from_text = rel.get('from_entity', '') + to_text = rel.get('to_entity', '') + rel_type = rel.get('type', '') + context = rel.get('context', '') + confidence = rel.get('confidence', 0.8) + + # 查找实体ID + from_id = local_entity_map.get(from_text) + to_id = local_entity_map.get(to_text) + + if not from_id or not to_id: + continue + + rel_type_upper = self.REL_TYPE_MAP.get(rel_type, rel_type.upper()) + + rel_counter += 1 + relationships.append({ + 'source': from_id, + 'target': to_id, + 'type': rel_type_upper, + 'properties': json.dumps({ + 'context': context, + 'confidence': confidence, + 'source_doc': source_doc_id, + }, ensure_ascii=False) + }) + + self.relationships.extend(relationships) + logger.info(f"构建了 {len(relationships)} 个关系") + return relationships + + def build_reference_relationships( + self, + resolved_refs: List[Dict], + doc_id_map: Dict[str, str] + ) -> List[Dict[str, Any]]: + """ + 从引用解析结果构建引用关系 + + Args: + resolved_refs: CitationResolver解析的引用列表 + doc_id_map: 文档标题到ID的映射 + """ + relationships = [] + + for ref in resolved_refs: + source_title = ref.get('source_title', '') + target_id = ref.get('target_id', '') + rel_type = ref.get('relation_type', 'CITES') + context = ref.get('context', '') + + source_id = doc_id_map.get(source_title) + if not source_id: + continue + + relationships.append({ + 'source': source_id, + 'target': target_id, + 'type': rel_type, + 'properties': json.dumps({'context': context}, ensure_ascii=False) + }) + + logger.info(f"构建了 {len(relationships)} 个引用关系") + return relationships + + def validate_relationships( + self, + relationships: List[Dict], + valid_node_ids: set + ) -> Dict[str, Any]: + """验证关系数据""" + valid = 0 + invalid = 0 + broken_links = [] + + for rel in relationships: + source = rel.get('source', '') + target = rel.get('target', '') + + if source in valid_node_ids and target in valid_node_ids: + valid += 1 + else: + invalid += 1 + if source not in valid_node_ids: + broken_links.append(f"source not found: {source}") + if target not in valid_node_ids: + broken_links.append(f"target not found: {target}") + + return { + 'valid_relationships': valid, + 'invalid_relationships': invalid, + 'broken_links': broken_links[:50], + } diff --git a/dofile/kg_project/src/deep_extraction_pipeline.py b/dofile/kg_project/src/deep_extraction_pipeline.py new file mode 100644 index 0000000..254248b --- /dev/null +++ b/dofile/kg_project/src/deep_extraction_pipeline.py @@ -0,0 +1,335 @@ +""" +深度法规实体抽取管道 +协调LLM抽取、实体规范化、关系构建 +""" + +import asyncio +import json +import pandas as pd +import logging +from pathlib import Path +from datetime import datetime +from typing import Dict, List, Any + +import sys +sys.path.insert(0, str(Path(__file__).parent)) + +from knowledge_extraction.llm_legal_extractor import LegalLLMExtractor +from knowledge_extraction.citation_resolver import CitationResolver +from data_processing.entity_normalizer import LegalEntityNormalizer +from data_processing.relationship_builder import RelationshipBuilder + + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +class DeepExtractionPipeline: + """深度法规实体抽取管道""" + + def __init__(self, config_file: str): + self.config_file = config_file + self.extractor = LegalLLMExtractor(config_file) + self.config = self.extractor.config + self.normalizer = LegalEntityNormalizer() + self.builder = RelationshipBuilder() + self.project_root = Path(__file__).parent.parent + + def load_structured_documents(self, input_file: str) -> List[Dict]: + """加载结构化文档数据""" + with open(input_file, 'r', encoding='utf-8') as f: + data = json.load(f) + return data['documents'] + + def _prepare_sections(self, documents: List[Dict]) -> List[Dict]: + """准备章节级别的抽取单元""" + sections = [] + + for doc in documents: + doc_title = doc.get('title', '') + chapters = doc.get('chapters', []) + + for chapter in chapters: + # 将章节下所有条文合并为一个抽取单元 + texts = [] + for art in chapter.get('articles', []): + art_text = art.get('full_text', art.get('text', '')) + if art_text: + texts.append(art_text) + + for section in chapter.get('sections', []): + for art in section.get('articles', []): + art_text = art.get('full_text', art.get('text', '')) + if art_text: + texts.append(art_text) + + combined_text = '\n'.join(texts) + + if len(combined_text.strip()) > 20: + sections.append({ + 'text': combined_text, + 'document_title': doc_title, + 'section_title': chapter.get('title', ''), + 'doc_id': doc.get('doc_id', ''), + }) + + logger.info(f"准备了 {len(sections)} 个章节抽取单元") + return sections + + def _prepare_documents_for_references(self, documents: List[Dict]) -> List[Dict]: + """准备文档级引用抽取""" + doc_list = [] + for doc in documents: + doc_list.append({ + 'title': doc.get('title', ''), + 'text': doc.get('text_preview', ''), # 需要原始文本 + 'doc_id': doc.get('doc_id', ''), + }) + return doc_list + + async def run( + self, + structured_input: str, + raw_input: str, + output_dir: str, + max_sections: int = None, + ) -> Dict[str, Any]: + """ + 执行完整抽取流程 + + Args: + structured_input: 结构化文档JSON路径 + raw_input: 原始文档JSON路径(用于引用抽取) + output_dir: 输出目录 + max_sections: 最大处理章节数(测试用) + """ + start_time = datetime.now() + output_path = Path(output_dir) + output_path.mkdir(parents=True, exist_ok=True) + + # 1. 加载数据 + logger.info("加载结构化文档...") + documents = self.load_structured_documents(structured_input) + logger.info(f"加载 {len(documents)} 个文档") + + # 2. 准备章节抽取单元 + sections = self._prepare_sections(documents) + if max_sections: + sections = sections[:max_sections] + logger.info(f"限制处理: {max_sections} 个章节") + + # 3. 加载已有的结构化节点(用于引用解析) + nodes_csv = output_path / 'nodes_structured.csv' + known_docs = [] + if nodes_csv.exists(): + nodes_df = pd.read_csv(nodes_csv, encoding='utf-8-sig') + known_docs = [ + {'id': row['id'], 'label': row['label'], 'type': row['type']} + for _, row in nodes_df.iterrows() + if row['type'] in ['Law', 'AdministrativeRegulation', 'DepartmentalRule', 'PolicyDocument'] + ] + logger.info(f"已知文档节点: {len(known_docs)}") + + # 4. LLM章节级抽取 + logger.info("开始LLM章节级抽取...") + extraction_results = await self.extractor.batch_extract_sections( + sections, + concurrent=self.extractor.config.get('batch_processing', {}).get('concurrent_requests', 5) + ) + + # 5. 实体规范化 + logger.info("规范化实体...") + entity_id_map = self.normalizer.normalize_batch(extraction_results) + entity_nodes = self.normalizer.get_entity_nodes() + stats = self.normalizer.get_statistics() + logger.info(f"规范化后实体: {stats['total_entities']}") + + # 6. 关系构建 + logger.info("构建关系...") + relationships = self.builder.build_from_extraction( + extraction_results, entity_id_map + ) + + # 7. 文档级引用抽取 + logger.info("开始文档级引用抽取...") + # 加载原始文本用于引用抽取 + with open(raw_input, 'r', encoding='utf-8') as f: + raw_data = json.load(f) + + doc_id_map = {d['label']: d['id'] for d in known_docs} + # 补充从文档数据中获取原始文本 + docs_for_ref = [] + raw_docs_map = {d['filename']: d for d in raw_data['documents']} + + for doc in documents: + filename = doc.get('filename', '') + raw_doc = raw_docs_map.get(filename, {}) + raw_text = raw_doc.get('raw_text', '') + if raw_text and len(raw_text) > 50: + docs_for_ref.append({ + 'title': doc['title'], + 'text': raw_text[:5000], # 截取前5000字符 + }) + + ref_results = await self.extractor.batch_extract_references(docs_for_ref, concurrent=3) + + # 8. 引用解析 + logger.info("解析法规引用...") + resolver = CitationResolver(known_docs) + + all_references = [] + for result in ref_results: + if not result or 'references' not in result: + continue + doc_title = result.get('metadata', {}).get('document_title', '') + for ref in result['references']: + ref['document_title'] = doc_title + all_references.append(ref) + + resolved_refs, unresolved_refs = resolver.resolve_batch(all_references) + ref_relationships = self.builder.build_reference_relationships(resolved_refs, doc_id_map) + relationships.extend(ref_relationships) + + # 9. 保存结果 + logger.info("保存结果...") + + # 从配置获取输出文件名 + output_cfg = self.config.get('output', {}) + llm_nodes_name = output_cfg.get('nodes_file', 'output/nodes_llm_v2.csv').split('/')[-1] + llm_rels_name = output_cfg.get('relationships_file', 'output/rels_llm_v2.csv').split('/')[-1] + report_name = output_cfg.get('report_file', 'output/extraction_report_v2.md').split('/')[-1] + + # LLM节点 + llm_nodes_df = pd.DataFrame(entity_nodes) + llm_nodes_path = output_path / llm_nodes_name + llm_nodes_df.to_csv(llm_nodes_path, index=False, encoding='utf-8-sig') + + # LLM关系 + llm_rels_df = pd.DataFrame(relationships) + llm_rels_path = output_path / llm_rels_name + llm_rels_df.to_csv(llm_rels_path, index=False, encoding='utf-8-sig') + + # 10. 合并 + logger.info("合并结构化和LLM结果...") + if nodes_csv.exists(): + structured_nodes = pd.read_csv(nodes_csv, encoding='utf-8-sig') + merged_nodes = pd.concat([structured_nodes, llm_nodes_df], ignore_index=True) + merged_nodes = merged_nodes.drop_duplicates(subset=['id'], keep='first') + else: + merged_nodes = llm_nodes_df + + rels_csv = output_path / 'rels_structured.csv' + if rels_csv.exists(): + structured_rels = pd.read_csv(rels_csv, encoding='utf-8-sig') + merged_rels = pd.concat([structured_rels, llm_rels_df], ignore_index=True) + else: + merged_rels = llm_rels_df + + merged_nodes_path = output_path / 'nodes_merged_v2.csv' + merged_rels_path = output_path / 'rels_merged_v2.csv' + merged_nodes.to_csv(merged_nodes_path, index=False, encoding='utf-8-sig') + merged_rels.to_csv(merged_rels_path, index=False, encoding='utf-8-sig') + + # 验证 + valid_ids = set(merged_nodes['id'].tolist()) + validation = self.builder.validate_relationships( + merged_rels.to_dict('records'), valid_ids + ) + + # 生成报告 + duration = (datetime.now() - start_time).total_seconds() + self._generate_report( + output_path, sections, extraction_results, entity_nodes, + relationships, resolved_refs, unresolved_refs, validation, duration, + report_name=report_name + ) + + result = { + 'total_sections': len(sections), + 'total_entities': len(entity_nodes), + 'total_relationships': len(relationships), + 'total_references_resolved': len(resolved_refs), + 'total_references_unresolved': len(unresolved_refs), + 'duration_seconds': duration, + } + + logger.info(f"\n抽取完成!耗时: {duration:.1f}s") + logger.info(f"章节: {result['total_sections']}, 实体: {result['total_entities']}, " + f"关系: {result['total_relationships']}, 引用: {result['total_references_resolved']}") + + return result + + def _generate_report(self, output_path, sections, extraction_results, + entity_nodes, relationships, resolved_refs, + unresolved_refs, validation, duration, + report_name='extraction_report_v2.md'): + """生成抽取报告""" + lines = ["# 第二轮深度抽取报告(城市更新政策工具箱本体)\n"] + lines.append(f"**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") + lines.append(f"**耗时**: {duration:.1f}秒\n\n") + + lines.append("## 统计\n\n") + lines.append(f"- 处理章节: {len(sections)}\n") + lines.append(f"- 抽取实体: {len(entity_nodes)}\n") + lines.append(f"- 构建关系: {len(relationships)}\n") + lines.append(f"- 解析引用: {len(resolved_refs)} 成功, {len(unresolved_refs)} 未匹配\n") + + # 实体类型分布 + lines.append("\n## 实体类型分布\n\n") + type_counts = {} + for node in entity_nodes: + t = node['type'] + type_counts[t] = type_counts.get(t, 0) + 1 + lines.append("| 类型 | 数量 |\n|------|------|\n") + for t, c in sorted(type_counts.items()): + lines.append(f"| {t} | {c} |\n") + + # 关系类型分布 + lines.append("\n## 关系类型分布\n\n") + rel_type_counts = {} + for rel in relationships: + t = rel['type'] + rel_type_counts[t] = rel_type_counts.get(t, 0) + 1 + lines.append("| 类型 | 数量 |\n|------|------|\n") + for t, c in sorted(rel_type_counts.items()): + lines.append(f"| {t} | {c} |\n") + + # 数据质量 + lines.append("\n## 数据质量\n\n") + lines.append(f"- 有效关系: {validation['valid_relationships']}\n") + lines.append(f"- 无效关系: {validation['invalid_relationships']}\n") + + report_path = output_path / report_name + with open(report_path, 'w', encoding='utf-8') as f: + f.writelines(lines) + logger.info(f"报告已保存: {report_path}") + + +async def main(): + config_file = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\config\deep_extraction_config.yaml" + structured_input = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\structured_documents.json" + raw_input = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\all_documents.json" + output_dir = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output" + + pipeline = DeepExtractionPipeline(config_file) + + # 测试模式:只处理前20个章节 + import sys + max_sections = 20 + if '--full' in sys.argv: + max_sections = None + + results = await pipeline.run( + structured_input, raw_input, output_dir, + max_sections=max_sections + ) + + print("\n结果:") + print(json.dumps(results, indent=2, ensure_ascii=False)) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/dofile/kg_project/src/knowledge_extraction/__init__.py b/dofile/kg_project/src/knowledge_extraction/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dofile/kg_project/src/knowledge_extraction/citation_resolver.py b/dofile/kg_project/src/knowledge_extraction/citation_resolver.py new file mode 100644 index 0000000..74738e9 --- /dev/null +++ b/dofile/kg_project/src/knowledge_extraction/citation_resolver.py @@ -0,0 +1,134 @@ +""" +法规引用解析器 - 将文本中的法规引用映射到已知文档节点 +""" + +import re +import json +import logging +from pathlib import Path +from typing import Dict, List, Optional, Tuple +from difflib import SequenceMatcher + +logger = logging.getLogger(__name__) + + +class CitationResolver: + """法规引用解析器""" + + def __init__(self, known_documents: List[Dict]): + """ + Args: + known_documents: 已知文档列表,每个包含 id, label, type + """ + self.known_docs = {} + self.name_variants = {} + + for doc in known_documents: + name = doc['label'] + doc_id = doc['id'] + self.known_docs[name] = doc_id + + # 生成名称变体 + variants = self._generate_variants(name) + for variant in variants: + self.name_variants[variant] = doc_id + + logger.info(f"CitationResolver 初始化: {len(self.known_docs)} 个已知文档") + + def _generate_variants(self, name: str) -> List[str]: + """生成法规名称变体""" + variants = [name] + + # 去掉书名号 + cleaned = name.replace('《', '').replace('》', '') + if cleaned != name: + variants.append(cleaned) + + # 去掉"中华人民共和国"前缀 + if name.startswith('中华人民共和国'): + short = name.replace('中华人民共和国', '') + variants.append(short) + + # 简称 + if '实施条例' in name: + variants.append(name.replace('实施条例', '条例')) + if '实施办法' in name: + variants.append(name.replace('实施办法', '办法')) + + return list(set(variants)) + + def resolve(self, referenced_name: str, threshold: float = 0.7) -> Optional[str]: + """ + 解析引用名称到文档ID + + Args: + referenced_name: 被引用的法规名称 + threshold: 相似度阈值 + + Returns: + 文档ID,未找到返回None + """ + if not referenced_name: + return None + + # 清理名称 + cleaned = referenced_name.strip() + cleaned = re.sub(r'[《》]', '', cleaned) + + # 精确匹配 + if cleaned in self.known_docs: + return self.known_docs[cleaned] + if cleaned in self.name_variants: + return self.name_variants[cleaned] + + # 模糊匹配 + best_match = None + best_score = 0 + + for known_name, doc_id in self.known_docs.items(): + score = SequenceMatcher(None, cleaned, known_name).ratio() + if score > best_score and score >= threshold: + best_score = score + best_match = doc_id + + if best_match: + logger.info(f"模糊匹配: '{referenced_name}' -> {best_match} (score={best_score:.2f})") + + return best_match + + def resolve_batch(self, references: List[Dict]) -> List[Tuple[str, str]]: + """ + 批量解析引用 + + Returns: + List of (source_doc_title, target_doc_id) pairs + """ + resolved = [] + unresolved = [] + + for ref in references: + doc_title = ref.get('document_title', '') + referenced_law = ref.get('referenced_law', '') + relation_type = ref.get('relation_type', 'cites') + context = ref.get('context', '') + + target_id = self.resolve(referenced_law) + + if target_id: + resolved.append({ + 'source_title': doc_title, + 'target_id': target_id, + 'target_name': referenced_law, + 'relation_type': relation_type.upper(), + 'context': context, + }) + else: + unresolved.append({ + 'source_title': doc_title, + 'target_name': referenced_law, + 'relation_type': relation_type, + }) + + logger.info(f"引用解析完成: {len(resolved)} 成功, {len(unresolved)} 未匹配") + + return resolved, unresolved diff --git a/dofile/kg_project/src/knowledge_extraction/llm_legal_extractor.py b/dofile/kg_project/src/knowledge_extraction/llm_legal_extractor.py new file mode 100644 index 0000000..64c7e3c --- /dev/null +++ b/dofile/kg_project/src/knowledge_extraction/llm_legal_extractor.py @@ -0,0 +1,303 @@ +""" +基于DeepSeek的法规知识抽取器 +从法条文本中抽取法律实体和关系 +使用OpenAI SDK直接调用DeepSeek API +""" + +import os +# 解决Windows上httpx代理检测导致的连接失败 +os.environ['NO_PROXY'] = '*' + +import yaml +import asyncio +import json +import logging +from pathlib import Path +from datetime import datetime +from typing import Dict, List, Optional, Any + +try: + from openai import OpenAI +except ImportError: + raise ImportError("请安装: pip install openai") + + +class LegalLLMExtractor: + """基于DeepSeek的法规知识抽取器""" + + def __init__(self, config_file: str): + self.config = self._load_config(config_file) + self.api_key = self._load_api_key() + + self.client = OpenAI( + api_key=self.api_key, + base_url='https://api.deepseek.com', + ) + + self.logger = logging.getLogger(__name__) + + def _load_config(self, config_file: str) -> Dict: + config_path = Path(config_file) + if not config_path.exists(): + config_path = Path(__file__).parent.parent.parent / 'config' / 'deep_extraction_config.yaml' + with open(config_path, 'r', encoding='utf-8') as f: + return yaml.safe_load(f) + + def _load_api_key(self) -> str: + api_file = Path(__file__).parent.parent.parent / 'config' / 'api_keys.yaml' + if api_file.exists(): + with open(api_file, 'r', encoding='utf-8') as f: + api_config = yaml.safe_load(f) + key = api_config.get('deepseek_api_key', '') + if key and key != 'YOUR_API_KEY_HERE': + return key + key = os.environ.get('DEEPSEEK_API_KEY', '') + if key: + return key + raise ValueError("未找到DeepSeek API密钥,请配置 config/api_keys.yaml 或设置 DEEPSEEK_API_KEY 环境变量") + + def _extract_json(self, response_text: str) -> str: + """从响应中提取JSON""" + if '```json' in response_text: + start = response_text.find('```json') + 7 + end = response_text.find('```', start) + if end > start: + return response_text[start:end].strip() + elif '```' in response_text: + start = response_text.find('```') + 3 + end = response_text.find('```', start) + if end > start: + content = response_text[start:end].strip() + if content.startswith('json'): + content = content[4:].strip() + return content + return response_text.strip() + + def _call_api(self, prompt: str, max_retries: int = 3) -> Optional[str]: + """同步调用API""" + for attempt in range(max_retries): + try: + response = self.client.chat.completions.create( + model=self.config['llm']['model'], + messages=[{'role': 'user', 'content': prompt}], + temperature=self.config['llm']['temperature'], + max_tokens=self.config['llm']['max_tokens'], + ) + return response.choices[0].message.content + except Exception as e: + self.logger.error(f"API调用失败 (attempt {attempt+1}/{max_retries}): {e}") + if attempt == max_retries - 1: + return None + import time + time.sleep(self.config['llm'].get('retry_delay', 2)) + + async def _call_api_async(self, prompt: str, max_retries: int = 3) -> Optional[str]: + """异步调用API""" + loop = asyncio.get_event_loop() + return await loop.run_in_executor(None, lambda: self._call_api(prompt, max_retries)) + + async def extract_from_section( + self, + section_text: str, + document_title: str, + section_title: str, + max_retries: int = 3 + ) -> Optional[Dict[str, Any]]: + """从章节文本中抽取实体和关系(基于动态配置的本体)""" + if len(section_text) > 5000: + section_text = section_text[:5000] + + # 从配置动态构建实体类型列表 + entity_prompts = self.config.get('entity_type_prompts', {}) + entity_types_desc = '\n'.join( + f"- {etype}:{desc}" + for etype, desc in entity_prompts.items() + ) + + # 从配置动态构建关系类型列表 + rel_prompts = self.config.get('relationship_type_prompts', {}) + rel_types_desc = '\n'.join( + f"- {rtype}:{desc}" + for rtype, desc in rel_prompts.items() + ) + + # 边界规则 + boundary_rules = self.config.get('extraction_tips', {}).get('entity_boundary_rules', '') + do_not = self.config.get('extraction_tips', {}).get('do_not_extract', '') + + prompt = f"""你是一位中国城市更新法规政策分析专家。请从以下法规章节中识别实体和关系。 + +所属法规:{document_title} +章节名称:{section_title} + +文本内容: +{section_text} + +## 需要识别的实体类型 + +{entity_types_desc} + +## 需要识别的关系类型 + +{rel_types_desc} + +{boundary_rules} + +{do_not} + +## 输出JSON格式 + +严格输出以下JSON格式,不要输出其他内容: +{{ + "entities": [ + {{ + "text": "实体原文表述", + "type": "实体类型(必须是上述类型之一)", + "attributes": {{ + "description": "实体简要描述" + }} + }} + ], + "relationships": [ + {{ + "from_entity": "源实体文本", + "from_type": "源实体类型", + "to_entity": "目标实体文本", + "to_type": "目标实体类型", + "type": "关系类型(必须是上述类型之一)", + "context": "关系上下文原文", + "confidence": 0.9 + }} + ] +}} + +## 注意事项 +- 只抽取文本中明确提到的实体和关系,不要推测 +- 关系的from_entity和to_entity必须是entities中出现的实体 +- 保持原文表述,不要改写实体名称 +- confidence范围0.0-1.0,表示抽取的可信度 +- 重点关注PolicyTool(政策工具)的识别,这是本次抽取的核心目标 +- 确保输出有效JSON,不要输出JSON以外的内容""" + + response_text = await self._call_api_async(prompt, max_retries) + if not response_text: + return None + + try: + json_text = self._extract_json(response_text) + result = json.loads(json_text) + result['metadata'] = { + 'document_title': document_title, + 'section_title': section_title, + 'extraction_time': datetime.now().isoformat(), + } + return result + except json.JSONDecodeError as e: + self.logger.warning(f"JSON解析失败: {e}") + return None + + async def extract_document_references( + self, + document_title: str, + full_text: str, + max_retries: int = 3 + ) -> Optional[Dict[str, Any]]: + """从文档全文中抽取跨文档引用关系""" + text_sample = full_text[:3000] + if len(full_text) > 5000: + text_sample += '\n...\n' + full_text[-2000:] + + prompt = f"""你是一位中国法律专家。请从以下法规文本中识别引用的其他法规文件。 + +法规名称:{document_title} + +文本片段: +{text_sample} + +请识别以下引用关系: +- cites(引用):文本中提到的其他法规 +- implements(实施):文本声明为实施某上位法而制定 +- amends(修正):文本对其他法规的修改 +- replaces(替代):文本声明替代或废止其他法规 + +输出JSON格式: +{{ + "references": [ + {{ + "referenced_law": "被引用的法规名称", + "relation_type": "cites/implements/amends/replaces", + "context": "引用上下文原文" + }} + ] +}} + +注意:只识别文本中明确提到的法规名称(书名号《》内的名称),不要推测。""" + + response_text = await self._call_api_async(prompt, max_retries) + if not response_text: + return None + + try: + json_text = self._extract_json(response_text) + result = json.loads(json_text) + result['metadata'] = { + 'document_title': document_title, + 'extraction_time': datetime.now().isoformat(), + } + return result + except json.JSONDecodeError: + return None + + async def batch_extract_sections( + self, + sections: List[Dict[str, str]], + concurrent: int = 5, + progress_callback=None + ) -> List[Optional[Dict]]: + """批量抽取章节实体""" + results = [] + batch_size = concurrent + + for i in range(0, len(sections), batch_size): + batch = sections[i:i + batch_size] + batch_num = i // batch_size + 1 + total_batches = (len(sections) + batch_size - 1) // batch_size + + self.logger.info(f"处理章节批次 {batch_num}/{total_batches}: {len(batch)} 个") + + tasks = [ + self.extract_from_section( + s['text'], s['document_title'], s['section_title'] + ) + for s in batch + ] + batch_results = await asyncio.gather(*tasks, return_exceptions=True) + results.extend(batch_results) + + if progress_callback: + await progress_callback(batch_num, total_batches, batch_results, batch) + + return results + + async def batch_extract_references( + self, + documents: List[Dict[str, str]], + concurrent: int = 3 + ) -> List[Optional[Dict]]: + """批量抽取文档引用""" + results = [] + batch_size = concurrent + + for i in range(0, len(documents), batch_size): + batch = documents[i:i + batch_size] + + tasks = [ + self.extract_document_references(d['title'], d['text']) + for d in batch + ] + batch_results = await asyncio.gather(*tasks, return_exceptions=True) + results.extend(batch_results) + + self.logger.info(f"引用抽取进度: {min(i+batch_size, len(documents))}/{len(documents)}") + + return results diff --git a/dofile/kg_project/src/main.py b/dofile/kg_project/src/main.py new file mode 100644 index 0000000..882fe45 --- /dev/null +++ b/dofile/kg_project/src/main.py @@ -0,0 +1,166 @@ +""" +城市规划法律法规知识图谱构建 - 主入口 +""" + +import sys +import json +import asyncio +from pathlib import Path + +# 添加项目路径 +sys.path.insert(0, str(Path(__file__).parent)) + + +def step1_read_documents(): + """Step1: 读取Word文档""" + from importlib import import_module + # 动态导入scripts目录下的模块 + sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts')) + docx_reader = import_module('docx_reader') + + source_dir = r"E:\Project\SI\2026_KG_PlanningLaw\data\城市规划法律法规\城市更新法规数据库" + output_path = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\all_documents.json" + + reader = docx_reader.DocxReader(source_dir) + reader.read_all_documents() + reader.save_to_json(output_path) + reader.print_summary() + + +def step2_extract_metadata(): + """Step2: 解析法规元数据""" + sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts')) + metadata_extractor = import_module('legal_metadata_extractor') + + input_path = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\all_documents.json" + output_path = r"E:\Project\SI\2026_KG_PlanningLaw\dofile\kg_project\output\structured_documents.json" + + extractor = metadata_extractor.LegalMetadataExtractor() + extractor.process_all(input_path, output_path) + + +def step3_generate_csv(): + """Step3: 生成结构化CSV""" + sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts')) + extract_csv = import_module('extract_legal_csv') + extract_csv.main() + + +def step4_deep_extraction(): + """Step4: LLM深度抽取""" + from deep_extraction_pipeline import DeepExtractionPipeline + + config_file = str(Path(__file__).parent.parent / 'config' / 'deep_extraction_config.yaml') + structured_input = str(Path(__file__).parent.parent / 'output' / 'structured_documents.json') + raw_input = str(Path(__file__).parent.parent / 'output' / 'all_documents.json') + output_dir = str(Path(__file__).parent.parent / 'output') + + pipeline = DeepExtractionPipeline(config_file) + + results = asyncio.run(pipeline.run( + structured_input, raw_input, output_dir, + max_sections=None # 完整抽取 + )) + + print("\n结果:") + print(json.dumps(results, indent=2, ensure_ascii=False)) + + +def step5_visualize(): + """Step5: 可视化""" + from importlib import import_module + sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts')) + visualize = import_module('visualize_legal_kg') + visualize.main() + + +def show_status(): + """显示项目状态""" + output_dir = Path(__file__).parent.parent / 'output' + + print("\n" + "=" * 60) + print("城市规划法律法规知识图谱 - 项目状态") + print("=" * 60) + + files = { + 'all_documents.json': 'Step1: Word文档读取', + 'structured_documents.json': 'Step2: 法规元数据解析', + 'nodes_structured.csv': 'Step3: 结构化节点', + 'rels_structured.csv': 'Step3: 结构化关系', + 'nodes_llm.csv': 'Step4: LLM抽取节点', + 'rels_llm.csv': 'Step4: LLM抽取关系', + 'nodes_merged.csv': 'Step5: 合并节点', + 'rels_merged.csv': 'Step5: 合并关系', + } + + for filename, description in files.items(): + filepath = output_dir / filename + if filepath.exists(): + import os + size = os.path.getsize(filepath) + print(f" [OK] {description} ({filename}, {size:,} bytes)") + else: + print(f" [--] {description} ({filename}, 未生成)") + + print("=" * 60) + + +def importlib_safe_import(module_name): + from importlib import import_module + return import_module(module_name) + + +def main(): + import argparse + + parser = argparse.ArgumentParser(description='城市规划法律法规知识图谱构建') + parser.add_argument('--step', type=int, choices=[1, 2, 3, 4, 5], help='执行指定步骤') + parser.add_argument('--full', action='store_true', help='运行完整流程') + parser.add_argument('--status', action='store_true', help='查看项目状态') + parser.add_argument('--test', action='store_true', help='测试模式(少量数据)') + + args = parser.parse_args() + + if args.status: + show_status() + return + + steps = { + 1: ("读取Word文档", step1_read_documents), + 2: ("解析法规元数据", step2_extract_metadata), + 3: ("生成结构化CSV", step3_generate_csv), + 4: ("LLM深度抽取", step4_deep_extraction), + 5: ("可视化", step5_visualize), + } + + if args.step: + name, func = steps[args.step] + print(f"\n{'='*60}") + print(f"执行 Step {args.step}: {name}") + print(f"{'='*60}") + func() + elif args.full: + print("\n运行完整流程...") + for step_num, (name, func) in steps.items(): + print(f"\n{'='*60}") + print(f"Step {step_num}: {name}") + print(f"{'='*60}") + try: + func() + except Exception as e: + print(f"Step {step_num} 失败: {e}") + break + else: + print("请指定操作:") + print(" python src/main.py --step 1 # 读取Word文档") + print(" python src/main.py --step 2 # 解析法规元数据") + print(" python src/main.py --step 3 # 生成结构化CSV") + print(" python src/main.py --step 4 # LLM深度抽取") + print(" python src/main.py --step 5 # 可视化") + print(" python src/main.py --full # 运行完整流程") + print(" python src/main.py --status # 查看项目状态") + print(" python src/main.py --test # 测试模式") + + +if __name__ == '__main__': + main() diff --git a/dofile/kg_project/start.bat b/dofile/kg_project/start.bat new file mode 100644 index 0000000..3e5bb95 --- /dev/null +++ b/dofile/kg_project/start.bat @@ -0,0 +1,29 @@ +@echo off +chcp 65001 >nul +echo ======================================== +echo 城市规划法律法规知识图谱构建工具 +echo ======================================== +echo. +echo 请选择操作: +echo 1. 读取Word文档 (Step1) +echo 2. 解析法规元数据 (Step2) +echo 3. 生成结构化CSV (Step3) +echo 4. LLM深度抽取 (Step4) +echo 5. 实体规范化与合并 (Step5) +echo 6. 可视化 (Step6) +echo 7. 运行完整流程 +echo 8. 查看进度 +echo. + +set /p choice=请输入选项: + +if "%choice%"=="1" python scripts/docx_reader.py +if "%choice%"=="2" python scripts/legal_metadata_extractor.py +if "%choice%"=="3" python scripts/extract_legal_csv.py +if "%choice%"=="4" python src/deep_extraction_pipeline.py +if "%choice%"=="5" python src/data_processing/merge_data.py +if "%choice%"=="6" python scripts/visualize_legal_kg.py +if "%choice%"=="7" python src/main.py --full +if "%choice%"=="8" python src/main.py --status + +pause