diff --git a/.agents/docs/README.md b/.agents/docs/README.md new file mode 100644 index 0000000..f5edfc6 --- /dev/null +++ b/.agents/docs/README.md @@ -0,0 +1,12 @@ +# 项目维护人员文档 + +本目录存放维护人员与自动化代理相关资料,主要用于项目运维,不属于对外公开的用户文档。 + +- standards/:存放维护人员需遵守的规范制度与校验规则 +- plans/:存放实施方案与工作交接说明 +- error-experience/、good-experience/:存放内部经验总结文档 +- guides/:面向维护人员的工作流程及集成实操手册 +- architecture/manifest.yaml:记录可读性检查所覆盖的文件路径 + +对外用户文档及说明文件请统一放置在 docs/ 目录下。 + diff --git a/.agents/docs/error-experience/2026-06-08-LlamaIndex多模态消息格式错误.md b/.agents/docs/error-experience/2026-06-08-LlamaIndex多模态消息格式错误.md new file mode 100644 index 0000000..9b397b3 --- /dev/null +++ b/.agents/docs/error-experience/2026-06-08-LlamaIndex多模态消息格式错误.md @@ -0,0 +1,59 @@ +--- +last_reviewed: 2026-06-09 +--- + +# LlamaIndex 多模态消息格式错误 + +## 错误现象 + +``` +pydantic_core._pydantic_core.ValidationError: 2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, ... +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, ... +``` + +## 触发条件 + +- llama-index-core >= 0.14.x +- 使用 `ChatMessage` 构造多模态消息(文本 + 图片) +- 传入 OpenAI 格式的 `content` 列表:`[{"type": "text", ...}, {"type": "image_url", ...}]` + +## 原因 + +`llama-index-core 0.14.x` 重构了 `ChatMessage` 的内部结构: + +| 版本 | 字段 | 多模态 content 格式 | +|------|------|-------------------| +| 0.14.x | `role`, `blocks`, `additional_kwargs` | `TextBlock` / `ImageBlock` 实例 | +| 旧版本 | `role`, `content` | OpenAI 风格字典列表 | + +底层 Pydantic 模型使用 `block_type` 作为 union discriminator,OpenAI 格式的 `{"type": "text", ...}` 字典不包含该字段,导致验证失败。 + +## 修复方法 + +**正确写法(llama-index 原生 blocks 格式):** + +```python +from llama_index.core.llms import ChatMessage +from llama_index.core.base.llms.types import ImageBlock, TextBlock + +messages = [ + ChatMessage(role="system", content=system_prompt), + ChatMessage( + role="user", + blocks=[ + TextBlock(text="请分析这张图片"), + ImageBlock( + url=f"data:image/jpeg;base64,{image_b64}", + detail="high", + ), + ], + ), +] +``` +## 适用版本 + +- llama-index-core: 0.14.22 +- llama-index-llms-openai-like: 0.7.2 \ No newline at end of file diff --git a/.agents/docs/guides/工程实践指南.md b/.agents/docs/guides/工程实践指南.md new file mode 100644 index 0000000..15c14f9 --- /dev/null +++ b/.agents/docs/guides/工程实践指南.md @@ -0,0 +1,261 @@ +--- +last_reviewed: 2026-06-09 +--- + +# 工程实践指南 + +创建或修改任何 Python 项目时,必须严格遵守以下工程规范。 + +--- + +## 1. 包管理与虚拟环境 + +- **唯一包管理器**:使用 `uv`,不使用 `pip`、`pipenv`、`poetry`。 +- **依赖声明**:所有依赖统一在 `pyproject.toml` 中管理,遵循 PEP 621 + PEP 735。 + - `[project].dependencies` 仅放运行时依赖。 + - `[dependency-groups].dev` 放开发依赖(测试、lint、类型检查等)。 +- **版本锁定**:使用 `uv.lock` 锁定依赖版本,提交到版本控制。 +- **安装命令**:`uv sync` 创建虚拟环境并安装所有依赖。 +- **运行命令**:所有 Python 命令通过 `uv run` 前缀执行,确保使用项目虚拟环境。 +- **禁止**:全局安装 Python 包、手动 `python -m venv`、使用 `requirements.txt` 作为主要依赖文件。 + +### `pyproject.toml` 必填字段模板 + +```toml +[project] +name = "" +version = "0.1.0" +description = "<项目描述>" +requires-python = ">=3.12" +dependencies = [ + # 运行时依赖 +] + +[dependency-groups] +dev = [ + "pytest>=8.0", + "pytest-cov>=5.0", + "ruff>=0.9", + "mypy>=1.14", + "deptry>=0.22", + "pre-commit>=4.0", +] +``` + +--- + +## 2. 目录结构 + +``` +/ +├── pyproject.toml # 项目配置(依赖 + 工具配置) +├── uv.lock # 依赖锁定文件 +├── .pre-commit-config.yaml # 提交前检查配置 +├── Makefile # Unix 任务脚本 +├── tasks.py # Windows/跨平台任务脚本 +├── .cursorignore # IDE 忽略配置 +├── src/ # 源代码目录 +│ └── main.py +├── tests/ # 测试目录 +│ └── test_main.py +└── logs/ # 运行时产物(不提交) +``` + +- 源码统一放在 `src/` 下,不直接在根目录放业务代码。 +- 测试统一放在 `tests/` 下。 +- 运行时产物(日志、缓存、临时文件)不提交到版本控制。 + +--- + +## 3. 代码质量工具链 + +### 3.1 Ruff(Lint + Format) + +在 `pyproject.toml` 中配置: + +```toml +[tool.ruff] +target-version = "py312" +line-length = 120 + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "N", "UP", "B"] +ignore = ["E501"] +``` + +- **规则覆盖**:格式错误(E/F/W)、导入排序(I)、命名规范(N)、语法升级(UP)、常见 Bug(B)。 +- **格式化**:使用 `ruff format` 替代 Black。 +- **运行方式**: + - `uv run ruff check .` — 检查问题 + - `uv run ruff check --fix .` — 自动修复 + - `uv run ruff format .` — 格式化代码 + - `uv run ruff format --check .` — 仅检查格式 + +### 3.2 MyPy(静态类型检查) + +```toml +[tool.mypy] +python_version = "3.12" +strict = true +warn_return_any = true +warn_unused_configs = true +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "tests.*" +ignore_errors = true +``` + +- 启用严格模式,测试文件豁免。 +- **运行方式**:`uv run mypy src/main.py` + +### 3.3 Deptry(依赖审计) + +```toml +[tool.deptry] +ignore_notebooks = true +``` + +- 检测未使用、缺失、重复的依赖。 +- **运行方式**:`uv run deptry .` + +--- + +## 4. 测试规范 + +### 4.1 Pytest 配置 + +```toml +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["."] +``` + +### 4.2 测试要求 + +- 测试文件命名:`test_*.py`,放在 `tests/` 目录。 +- 测试函数/类命名:以 `test_` 或 `Test` 开头。 +- 使用 `pytest.fixture` 管理测试资源。 +- 必须使用 `pytest-cov` 生成覆盖率报告。 +- **运行方式**: + ```bash + uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=term-missing + ``` + +--- + +## 5. Pre-commit Hooks + +`.pre-commit-config.yaml` 必须包含: + +```yaml +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.6 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format +``` + +- 安装:`uv run pre-commit install` +- 手动运行:`uv run pre-commit run --all-files` + +--- + +## 6. 任务运行器 + +### 6.1 Makefile(Unix) + +```makefile +.PHONY: install check test run clean + +install: + @uv sync + @uv run pre-commit install + +check: + @uv lock --locked + @uv run ruff check . + @uv run ruff format --check . + @uv run mypy src/main.py + @uv run deptry . + +test: + @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=term-missing + +run: + @uv run python src/main.py + +clean: + @rm -rf .venv __pycache__ .pytest_cache .mypy_cache .ruff_cache +``` + +### 6.2 tasks.py(跨平台) + +提供 `tasks.py` 作为 Windows 兼容的任务运行器,支持相同任务名:`install`、`check`、`test`、`run`、`clean`。 + +--- + +## 7. 版本控制忽略 + +`.cursorignore` / `.gitignore` 必须排除: + +``` +.venv/ +__pycache__/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +*.pyc +logs/ +``` + +--- + +## 8. 开发工作流 + +新项目初始化顺序: + +1. 创建 `pyproject.toml`,声明项目元数据和依赖。 +2. 运行 `uv sync` 创建虚拟环境。 +3. 创建 `.pre-commit-config.yaml`,运行 `uv run pre-commit install`。 +4. 创建 `src/` 目录和入口文件。 +5. 创建 `tests/` 目录和基础测试。 +6. 创建 `Makefile` + `tasks.py`。 +7. 运行 `make check` 或 `python tasks.py check` 验证代码质量。 +8. 运行 `make test` 或 `python tasks.py test` 验证测试通过。 + +日常开发顺序: + +1. `uv run ruff check --fix .` — 先修复 lint 问题。 +2. `uv run ruff format .` — 格式化代码。 +3. `uv run mypy src/` — 类型检查。 +4. `uv run python -m pytest` — 运行测试。 +5. 提交前 pre-commit 会自动执行检查和格式化。 + +--- + +## 9. 编码风格 + +- Python 3.12+ 语法,使用现代特性(如 `match/case`、类型合并 `X | Y`)。 +- 函数和模块必须有 docstring。 +- 优先使用类型注解,返回值类型必须标注。 +- 行长度限制 120 字符。 +- 导入按标准库 → 第三方 → 本地模块分组排序。 +- 偏好函数式编程风格,避免不必要的面向对象封装。 +- 配置与代码分离,使用常量或配置模块管理可变参数。 + +--- + +## 10. 强制检查清单 + +在提交代码或声明任务完成前,必须确认: + +- [ ] `uv lock --locked` 通过(锁定文件与 pyproject.toml 一致) +- [ ] `uv run ruff check .` 无错误 +- [ ] `uv run ruff format --check .` 无差异 +- [ ] `uv run mypy src/` 无类型错误 +- [ ] `uv run deptry .` 无依赖问题 +- [ ] `uv run python -m pytest --cov` 全部通过且覆盖率合理 +- [ ] 所有命令使用 `uv run` 前缀,无全局 pip 操作 \ No newline at end of file diff --git a/.agents/docs/standards/README.md b/.agents/docs/standards/README.md new file mode 100644 index 0000000..163f878 --- /dev/null +++ b/.agents/docs/standards/README.md @@ -0,0 +1,9 @@ +--- +last_reviewed: 2026-06-09 +--- + +# 标准元数据 + +`.agents/docs/standards/*.md` 下所有文件都必须包含 frontmatter,字段包括: + +- `last_reviewed`:最近一次策略审查的 ISO 日期 `YYYY-MM-DD`。 \ No newline at end of file diff --git a/.agents/docs/standards/复利式工程实践.md b/.agents/docs/standards/复利式工程实践.md new file mode 100644 index 0000000..695433b --- /dev/null +++ b/.agents/docs/standards/复利式工程实践.md @@ -0,0 +1,13 @@ +--- +last_reviewed: 2026-06-09 +--- + +# 复利式工程实践 + +记录经验教训: +* 错误经验:`.agents/docs/error-experience/YYYY-MM-DD-.md` +* 正向经验:`.agents/docs/good-experience/YYYY-MM-DD-.md` +* 计划:`.agents/docs/plans/` +* 指南:`agents/docs/guides/` + +在出现重大 bug、CI 失败或发现有价值模式后,创建一条条目并记录根因与经验。 \ No newline at end of file diff --git a/.agents/docs/standards/调试规范.md b/.agents/docs/standards/调试规范.md new file mode 100644 index 0000000..db91834 --- /dev/null +++ b/.agents/docs/standards/调试规范.md @@ -0,0 +1,34 @@ +--- +last_reviewed: 2026-06-09 +--- +# 调试规范 + +## 调试前检查清单 + +在循环调试任务前,需完成以下检查: + +1. 梳理代码链路(最长耗时 5 分钟)。从入口函数追踪至异常执行环节,排查硬编码值、参数缺失或分支逻辑异常等问题。 +2. 对比正常与异常场景。若功能 A 运行正常、功能 B 出现故障,梳理二者代码链路的差异,问题通常就出在差异部分。 +3. 排查基础配置项。检查代理设置、环境变量、端口号、功能开关等。多数故障由配置问题导致,而非代码逻辑错误。 + +## 调试过程要求 + +1. 两次尝试原则。若同一排查方式(重跑测试、调整参数等)连续失败两次,立即停止,更换排查思路: + * 增加针对性日志或打印语句 + * 阅读异常依赖库的源码 + * 精简代码,复现最小故障案例 + * 反思:自身哪些预设判断可能存在偏差 +2. 禁止无限循环调试。定时任务仅用于监控正常运行的进程,不可作为调试工具。若定时循环连续两轮无进展,关闭循环,转为人工调试。 +3. 记录排查思路。每开始一次尝试前,做好记录: + * 初步判断的问题原因 + * 用于验证猜想的依据 + * 本次准备执行的操作 + * 避免重复无效尝试与逻辑死循环 + +## 调试收尾工作 + +1. 编写经验文档。所有非简单故障的调试工作,均需在`.agents/docs/error-experience/` 目录下新建记录文档,内容包含: + * 故障现象 + * 历次排查操作及失败原因 + * 最终解决方案 + * 后续可借鉴的调试经验 \ No newline at end of file diff --git a/.coverage b/.coverage new file mode 100644 index 0000000..e8cf8cf Binary files /dev/null and b/.coverage differ diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000..76f0ec1 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,7 @@ +.venv/ +__pycache__/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.playwright-mcp/ +*.pyc \ No newline at end of file diff --git a/.gitignore b/.gitignore index b14367e..c847677 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,10 @@ -# Python -__pycache__/ -*.py[cod] -*.pyo -*.egg-info/ -dist/ -build/ -.eggs/ - -# Playwright MCP snapshots -.playwright-mcp/ - -# Local secrets & backups -config.json -*.doc.bak - -# Uploads (user data) -web/uploads/ - -# Logs -pipeline.log -*.log - -# Debug images -images/ - -# IDE -.vscode/ -.idea/ +.venv/ .cursor/ - -# OS -.DS_Store -Thumbs.db \ No newline at end of file +__pycache__/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.playwright-mcp/ +*.pyc +logs/ +.vscode/ \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..70f5223 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.6 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2c43da6 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,19 @@ +--- +last_reviewed: 2026-06-09 +--- + +# AGENTS 索引 + +本文件是规则的入口。详细策略文本位于 `.agents/docs/standards/*.md`。 + +## 文档边界 + +* `docs/` 目录专门存放面向开源用户、外部贡献者的项目公开文档及说明文件。 +* 维护规范、实施方案、经验总结、拉取请求佐证材料与各类内部记录资料,均统一放置在 `.agents/` 目录下,避免内部自动化流程相关内容混入公开文档目录。 +* 每个文件夹下都有一个 `README.md` 文件用来交代这个文件夹的作用以及重要的信息。 + +## 标准目录 + +* 标准文档元数据:`.agents/docs/standards/README.md` +* 调试规范:`.agents/docs/standards/调试规范.md` +* 复利式工程实践:`.agents/docs/standards/复利式工程实践.md` \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8280fc0 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +.PHONY: install check test run clean + +install: + @uv sync + @uv run pre-commit install + +check: + @uv lock --locked + @uv run ruff check . + @uv run ruff format --check . + @uv run mypy src/main.py + @uv run deptry . + +test: + @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=term-missing + +run: + @uv run python src/main.py + +clean: + @rm -rf .venv __pycache__ .pytest_cache .mypy_cache .ruff_cache \ No newline at end of file diff --git a/PowerShell注意事项.md b/PowerShell注意事项.md new file mode 100644 index 0000000..e5803f2 --- /dev/null +++ b/PowerShell注意事项.md @@ -0,0 +1,103 @@ +# PowerShell 使用注意事项 + +> 适用于 Windows 环境下的 Shell 命令执行。以下坑点均来自实际踩坑记录。 + +--- + +## 1. 没有 `head` / `tail` / `sed` / `awk` + +PowerShell 不是 bash,这些 Unix 工具不存在。 + +| bash | PowerShell 替代 | +|------|-----------------| +| `cmd \| head -n 5` | `cmd \| Select-Object -First 5` | +| `cmd \| tail -n 3` | `cmd \| Select-Object -Last 3` | +| `grep pattern file` | `Select-String pattern file`(或直接调用 `rg`) | + +**优先用 ripgrep (`rg`)**:搜索文件内容时直接用 `rg`,不依赖 PowerShell 内置工具。 + +--- + +## 2. 引号嵌套规则严格 + +PowerShell 对外部命令的引号处理不同于 bash: + +- `"` 是双引号字符串,内部变量会展开 +- `'` 是单引号字符串,不展开变量 +- `-c "..."` 里再嵌套 Python 的 `"..."` 或 `r'...'` 时,层数超过两层极易出错 + +**示例:** + +```powershell +# 危险:$e 会被 PowerShell 当作变量展开 +python -c "print(f'error: {$e}')" + +# 安全:用单引号包裹整个 -c 参数(PowerShell 不展开单引号) +python -c 'print("hello")' +``` + +**最佳实践**:复杂命令写进临时 `.py` 脚本文件执行,而不是用 `-c` 一行塞完。 + +--- + +## 3. `$` 变量展开干扰 + +在双引号字符串中,PowerShell 会尝试解析 `$var`、`${expr}`。如果 Python 代码里包含 f-string 的 `{...}` 或正则里的 `$`,会被 PowerShell 提前处理。 + +**应对**:用单引号包裹整个命令参数,或将逻辑写进脚本文件。 + +--- + +## 4. Unicode / GBK 编码冲突 + +Windows PowerShell 默认控制台编码是 GBK。当 Python 输出包含非 GBK 字符(如全角符号、emoji、CJK 扩展字符)时: + +``` +UnicodeEncodeError: 'gbk' codec can't encode character '\uff08' in position X +``` + +**解决:** + +- Python 脚本开头加 `sys.stdout.reconfigure(encoding='utf-8')` +- 或设置环境变量 `$env:PYTHONIOENCODING = 'utf-8'` +- 或将输出写入文件而非直接打印到控制台 + +--- + +## 5. stderr 重定向行为不一致 + +bash 的 `2>&1` 在 PowerShell 中对外部程序(如 Python)有时能工作,但格式可能错乱。PowerShell 默认只捕获 stdout(流编号 6),stderr(流编号 2)需要显式合并。 + +| 写法 | 说明 | +|------|------| +| `cmd 2>&1` | stderr 合并到 stdout,对外部程序可用但不稳定 | +| `cmd *>&1` | PowerShell 特有语法,捕获所有输出流,更可靠 | + +**实际表现**:Python Traceback 走 stderr,用 `2>&1` 有时被 PowerShell 拦截后格式错乱。建议优先写脚本文件执行。 + +--- + +## 6. 中文路径在错误回显中乱码 + +命令失败时,PowerShell 的 stderr 回显中包含中文的路径会显示为 `�����`。不影响命令执行本身,但会让错误信息难以阅读。 + +**应对**:优先通过 Python 脚本自身打印结构化错误(写入文件或返回 JSON),而非依赖 PowerShell 的错误捕获来诊断问题。 + +--- + +## 7. 管道传递的是对象而非文本 + +PowerShell 的管道传递的是 .NET 对象而非文本字节流。当把 PowerShell 管道接到外部程序时,对象的 `ToString()` 可能不是预期的格式。 + +--- + +## 核心原则速查 + +1. **复杂命令写脚本**:超过一行的操作,写成 `.py` 或 `.ps1` 文件执行 +2. **优先用 ripgrep (`rg`)**:搜索文件内容时直接用 `rg` +3. **编码问题提前处理**:Python 入口设 `sys.stdout.reconfigure(encoding='utf-8')` +4. **避免 `-c` 嵌套引号超过两层** + +--- + +*创建于 2026-05-27 | Windows PowerShell + Python 环境* \ No newline at end of file diff --git a/README.md b/README.md index 73e3db6..42867c4 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,44 @@ # 财务报销自动化 -自动从 PDF 发票提取信息,OCR 识别支付记录截图,生成发票汇总表与易耗品出库单,并可选在财务系统中自动填报报销单。 +自动从 PDF 发票提取信息,生成发票汇总表与易耗品出库单,并可选在财务系统中自动填报报销单。 + +**支持发票类型区分**:系统自动识别高铁票、酒店住宿等差旅发票与普通发票。差旅发票不生成易耗品出库单,走差旅报销流程;普通发票生成出库单,走普通报销流程。 ## 项目结构 ``` -├── run.py # CLI 入口(发票提取 → OCR → 浏览器填报) -├── config.json # 配置文件(登录凭据、默认值、存放地点等) -├── 易耗品、出库单.doc # 易耗品出库单 Word 模板(Web/CLI 填写用) -├── app/ -│ ├── config.py # 配置加载 -│ ├── extractor.py # PDF 发票信息提取 -│ ├── ocr.py # OCR 刷卡信息识别 -│ ├── bot.py # 浏览器自动填报 -│ ├── pipeline.py # CLI 流程编排 -│ └── fill_consumable_doc.py # 将 CSV 填入易耗品出库单(Word COM) -├── web/ -│ ├── app.py # Web 服务入口 -│ ├── templates/ -│ │ ├── index.html # PC 端主页 -│ │ └── mobile_upload.html # 移动端扫码上传 -│ ├── static/ # 前端 CSS / JS -│ └── uploads/ # 按会话隔离的上传与产物目录 -├── *.pdf # 发票 PDF(CLI 模式,放在项目根目录) -├── *.png / *.jpg # 与 PDF 配对的支付截图 -├── invoice_summary.csv # 发票汇总表(CLI 产物) -└── images/ # 浏览器调试截图 +├── pyproject.toml # 项目配置(依赖、工具链) +├── uv.lock # 依赖锁定文件 +├── Makefile # 任务脚本(跨平台) +├── tasks.py # 任务脚本(Windows 兼容) +├── config.json # 配置文件(登录凭据、默认值等) +├── config.example.json # 配置示例 +├── 易耗品、出库单.doc # 易耗品出库单 Word 模板 +├── src/ +│ ├── __init__.py # 包初始化 / 日志器 +│ ├── config.py # 配置加载 +│ ├── bot.py # 浏览器自动填报 +│ ├── pipeline.py # CLI 流程编排 +│ ├── main.py # CLI 入口 +│ ├── doc/ # 文档处理模块 +│ │ ├── extractor.py # 编排入口:串联 PDF 读取 → LLM 提取 → 分类 +│ │ ├── pdf.py # PDF 文件发现与文本提取 +│ │ ├── llm_extractor.py # LLM 信息提取 +│ │ ├── invoice.py # 发票类型常量、分类逻辑、CSV 读写工具 +│ │ ├── fill_consumable_doc.py # 将 CSV 填入易耗品出库单(Word COM) +│ │ ├── prompt.py # LLM 提示词模板 +│ │ └── prompts/ # 提示词模板文件 +│ └── web/ +│ ├── app.py # Web 服务入口 +│ ├── templates/ +│ │ ├── index.html # PC 端主页 +│ │ └── mobile_upload.html # 移动端扫码上传 +│ ├── static/ # 前端 CSS / JS +│ └── uploads/ # 按会话隔离的上传与产物目录 +├── tests/ # 测试目录 +├── *.pdf # 发票 PDF(CLI 模式,放在项目根目录) +├── invoice_summary.csv # 发票汇总表(CLI 产物) +└── images/ # 浏览器调试截图 ``` ## 数据流 @@ -33,49 +46,47 @@ ```mermaid flowchart TB PDF[PDF 发票] --> Extract[extractor 提取] - Extract --> List[发票列表] - Img[支付截图] --> OCR[OCR 识别] - List --> OCR - OCR --> CSV[(invoice_summary.csv)] - CSV --> Fill[fill_consumable_doc] + Extract --> Classify{发票类型分类} + Classify -->|差旅发票| Travel[高铁票 / 酒店住宿] + Classify -->|普通发票| General[普通发票] + Travel --> CSV[(invoice_summary.csv)] + General --> CSV + CSV -->|仅普通发票| Fill[fill_consumable_doc] Fill --> Doc[易耗品、出库单.doc] CSV --> Bot[bot 浏览器自动化] - Bot --> Submit[财务系统填报
可选] + Bot -->|差旅模式| Submit_T[差旅报销填报
TODO] + Bot -->|普通模式| Submit_G[普通报销填报] ``` ## 环境要求 -- Python 3.10+ +- **Python 3.12+** +- **uv** 包管理器([安装指南](https://docs.astral.sh/uv/getting-started/installation/)) - Windows(易耗品出库单填写依赖 Microsoft Word + COM,仅 Windows 可用) -- 主要依赖见下方安装步骤 ## 快速开始 ### 1. 安装依赖 -> OCR 相关依赖建议在已有 PaddleOCR 的环境中安装(如 MinerU 虚拟环境)。 - ```bash -pip install pdfplumber==0.11.9 paddleocr==2.8.1 playwright==1.60.0 flask==3.0.3 pywin32 -playwright install chromium +# 同步所有依赖(运行时 + 开发工具) +make install +# Windows 上等效命令: +python tasks.py install ``` +项目使用 `uv` 管理依赖,所有包版本锁定在 `uv.lock` 中,确保可复现。 + | 依赖 | 用途 | |------|------| | pdfplumber | PDF 发票文本提取 | -| paddleocr | 支付截图 OCR | | playwright | 财务系统浏览器自动化 | | flask | Web 服务 | | pywin32 | 填写 Word 出库单(`fill_consumable_doc`) | ### 2. 准备数据(CLI 模式) -将发票 PDF 和对应的支付截图放在项目根目录下。脚本会自动匹配 PDF 与截图: - -1. **优先文件名匹配** — PDF 与截图同名(如 `发票.pdf` ↔ `发票.png`) -2. **金额近邻匹配** — 文件名不同时,按价税合计与刷卡金额就近配对 - -截图支持格式:`.png`、`.jpg`、`.jpeg`、`.bmp`、`.webp`。 +将发票 PDF 放在项目根目录下。 ### 3. 配置 @@ -104,16 +115,17 @@ playwright install chromium ### 4. 运行(CLI) ```bash -# 全流程(发票提取 → OCR 识别 → 浏览器填报) -python run.py +# 全流程(发票提取 → 浏览器填报) +make run +# Windows 等效: +python tasks.py run # 仅执行某一步 -python run.py --step invoice # 仅发票提取 -python run.py --step ocr # 仅 OCR 识别 -python run.py --step submit # 仅浏览器填报 +uv run python src/main.py --step invoice # 仅发票提取 +uv run python src/main.py --step submit # 仅浏览器填报 # 覆盖配置中的登录凭据 -python run.py -u 工号 -p 密码 +uv run python src/main.py -u 工号 -p 密码 ``` ### 5. 填写易耗品出库单(CLI) @@ -121,9 +133,9 @@ python run.py -u 工号 -p 密码 需已生成 `invoice_summary.csv`,且本机已安装 **Microsoft Word**: ```bash -python -m app.fill_consumable_doc -python -m app.fill_consumable_doc --csv invoice_summary.csv --doc "易耗品、出库单.doc" -python -m app.fill_consumable_doc --no-backup # 不生成 .doc.bak 备份 +uv run python -m src.doc.fill_consumable_doc +uv run python -m src.doc.fill_consumable_doc --csv invoice_summary.csv --doc "易耗品、出库单.doc" +uv run python -m src.doc.fill_consumable_doc --no-backup # 不生成 .doc.bak 备份 ``` 填写规则概要: @@ -137,8 +149,7 @@ python -m app.fill_consumable_doc --no-backup # 不生成 .doc.bak 备份 | 步骤 | 命令 | 说明 | |------|------|------| -| 发票提取 | `--step invoice` | 扫描根目录 PDF,生成 `invoice_summary.csv` / `.md` | -| OCR 识别 | `--step ocr` | 识别支付截图,回填刷卡日期、金额、持卡人等到 CSV | +| 发票提取 | `--step invoice` | 扫描根目录 PDF,生成 `invoice_summary.csv` | | 浏览器填报 | `--step submit` | 登录信息门户 → 报销系统 → 自动填单、上传附件 | > 分步执行时,上一步的 CSV 会自动成为下一步的输入。 @@ -148,12 +159,14 @@ python -m app.fill_consumable_doc --no-backup # 不生成 .doc.bak 备份 | 列名 | 说明 | |------|------| | 序号 | 行号 | +| 发票类型 | 自动识别:`高铁票` / `酒店住宿` / `普通发票` | | 发票号码 | 电子发票号码 | | 开票日期 | 开票日期 | -| 项目名称 / 规格型号 | 货物或应税劳务信息 | +| 项目名称 / 规格型号 | 货物或应税劳务信息(差旅发票为出发站→到达站) | | 价税合计 | 发票含税金额 | | 销售方名称 | 销方名称 | -| 人员姓名 / 刷卡日期 / 公务卡号 / 刷卡金额 | OCR 自支付截图回填 | +| 出发站 / 到达站 / 车次 / 乘车日期 / 座位等级 | 高铁票专用字段 | +| 人员姓名 / 刷卡日期 / 公务卡号 / 刷卡金额 | 可手工或 Web 端编辑补充 | | 备注 / 工号 | 可手工或 Web 端编辑补充 | ## Web 服务 @@ -161,48 +174,85 @@ python -m app.fill_consumable_doc --no-backup # 不生成 .doc.bak 备份 提供浏览器界面:上传文件 → 自动处理 → 在线编辑 → 下载产物 → 可选提交财务系统。 ```bash -python web/app.py +uv run python src/web/app.py ``` 访问 `http://localhost:5000`。 ### 推荐使用流程 -1. 上传 PDF + 支付截图(或上传已有 CSV) +1. 上传 PDF(或上传已有 CSV) 2. 填写配置(账号、密码、姓名、公务卡号、存放地点等),可上传 `config.json` 一键填充 -3. 点击 **开始处理** — 完成发票提取、OCR、生成 CSV,并自动填写 **易耗品、出库单.doc** -4. 在 **下载文件** 区域下载 CSV / Markdown / 出库单 Word -5. 在表格中核对、修改发票数据(提交财务系统前会自动保存) -6. 确认无误后点击 **提交到财务系统** +3. 点击 **开始处理** — 完成发票提取、生成 CSV,系统自动识别发票类型并分类统计 +4. **普通发票**:自动生成 **易耗品、出库单.doc**,可下载 +5. **差旅发票**(高铁票/酒店住宿):跳过出库单生成,直接进入差旅报销流程 +6. 在表格中核对、修改发票数据(提交财务系统前会自动保存) +7. 确认无误后点击 **提交到财务系统** ### 处理模式 | 模式 | 入口 | 说明 | |------|------|------| -| **PDF 模式** | 上传 PDF + 截图 | 提取发票 → OCR → 生成 CSV + 出库单 | -| **CSV 快捷模式** | 仅上传 CSV | 跳过提取与 OCR,直接生成出库单并进入编辑/提交 | +| **PDF 模式** | 上传 PDF | 提取发票 → 生成 CSV + 自动分类 → 普通发票生成出库单 | +| **CSV 快捷模式** | 仅上传 CSV | 跳过提取,读取 CSV 中的发票类型,按需生成出库单 | + +### 发票类型区分 + +系统自动识别以下发票类型并按类型分流: + +| 发票类型 | 识别依据 | 出库单 | 填报模式 | +|----------|----------|--------|----------| +| **高铁票** | 含"电子客票"、"中国铁路"、"12306"等关键词 | 不生成 | 差旅报销(TODO) | +| **酒店住宿** | 含"住宿费"、"餐饮服务"、"租赁服务"等关键词 | 不生成 | 差旅报销(TODO) | +| **普通发票** | 其他所有发票 | 自动生成 | 普通报销(已实现) | + +当同一批次同时包含差旅发票和普通发票时,系统会为普通发票生成出库单,并在填报时分别处理。 ### 功能一览 | 功能 | 说明 | |------|------| -| 发票提取 + OCR | 上传 PDF 后自动完成 | -| 易耗品出库单 | 处理完成后自动生成 Word,可下载 | +| 发票提取 | 上传 PDF 后自动完成 | +| 发票类型自动分类 | 高铁票/酒店住宿/普通发票,自动分流处理 | +| 易耗品出库单 | 仅普通发票自动生成 Word,差旅发票跳过 | | 表格在线编辑 | 处理完成后可修改 CSV 各字段;保存后重新生成出库单 | | 财务系统填报 | 单独按钮触发,处理阶段不会自动提交 | | 实时日志 | SSE 推送处理进度 | | 配置上传 | 支持上传 `config.json` 填充表单 | -| 手机扫码上传 | 二维码打开移动端页面,拍照上传支付截图,PC 端轮询同步 | +| 手机扫码上传 | 二维码打开移动端页面,拍照上传,PC 端轮询同步 | -> Web 端浏览器填报以无头模式运行。未上传 PDF 时,填报阶段会跳过附件上传。 +> Web 端浏览器填报以无头模式运行。未上传 PDF 时,填报阶段会跳过附件上传。 > 出库单生成需要 **Windows + Word + pywin32**;若失败,页面会显示具体原因,CSV 等其它产物仍可正常使用。 接口说明见 [API.md](./API.md)。 +## 开发任务 + +项目提供统一的任务脚本,支持跨平台使用: + +| 任务 | Makefile | tasks.py | 说明 | +|------|----------|----------|------| +| 安装依赖 | `make install` | `python tasks.py install` | 同步依赖 + 安装 pre-commit | +| 代码检查 | `make check` | `python tasks.py check` | Ruff lint + 格式化 + MyPy 类型检查 + deptry 依赖检查 | +| 运行测试 | `make test` | `python tasks.py test` | pytest + 覆盖率报告 | +| 运行 CLI | `make run` | `python tasks.py run` | 执行全流程 | +| 清理缓存 | `make clean` | `python tasks.py clean` | 删除虚拟环境和缓存 | + +## 代码质量 + +项目配置了完整的代码质量工具链: + +- **Ruff** — 快速 lint 检查和代码格式化(替代 flake8 + isort + black) +- **MyPy** — 严格模式类型检查(`strict = true`) +- **deptry** — 检测未声明、未使用、过时依赖 +- **pre-commit** — 提交前自动运行 Ruff 检查和格式化 + +所有检查通过后方可提交代码。 + ## 注意事项 - 浏览器填报时会打开或使用 Chromium,请勿手动干扰自动化流程 - 调试截图保存在 `images/` 目录 -- OCR 不会覆盖 CSV 中已有非空字段 - 项目根目录需保留 `易耗品、出库单.doc` 模板;Web 每次从模板复制到会话目录再填写,不修改原模板 - `config.json` 含敏感信息,请勿提交到公开仓库 +- **发票类型区分**:差旅发票(高铁票/酒店住宿)不会生成易耗品出库单,当前差旅报销填报流程仍在开发中(TODO) \ No newline at end of file diff --git a/app/extractor.py b/app/extractor.py deleted file mode 100644 index bd97383..0000000 --- a/app/extractor.py +++ /dev/null @@ -1,256 +0,0 @@ -""" -PDF 发票信息提取 - -从 PDF 发票文件中提取关键字段,输出为标准化的发票数据列表。 - -对外接口: - extract_invoices(directory) -> list[dict] 扫描目录下所有 PDF 并提取 - save_csv(invoices, path) 保存为 CSV - save_markdown(invoices, path) 保存为 Markdown 汇总 -""" - -import csv -import re -from pathlib import Path - -from . import get_logger - -log = get_logger("extractor") - -CSV_COLUMNS = [ - "序号", "发票号码", "开票日期", "项目名称", "规格型号", - "价税合计", "销售方名称", "人员姓名", "刷卡日期", - "公务卡号", "刷卡金额", "备注", "工号", -] - - -# ------------------------------------------------------------------ -# PDF 文件发现与文本提取 -# ------------------------------------------------------------------ - -def find_pdf_files(directory: str = ".") -> list[Path]: - """查找目录下所有 PDF 文件(非递归)""" - pdf_dir = Path(directory) - if not pdf_dir.exists(): - return [] - return sorted(pdf_dir.glob("*.pdf")) - - -def extract_text_from_pdf(filepath: Path) -> str: - """从单个 PDF 中提取全部文本""" - try: - import pdfplumber - except ImportError: - raise ImportError("缺少 pdfplumber,请执行: pip install pdfplumber") - - try: - parts = [] - with pdfplumber.open(filepath) as pdf: - for page in pdf.pages: - text = page.extract_text() - if text: - parts.append(text) - return "\n".join(parts) - except Exception as e: - log.error(f"无法读取 {filepath.name}: {e}") - return "" - - -# ------------------------------------------------------------------ -# 字段解析 -# ------------------------------------------------------------------ - -def _first(regexes: list[str], text: str) -> str | None: - """尝试多个正则,返回第一个匹配组的文本""" - for pattern in regexes: - m = re.search(pattern, text) - if m: - return m.group(1).strip() - return None - - -def _parse_line_item(line: str) -> dict | None: - """解析单行明细(*分类*具体名称 格式)""" - m = re.match(r"\*([^*]+)\*\s*(.+)", line) - if m: - return { - "项目名称": f"*{m.group(1).strip()}*{m.group(2).strip()}", - "规格型号": m.group(2).strip(), - } - return None - - -def _extract_line_items(text: str) -> list[dict]: - """从发票文本中提取所有明细行""" - items = [] - skip_keywords = ["项目名称", "合 计", "价税合计", "备注", "开票人"] - - for line in text.split("\n"): - line = line.strip() - if not line: - continue - if any(kw in line for kw in skip_keywords): - continue - if "*" in line: - item = _parse_line_item(line) - if item: - items.append(item) - - return items - - -def _format_date(date_raw: str) -> str: - """将「2026年5月18日」转为「2026/5/18」""" - m = re.match(r"(\d{4})年(\d{1,2})月(\d{1,2})日", date_raw) - if m: - return f"{m.group(1)}/{m.group(2)}/{m.group(3)}" - return date_raw - - -def parse_invoice(text: str) -> dict: - """从发票文本中提取关键字段,返回 dict - - 返回字段: - 发票号码, 开票日期, 销售方名称, 价税合计, _items (明细列表) - 其他字段(人员姓名等)留空,后续由 OCR 步骤填充 - """ - invoice: dict[str, str] = {} - - invoice["发票号码"] = _first([r"发票号码[::]?\s*(\d+)"], text) or "" - - date_raw = _first([r"开票日期[::]?\s*(\d{4}年\d{1,2}月\d{1,2}日)"], text) or "" - invoice["开票日期"] = _format_date(date_raw) if date_raw else "" - - invoice["销售方名称"] = _first( - [ - r"销\s*售?\s*方?\s*名称[::]?\s*(.+?)(?:\n|$)", - r"销\s*名称[::]?\s*(.+?)(?:\n|$)", - ], - text, - ) or "" - - invoice["价税合计"] = _first( - [r"价税合计.*?(小写)[¥¥]?\s*(\d+\.?\d*)"], text - ) or "" - - invoice["_items"] = _extract_line_items(text) - - # 以下字段无法从 PDF 提取,留空由 OCR 步骤填充 - for key in ("项目名称", "规格型号", "人员姓名", "刷卡日期", - "公务卡号", "刷卡金额", "备注", "工号"): - if key not in invoice: - invoice[key] = "" - - return invoice - - -# ------------------------------------------------------------------ -# CSV / Markdown 输出 -# ------------------------------------------------------------------ - -def save_csv(invoices: list[dict], output_path: str | Path = "invoice_summary.csv"): - """将发票列表保存为 CSV""" - csv_path = Path(output_path) - - with open(csv_path, "w", encoding="utf-8", newline="") as f: - writer = csv.writer(f) - writer.writerow(CSV_COLUMNS) - - for idx, inv in enumerate(invoices, 1): - items = inv.get("_items", []) - first_item = items[0] if items else {} - writer.writerow([ - idx, - inv.get("发票号码", ""), - inv.get("开票日期", ""), - first_item.get("项目名称", inv.get("项目名称", "")), - first_item.get("规格型号", inv.get("规格型号", "")), - inv.get("价税合计", ""), - inv.get("销售方名称", ""), - inv.get("人员姓名", ""), - inv.get("刷卡日期", ""), - inv.get("公务卡号", ""), - inv.get("刷卡金额", ""), - inv.get("备注", ""), - inv.get("工号", ""), - ]) - - log.info(f"CSV 已保存: {csv_path.name}") - - -def save_markdown(invoices: list[dict], output_path: str | Path = "invoice_summary.md"): - """将发票列表保存为 Markdown 汇总表""" - md_path = Path(output_path) - lines = [ - "# 发票信息汇总表", - "", - "| 序号 | 发票号码 | 开票日期 | 项目名称 | 规格型号 | 价税合计 | 销售方名称 |", - "|------|---------|---------|---------|---------|---------|-----------|", - ] - - total = 0.0 - for idx, inv in enumerate(invoices, 1): - amount = 0.0 - try: - amount = float(inv.get("价税合计", "0")) - except (ValueError, TypeError): - pass - total += amount - - items = inv.get("_items", []) - first_item = items[0] if items else {} - project = first_item.get("项目名称", inv.get("项目名称", "-")) - spec = first_item.get("规格型号", inv.get("规格型号", "-")) - - lines.append( - f"| {idx} " - f"| {inv.get('发票号码', '')} " - f"| {inv.get('开票日期', '')} " - f"| {project} | {spec} " - f"| ¥{amount:,.2f} " - f"| {inv.get('销售方名称', '')} |" - ) - - lines.append("") - lines.append(f"**总计: ¥{total:,.2f}**") - lines.append("") - - with open(md_path, "w", encoding="utf-8") as f: - f.write("\n".join(lines)) - - log.info(f"Markdown 已保存: {md_path.name}") - - -# ------------------------------------------------------------------ -# 主入口 -# ------------------------------------------------------------------ - -def extract_invoices(directory: str = ".") -> list[dict]: - """扫描目录下所有 PDF,提取发票信息并返回列表""" - target_dir = Path(directory).absolute() - - pdf_files = find_pdf_files(directory) - if not pdf_files: - log.warning("未找到 PDF 文件") - return [] - - log.info(f"发现 {len(pdf_files)} 个 PDF 文件") - - all_invoices = [] - for pdf_path in pdf_files: - text = extract_text_from_pdf(pdf_path) - if text: - invoice = parse_invoice(text) - if invoice: - all_invoices.append(invoice) - else: - log.warning(f"未能解析: {pdf_path.name}") - else: - log.warning(f"未能提取文本: {pdf_path.name}") - - if all_invoices: - log.info(f"共处理 {len(all_invoices)} 张发票") - else: - log.warning("未成功解析任何发票") - - return all_invoices \ No newline at end of file diff --git a/app/ocr.py b/app/ocr.py deleted file mode 100644 index aea5a57..0000000 --- a/app/ocr.py +++ /dev/null @@ -1,454 +0,0 @@ -""" -OCR 刷卡信息提取 - -从支付截图中识别刷卡记录(姓名、日期、金额),回填到发票数据中。 - -匹配策略: - 1. 先按文件名匹配(PDF 和图片同名) - 2. 未匹配的通过金额近邻匹配 - -对外接口: - enrich_with_ocr(rows, directory) -> list[dict] 用 OCR 识别结果丰富发票数据 -""" - -import csv -import os -import re -from pathlib import Path - -from . import get_logger - -log = get_logger("ocr") - - -# ------------------------------------------------------------------ -# 懒加载 OCR -# ------------------------------------------------------------------ - -_ocr_instance = None - - -def _get_ocr(): - """懒加载 PaddleOCR 实例(兼容 2.x / 3.x)""" - global _ocr_instance - if _ocr_instance is not None: - return _ocr_instance - - os.environ.setdefault("FLAGS_use_mkldnn", "0") - os.environ.setdefault("FLAGS_mkldnn_cache_enabled", "0") - - from paddleocr import PaddleOCR - - try: - _ocr_instance = PaddleOCR(use_textline_orientation=True, lang="ch") - except TypeError: - try: - _ocr_instance = PaddleOCR(lang="ch") - except TypeError: - _ocr_instance = PaddleOCR() - - return _ocr_instance - - -# ------------------------------------------------------------------ -# OCR 识别 -# ------------------------------------------------------------------ - -def ocr_image(image_path: Path) -> list[dict]: - """对单张图片执行 OCR,返回 [{"text": str, "confidence": float}, ...]""" - ocr = _get_ocr() - texts = [] - - try: - results = ocr.ocr(str(image_path), cls=True) - if results and isinstance(results, list): - for page_result in results: - if not page_result: - continue - for line in page_result: - if isinstance(line, (list, tuple)) and len(line) >= 2: - _, text_info = line[0], line[1] - if isinstance(text_info, (list, tuple)) and len(text_info) >= 2: - texts.append({ - "text": str(text_info[0]), - "confidence": float(text_info[1]), - }) - except Exception: - try: - if hasattr(ocr, "predict"): - results = ocr.predict(str(image_path)) - if results: - for result in results: - if hasattr(result, "rec_result_list"): - for line in result.rec_result_list: - t = getattr(line, "text", "") or "" - s = getattr(line, "score", 0.0) or 0.0 - texts.append({"text": str(t), "confidence": float(s)}) - elif isinstance(result, list): - for line in result: - if isinstance(line, (list, tuple)) and len(line) >= 2: - t = line[1][0] if isinstance(line[1], (list, tuple)) else str(line[1]) - s = line[1][1] if isinstance(line[1], (list, tuple)) and len(line[1]) > 1 else 0.0 - texts.append({"text": str(t), "confidence": float(s)}) - except Exception as e: - log.error(f"OCR 识别失败: {e}") - - return texts - - -def extract_card_info(texts: list[dict]) -> dict: - """从 OCR 文本中提取刷卡信息(日期 / 金额 / 姓名)""" - info = {"刷卡日期": "", "刷卡金额": "", "人员姓名": ""} - - valid = [t for t in texts if t["confidence"] > 0.5] - full_text = " ".join(t["text"] for t in valid) - if not full_text: - return info - - # 日期(优先级匹配,避免误抓发票开票日期) - date_candidates = [] - for pattern, priority in [ - (r"记账时间[::\s]*(\d{4}[-/]\d{1,2}[-/]\d{1,2})", 10), - (r"交易时间[::\s]*(\d{4}[-/]\d{1,2}[-/]\d{1,2})", 9), - (r"刷卡日期[::\s]*(\d{4}[-/]\d{1,2}[-/]\d{1,2})", 9), - (r"日期[::\s]*(\d{4}[-/]\d{1,2}[-/]\d{1,2})", 5), - ]: - for m in re.finditer(pattern, full_text): - date_candidates.append((priority, m.start(), m.group(1).replace("-", "/"))) - if date_candidates: - date_candidates.sort(key=lambda x: (-x[0], x[1])) - info["刷卡日期"] = date_candidates[0][2] - - # 金额 - amount_candidates = [] - for pattern, priority in [ - (r"交易金额[::\s]*([+-]?[\d,]+\.?\d*)", 10), - (r"刷卡金额[::\s]*([+-]?[\d,]+\.?\d*)", 10), - (r"金额[::\s]*([+-]?[\d,]+\.?\d*)", 5), - ]: - for m in re.finditer(pattern, full_text): - amt_str = m.group(1).replace(",", "").replace("+", "") - try: - val = float(amt_str) - if 0 < val < 999999: - amount_candidates.append((priority, m.start(), amt_str)) - except ValueError: - continue - if amount_candidates: - amount_candidates.sort(key=lambda x: (-x[0], x[1])) - info["刷卡金额"] = amount_candidates[0][2] - - # 姓名(排除公司/机构后缀) - EXCLUDE_SUFFIXES = ("公司", "银行", "中心", "支行", "商户", "网点", "有限", "责任") - name_candidates = [] - for pattern, priority in [ - (r"交易户名[::\s]*([\u4e00-\u9fff]{2,6})", 10), - (r"户名[::\s]*([\u4e00-\u9fff]{2,6})", 8), - (r"持卡人[::\s]*([\u4e00-\u9fff]{2,6})", 8), - (r"姓名[::\s]*([\u4e00-\u9fff]{2,6})", 8), - ]: - for m in re.finditer(pattern, full_text): - name = m.group(1) - if not any(name.endswith(s) for s in EXCLUDE_SUFFIXES): - name_candidates.append((priority, m.start(), name)) - if name_candidates: - name_candidates.sort(key=lambda x: (-x[0], x[1])) - info["人员姓名"] = name_candidates[0][2] - - return info - - -# ------------------------------------------------------------------ -# PDF 发票号提取 -# ------------------------------------------------------------------ - -def extract_invoice_number(pdf_path: Path) -> str: - """从 PDF 中提取发票号码""" - try: - import pdfplumber - except ImportError: - log.warning("缺少 pdfplumber,跳过发票号提取") - return "" - - try: - with pdfplumber.open(str(pdf_path)) as pdf_file: - page_text = "" - for page in pdf_file.pages: - page_text += page.extract_text() or "" - - for pattern in [ - r"发票号码[::\s]*([A-Za-z0-9]{8,20})", - r"发票代码[::\s]*([A-Za-z0-9]{10,12})", - r"号码[::\s]*([A-Za-z0-9]{8,20})", - ]: - m = re.search(pattern, page_text) - if m: - return m.group(1) - except Exception as e: - log.warning(f"PDF 读取失败 ({pdf_path.name}): {e}") - - return "" - - -# ------------------------------------------------------------------ -# 图片配对 -# ------------------------------------------------------------------ - -def _extract_amount_from_pdf(pdf_path: Path) -> float | None: - """从 PDF 中提取价税合计金额""" - try: - import pdfplumber - with pdfplumber.open(str(pdf_path)) as pdf: - text = "" - for page in pdf.pages: - t = page.extract_text() - if t: - text += t + "\n" - m = re.search(r"价税合计.*?(小写)[¥¥]?\s*(\d+\.?\d*)", text) - if m: - return float(m.group(1)) - except Exception: - pass - return None - - -def _extract_amount_from_image(img_path: Path) -> float | None: - """从图片 OCR 中提取刷卡金额""" - texts = ocr_image(img_path) - if not texts: - return None - info = extract_card_info(texts) - amt_str = info.get("刷卡金额", "") - if amt_str: - try: - return float(amt_str) - except ValueError: - pass - return None - - -def find_image_pairs(directory: str = ".") -> list[tuple[Path, Path]]: - """查找 PDF 和对应图片的配对 - - 1. 先按文件名匹配(PDF 和图片同名) - 2. 未匹配的通过金额近邻匹配 - """ - base = Path(directory) - pdfs = sorted(base.glob("*.pdf")) - image_exts = {".png", ".jpg", ".jpeg", ".bmp", ".webp"} - - all_images = sorted( - f for ext in image_exts for f in base.glob(f"*{ext}") - ) - - # ---- Phase 1: 文件名匹配 ---- - pairs: list[tuple[Path, Path]] = [] - matched_pdfs: set[Path] = set() - matched_imgs: set[Path] = set() - - for pdf in pdfs: - for ext in image_exts: - img = base / f"{pdf.stem}{ext}" - if img.exists(): - pairs.append((pdf, img)) - matched_pdfs.add(pdf) - matched_imgs.add(img) - break - - unmatched_pdfs = [p for p in pdfs if p not in matched_pdfs] - unmatched_imgs = [i for i in all_images if i not in matched_imgs] - - if not unmatched_pdfs or not unmatched_imgs: - return pairs - - # ---- Phase 2: 金额近邻匹配 ---- - if len(unmatched_pdfs) > 0 and len(unmatched_imgs) > 0: - log.info(f"文件名匹配 {len(pairs)} 组,剩余 {len(unmatched_pdfs)} 个 PDF、{len(unmatched_imgs)} 张图片,尝试金额匹配...") - - pdf_amounts: dict[Path, float] = {} - for pdf in unmatched_pdfs: - amt = _extract_amount_from_pdf(pdf) - if amt is not None: - pdf_amounts[pdf] = amt - - img_amounts: dict[Path, float] = {} - for img in unmatched_imgs: - amt = _extract_amount_from_image(img) - if amt is not None: - img_amounts[img] = amt - - # 贪婪匹配:每张图片找金额差最小的 PDF - used_pdfs: set[Path] = set() - for img, img_amt in sorted(img_amounts.items(), key=lambda x: x[0].name): - best_pdf: Path | None = None - best_diff: float = float("inf") - - for pdf, pdf_amt in pdf_amounts.items(): - if pdf in used_pdfs: - continue - diff = abs(pdf_amt - img_amt) - if diff < best_diff: - best_diff = diff - best_pdf = pdf - - if best_pdf is not None: - pairs.append((best_pdf, img)) - used_pdfs.add(best_pdf) - - log.info(f"金额匹配完成,共 {len(pairs)} 组配对") - - return pairs - - -# ------------------------------------------------------------------ -# CSV 读写 -# ------------------------------------------------------------------ - -from .extractor import CSV_COLUMNS - - -def _load_csv(csv_path: Path) -> list[dict] | None: - """读取现有 CSV 为 dict 列表,失败返回 None""" - try: - with open(csv_path, encoding="utf-8", newline="") as f: - reader = csv.DictReader(f) - fieldnames = reader.fieldnames or [] - missing = [c for c in CSV_COLUMNS if c not in fieldnames] - if missing: - log.error(f"CSV 缺少必要列: {missing}") - return None - return [row for row in reader] - except FileNotFoundError: - log.error(f"CSV 文件不存在: {csv_path.name}") - return None - except Exception as e: - log.error(f"CSV 读取失败: {e}") - return None - - -def _save_csv(csv_path: Path, rows: list[dict]): - """保存 CSV""" - with open(csv_path, "w", encoding="utf-8", newline="") as f: - writer = csv.DictWriter(f, fieldnames=CSV_COLUMNS) - writer.writeheader() - writer.writerows(rows) - - -# ------------------------------------------------------------------ -# Markdown 同步 -# ------------------------------------------------------------------ - -def save_markdown_from_csv(csv_path: Path, rows: list[dict]): - """根据最新 CSV 数据生成 Markdown 汇总表""" - md_path = csv_path.with_suffix(".md") - columns = [ - ("序号", "序号"), ("发票号码", "发票号码"), ("开票日期", "开票日期"), - ("项目名称", "项目名称"), ("规格型号", "规格型号"), ("价税合计", "价税合计"), - ("销售方名称", "销售方名称"), ("人员姓名", "人员姓名"), - ("刷卡日期", "刷卡日期"), ("公务卡号", "公务卡号"), - ("刷卡金额", "刷卡金额"), ("备注", "备注"), ("工号", "工号"), - ] - - lines = ["# 发票信息汇总表", ""] - header = " | ".join(col[1] for col in columns) - separator = "|".join(["------" for _ in columns]) - lines.append(f"| {header} |") - lines.append(f"|{separator}|") - - total_price = 0.0 - total_card = 0.0 - - for row in rows: - cells = [] - for key, _ in columns: - value = row.get(key, "").strip() - - if key == "价税合计" and value: - try: - total_price += float(value.replace(",", "")) - cells.append(f"¥{float(value.replace(',', '')):,.2f}") - except (ValueError, TypeError): - cells.append(value) - elif key == "刷卡金额" and value: - try: - total_card += float(value.replace(",", "")) - cells.append(f"¥{float(value.replace(',', '')):,.2f}") - except (ValueError, TypeError): - cells.append(value) - else: - cells.append(value if value else "") - - lines.append("| " + " | ".join(cells) + " |") - - lines.append("") - lines.append(f"**价税合计总计: ¥{total_price:,.2f}**") - lines.append(f"**刷卡金额总计: ¥{total_card:,.2f}**") - lines.append("") - - with open(md_path, "w", encoding="utf-8") as f: - f.write("\n".join(lines)) - - log.info(f"Markdown 已同步: {md_path.name}") - - -# ------------------------------------------------------------------ -# 主入口 -# ------------------------------------------------------------------ - -def enrich_with_ocr(rows: list[dict], directory: str = ".") -> list[dict]: - """用 OCR 识别结果丰富发票数据,返回更新后的行列表 - - rows 应包含「发票号码」列,已存在的字段不会覆盖。 - """ - pairs = find_image_pairs(directory) - if not pairs: - log.warning("未找到 PDF-图片配对文件,跳过 OCR") - return rows - - log.info(f"找到 {len(pairs)} 组 PDF-图片配对") - - ocr_by_invoice: dict[str, dict] = {} - - for idx, (pdf, img) in enumerate(pairs, 1): - inv_num = extract_invoice_number(pdf) - if not inv_num: - inv_num = pdf.stem - - texts = ocr_image(img) - if not texts: - log.warning(f"OCR 未识别到文本: {img.name}") - continue - - info = extract_card_info(texts) - ocr_by_invoice[inv_num] = info - - # 更新行数据 - updated = 0 - matched = 0 - - for i, row in enumerate(rows): - inv_num = row.get("发票号码", "").strip() - ocr_info = ocr_by_invoice.get(inv_num) - - if not ocr_info: - for key, val in ocr_by_invoice.items(): - if inv_num in key or key in inv_num: - ocr_info = val - break - - if ocr_info: - matched += 1 - - if not row.get("人员姓名", "").strip() and ocr_info["人员姓名"]: - row["人员姓名"] = ocr_info["人员姓名"] - updated += 1 - if not row.get("刷卡日期", "").strip() and ocr_info["刷卡日期"]: - row["刷卡日期"] = ocr_info["刷卡日期"] - updated += 1 - if not row.get("刷卡金额", "").strip() and ocr_info["刷卡金额"]: - row["刷卡金额"] = ocr_info["刷卡金额"] - updated += 1 - - log.info(f"OCR 完成: 匹配 {matched}/{len(rows)} 行,更新 {updated} 个字段") - - return rows \ No newline at end of file diff --git a/app/pipeline.py b/app/pipeline.py deleted file mode 100644 index 0822560..0000000 --- a/app/pipeline.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -报销全流程编排 - -将发票提取 → OCR 识别 → 浏览器填报串联为一条管道, -数据在内存中流转,同时生成 CSV / Markdown 中间产物。 -""" - -import sys -from pathlib import Path - -from . import get_logger -from .config import load_config -from .extractor import extract_invoices, save_csv as save_invoice_csv, save_markdown as save_invoice_md -from .ocr import enrich_with_ocr, _save_csv as save_ocr_csv, save_markdown_from_csv, _load_csv - -log = get_logger("pipeline") - - -def run_pipeline(step: str = "all", username: str = None, password: str = None): - """执行报销流程 - - Args: - step: all | invoice | ocr | submit - username: 覆盖 config.json 中的用户名 - password: 覆盖 config.json 中的密码 - """ - config = load_config() - if username: - config["username"] = username - if password: - config["password"] = password - - # 工作目录(项目根目录) - project_dir = Path(__file__).parent.parent - - # -------------------------------------------------- - # Step 1: 发票提取 - # -------------------------------------------------- - invoices = None - - if step in ("all", "invoice"): - log.info("=" * 60) - log.info("[1/3] 发票提取") - log.info("=" * 60) - - invoices = extract_invoices(str(project_dir)) - if not invoices: - log.error("未提取到任何发票数据") - return 1 - - save_invoice_csv(invoices, project_dir / "invoice_summary.csv") - save_invoice_md(invoices, project_dir / "invoice_summary.md") - - if step == "invoice": - log.info("[1/3] 发票提取 完成") - return 0 - - # -------------------------------------------------- - # Step 2: OCR 识别 - # -------------------------------------------------- - if step in ("all", "ocr"): - log.info("=" * 60) - log.info("[2/3] OCR 识别") - log.info("=" * 60) - - csv_path = project_dir / "invoice_summary.csv" - - if invoices is None: - rows = _load_csv(csv_path) - if rows is None: - return 1 - else: - # 将 dict 列表转为 CSV 风格的 dict(对齐列名) - from .extractor import CSV_COLUMNS - rows = [] - for idx, inv in enumerate(invoices, 1): - items = inv.get("_items", []) - first_item = items[0] if items else {} - rows.append({ - "序号": str(idx), - "发票号码": inv.get("发票号码", ""), - "开票日期": inv.get("开票日期", ""), - "项目名称": first_item.get("项目名称", inv.get("项目名称", "")), - "规格型号": first_item.get("规格型号", inv.get("规格型号", "")), - "价税合计": inv.get("价税合计", ""), - "销售方名称": inv.get("销售方名称", ""), - "人员姓名": inv.get("人员姓名", ""), - "刷卡日期": inv.get("刷卡日期", ""), - "公务卡号": inv.get("公务卡号", ""), - "刷卡金额": inv.get("刷卡金额", ""), - "备注": inv.get("备注", ""), - "工号": inv.get("工号", ""), - }) - - rows = enrich_with_ocr(rows, str(project_dir)) - save_ocr_csv(csv_path, rows) - save_markdown_from_csv(csv_path, rows) - invoices = rows - - if step == "ocr": - log.info("[2/3] OCR 识别 完成") - return 0 - - # -------------------------------------------------- - # Step 3: 浏览器填报 - # -------------------------------------------------- - if step in ("all", "submit"): - log.info("=" * 60) - log.info("[3/3] 报销提交") - log.info("=" * 60) - - from .bot import load_invoice_data, run_bot - - csv_path = project_dir / "invoice_summary.csv" - bot_invoices = load_invoice_data(str(csv_path), config) - run_bot(config, bot_invoices) - - if step == "submit": - log.info("[3/3] 报销提交 完成") - return 0 - - # -------------------------------------------------- - # 全流程完成 - # -------------------------------------------------- - log.info("=" * 60) - log.info("全流程执行完毕") - log.info("=" * 60) - return 0 \ No newline at end of file diff --git a/config.example.json b/config.example.json index b33d40b..9a1f95f 100644 --- a/config.example.json +++ b/config.example.json @@ -8,5 +8,10 @@ "default_name": "默认报销人姓名", "default_card_no": "默认公务卡号", "default_person_id": "默认人员编号", - "consumable_storage": "躬行楼 C205" + "consumable_storage": "躬行楼 C205", + "llm": { + "model": "你的模型名称", + "api_base": "你的API地址", + "api_key": "你的API密钥" + } } diff --git a/config.json b/config.json new file mode 100644 index 0000000..bcc2d01 --- /dev/null +++ b/config.json @@ -0,0 +1,17 @@ +{ + "username": "202407021", + "password": "wang!1624155937", + "sso_login_url": "https://tyrz.fynu.edu.cn/sso/login", + "portal_url": "https://tyrz.fynu.edu.cn/oshall", + "reimburse_url": "http://210.45.32.214:8081", + "reimburse_page": "/expen/common/common?v=4.0", + "default_name": "王建锋", + "default_card_no": "6282880139161682", + "default_person_id": "202407021", + "consumable_storage": "新工科 D605", + "llm": { + "model": "qwen/qwen3.5-9b", + "api_base": "http://100.123.83.115:1234/v1", + "api_key": "123456" + } +} \ No newline at end of file diff --git a/API.md b/docs/API.md similarity index 74% rename from API.md rename to docs/API.md index 75c9a06..12ce6a5 100644 --- a/API.md +++ b/docs/API.md @@ -1,7 +1,11 @@ +--- +last_reviewed: 2026-06-09 +--- + # 财务报销自动化 — API 文档 > 基础地址: `http://localhost:5000` -> 启动: `python web/app.py` +> 启动: `uv run python src/web/app.py` ## 总览 @@ -12,7 +16,7 @@ | 3 | POST | `/api/upload/` | 上传文件(PDF/图片) | | 4 | POST | `/api/upload-csv/` | 上传 CSV 发票数据 | | 5 | GET | `/api/files/` | 列出会话目录中的文件 | -| 6 | POST | `/api/process/` | 启动处理(提取+OCR+出库单) | +| 6 | POST | `/api/process/` | 启动处理(提取+LLM识别+出库单) | | 7 | GET | `/api/logs/` | SSE 日志流 | | 8 | GET | `/api/download//` | 下载生成的文件 | | 9 | GET | `/api/data/` | 获取发票数据(JSON) | @@ -26,8 +30,8 @@ ## 会话与目录 - 调用 `POST /api/session` 获得 `session_id` -- 该会话下所有文件存放在 `web/uploads//` -- 典型产物:`invoice_summary.csv`、`invoice_summary.md`、`易耗品、出库单.doc`、`config.json`、`session.log`、`result.json` +- 该会话下所有文件存放在 `src/web/uploads//` +- 典型产物:`invoice_summary.csv`、`易耗品、出库单.doc`、`config.json`、`session.log`、`result.json` --- @@ -91,7 +95,7 @@ Content-Type: multipart/form-data { "ok": true, "filename": "invoice_summary.csv" } ``` -> 上传 CSV 后可跳过 PDF 提取和 OCR,直接进入处理/编辑流程。 +> 上传 CSV 后可跳过 PDF 提取和 LLM 识别,直接进入处理/编辑流程。 --- @@ -137,17 +141,17 @@ Content-Type: application/json | mode | 行为 | |------|------| -| `auto` | 仅有 CSV、无 PDF → CSV 模式;否则 → PDF 提取 + OCR | -| `csv` | 使用已上传 CSV,跳过提取与 OCR | -| `pdf` | 执行 PDF 提取 + OCR | +| `auto` | 仅有 CSV、无 PDF → CSV 模式;否则 → PDF 提取 + LLM 识别 | +| `csv` | 使用已上传 CSV,跳过提取与 LLM 识别 | +| `pdf` | 执行 PDF 提取 + LLM 识别 | **处理内容(PDF 模式):** -1. 从会话目录 PDF 提取发票信息 → `invoice_summary.csv` / `.md` -2. 对支付截图 OCR,回填刷卡字段 -3. 从项目根目录复制 `易耗品、出库单.doc` 模板到会话目录并自动填写(需 Windows + Word) +1. 从会话目录 PDF 提取发票信息 → `invoice_summary.csv` +2. 对支付截图多模态 LLM 识别,回填刷卡字段 +3. 根据发票类型自动分类:差旅发票(高铁票/酒店住宿)不生成出库单;普通发票从模板复制并自动填写 -配置会写入 `web/uploads//config.json`。 +配置会写入 `src/web/uploads//config.json`。 **响应(立即):** @@ -165,12 +169,28 @@ Content-Type: application/json "elapsed": "45.2s", "invoice_count": 4, "csv_url": "/api/download//invoice_summary.csv", - "md_url": "/api/download//invoice_summary.md", + "travel_count": 2, + "general_count": 2, "doc_url": "/api/download//%E6%98%93%E8%80%97%E5%93%81%E3%80%81%E5%87%BA%E5%BA%93%E5%8D%95.doc", "doc_ok": true } ``` +**纯差旅发票(跳过出库单生成):** + +```json +{ + "ok": true, + "invoice_count": 3, + "csv_url": "/api/download//invoice_summary.csv", + "travel_count": 3, + "general_count": 0, + "doc_ok": null, + "doc_skipped": true, + "doc_message": "差旅发票无需生成易耗品出库单" +} +``` + **出库单生成失败时(CSV 等仍可能成功):** ```json @@ -178,11 +198,23 @@ Content-Type: application/json "ok": true, "invoice_count": 4, "csv_url": "/api/download//invoice_summary.csv", + "travel_count": 2, + "general_count": 2, "doc_ok": false, "doc_error": "服务器未安装 pywin32,无法生成 Word 出库单" } ``` +**字段说明:** + +| 字段 | 类型 | 说明 | +|------|------|------| +| `travel_count` | int | 差旅发票数量(高铁票/酒店住宿) | +| `general_count` | int | 普通发票数量 | +| `doc_ok` | bool/null | `true`=成功,`false`=失败,`null`=已跳过(纯差旅发票) | +| `doc_skipped` | bool | 是否因纯差旅发票而跳过出库单生成 | +| `doc_message` | string | 跳过时的提示信息 | + --- ### 6. SSE 日志流 @@ -211,9 +243,11 @@ data: 2026-05-26 12:00:01 [INFO ] extractor: 正在提取发票... | 来源 | 典型字段 | |------|----------| -| `/api/process` | `ok`, `elapsed`, `invoice_count`, `csv_url`, `md_url`, `doc_url`, `doc_ok`, `doc_error` | +| `/api/process` | `ok`, `elapsed`, `invoice_count`, `csv_url`, `travel_count`, `general_count`, `doc_url`, `doc_ok`, `doc_skipped`, `doc_error` | | `/api/submit-financial` | `ok`, `submit_ok`, `submit_error` | +> SSE 超时时间为 10 分钟(600 秒)。 + --- ### 7. 下载文件 @@ -227,7 +261,6 @@ GET /api/download// | 扩展名 | Content-Type | |--------|----------------| | `.csv` | `text/csv; charset=utf-8` | -| `.md` | `text/markdown; charset=utf-8` | | `.doc` | `application/msword` | | 其它 | `application/octet-stream` | @@ -235,8 +268,7 @@ GET /api/download// | 文件名 | 说明 | |--------|------| -| `invoice_summary.csv` | 发票汇总(含 OCR 结果) | -| `invoice_summary.md` | Markdown 摘要 | +| `invoice_summary.csv` | 发票汇总(含 LLM 识别结果) | | `易耗品、出库单.doc` | 自动填写的出库单 | | 用户上传的 CSV 名 | CSV 快捷模式下的原始文件 | @@ -328,7 +360,17 @@ Content-Type: application/json 保存后会根据最新 CSV **重新生成** 出库单 Word(与会话 `config.json` 中的 `consumable_storage` 等配置一致)。 -若出库单生成失败: +**纯差旅发票跳过出库单:** + +```json +{ + "ok": true, + "doc_ok": null, + "doc_skipped": true +} +``` + +**出库单生成失败:** ```json { @@ -348,10 +390,14 @@ POST /api/submit-financial/ **前置条件:** -- 会话目录存在 `config.json`(由 `/api/process` 写入) +- 会话目录存在 `config.json`(由 `/api/process` 写入),否则返回 `400` - 存在可用的发票 CSV(通常为 `invoice_summary.csv`) -**说明:** 前端一般在提交前调用 `/api/save` 保存表格修改。本接口**不会**自动执行发票提取或 OCR。 +**说明:** + +- 前端一般在提交前调用 `/api/save` 保存表格修改 +- 本接口**不会**自动执行发票提取或 LLM 识别 +- 根据发票类型选择填报模式:纯差旅发票走差旅报销流程,含普通发票走普通报销流程 **响应(立即):** @@ -416,6 +462,19 @@ Content-Type: multipart/form-data --- +## 发票类型分类 + +系统自动将发票分为两类,影响出库单生成和后续报销流程: + +| 类型 | 判断依据 | 出库单 | 报销流程 | +|------|----------|--------|----------| +| 差旅发票 | 高铁票、酒店住宿等 | 不生成 | 差旅报销 | +| 普通发票 | 其他(办公用品、耗材等) | 自动生成 | 普通报销 | + +`/api/process` 和 `/api/save` 的响应中 `travel_count` / `general_count` 即为分类统计。 + +--- + ## 端到端流程 ```mermaid @@ -430,8 +489,12 @@ sequenceDiagram PC->>Server: POST /api/upload/{sid} PC->>Server: POST /api/process/{sid} - Note over Server: PDF 提取 + OCR + 写 config.json - Server->>Word: 复制模板并填写出库单 + Note over Server: PDF 提取 + LLM 识别 + 写 config.json + alt 含普通发票 + Server->>Word: 从模板复制并填写出库单 + else 纯差旅发票 + Note over Server: 跳过出库单生成 + end Server-->>PC: SSE done (csv_url, doc_url, ...) PC->>Server: GET /api/data/{sid} @@ -457,7 +520,7 @@ sequenceDiagram 不经过 Web、在本地直接填写出库单: ```bash -python -m app.fill_consumable_doc --csv invoice_summary.csv --doc "易耗品、出库单.doc" +uv run python -m src.doc.fill_consumable_doc --csv invoice_summary.csv --doc "易耗品、出库单.doc" ``` -详见 [README.md](./README.md)。 +详见 [README.md](./README.md)。 \ No newline at end of file diff --git a/docs/报销操作指南.md b/docs/报销操作指南.md new file mode 100644 index 0000000..dd9510a --- /dev/null +++ b/docs/报销操作指南.md @@ -0,0 +1,481 @@ +--- +last_reviewed: 2026-06-09 +--- + +# 阜阳师范大学财务报销系统 - 自动化操作指南 + +> 适用场景:日常报销录入(支持 Web 界面和 CLI 两种方式) +> 最后更新:2026-06-09 + +--- + +## 一、系统概览 + +``` +整体流程: + +信息门户(SSO登录) → 财务系统入口 → 单点登录页 → 网络报销 → 日常报销录入 +(tyrz.fynu.edu.cn) (点击"财务系统") (新标签页) (a:has(img)) (/expen/common/common) + +目标系统: http://210.45.32.214:8081 +用户: 张三 (工号: xxxxxxx) +``` + +### 关键 URL + +| 系统 | URL | 说明 | +|------|-----|------| +| SSO 登录 | `https://tyrz.fynu.edu.cn/sso/login` | 统一认证入口 | +| 信息门户 | `https://tyrz.fynu.edu.cn/oshall` | 登录后跳转目标 | +| 报销系统 | `http://210.45.32.214:8081` | 网络报销主系统 | +| 日常报销录入 | `/expen/common/common?v=4.0` | 目标录入页面 | + +--- + +## 二、两种使用方式 + +### 方式一:Web 界面(推荐) + +适合日常使用,可视化操作,支持手机端拍照上传。 + +```bash +uv run python src/web/app.py +# 访问: http://localhost:5000 +``` + +### 方式二:CLI 命令行 + +适合脚本化、批量处理。 + +```bash +# 全流程(发票提取 → LLM 识别 → 浏览器填报) +uv run python src/main.py + +# 分步执行 +uv run python src/main.py --step invoice # 仅发票提取 +uv run python src/main.py --step submit # 仅浏览器填报 + +# 覆盖登录凭据 +uv run python src/main.py -u 202407021 -p "your_password" +``` + +--- + +## 三、数据准备 + +### 3.1 发票数据 CSV + +系统支持两种数据输入方式: + +**方式 A:PDF 发票 + 支付截图(自动提取)** + +上传 PDF 发票文件和支付截图,系统自动完成: +1. 从 PDF 提取发票信息 +2. 多模态 LLM 识别支付截图中的刷卡信息 +3. 生成 `invoice_summary.csv` + +**方式 B:直接上传 CSV(快捷模式)** + +已有发票数据 CSV 可直接上传,跳过 PDF 提取和 LLM 识别步骤。 + +CSV 需包含以下列: + +| 列名 | 说明 | 示例 | +|------|------|------| +| 序号 | 发票序号 | 1, 2, 3... | +| 发票号码 | 发票编号 | 26442000005432652421 | +| 开票日期 | 发票开具日期 | 2026/5/18 | +| 项目名称 | 采购项目名称 | 电阻一批 | +| 规格型号 | 规格型号 | — | +| 价税合计 | 发票金额 | 2900.00 | +| 销售方名称 | 商户/销售方 | 佛山市泓宇芯科技有限公司 | +| 人员姓名 | 报销人 | 王建锋(默认值) | +| 刷卡日期 | 公务卡消费日期 | 2026/5/18 → 自动转为 2026-05-18 | +| 公务卡号 | 公务卡卡号 | 6282880139161682(默认值) | +| 刷卡金额 | 实际刷卡金额 | 2900.00 | +| 备注 | 备注信息 | — | +| 工号 | 人员工号 | 202407021(默认值) | + +### 3.2 发票类型分类 + +系统自动将发票分为两类,影响后续处理流程: + +| 类型 | 判断依据 | 出库单 | 报销流程 | +|------|----------|--------|----------| +| 差旅发票 | 高铁票、酒店住宿等 | 不生成 | 差旅报销 | +| 普通发票 | 其他(办公用品、耗材等) | 自动生成 | 普通报销 | + +### 3.3 附件文件 + +PDF 发票文件需与支付截图配对上传。系统通过金额匹配自动关联发票和支付记录。 + +--- + +## 四、Web 界面操作流程 + +### 4.1 启动服务 + +```bash +uv run python src/web/app.py +# 访问: http://localhost:5000 +``` + +### 4.2 上传文件 + +1. **发票 PDF**:点击或拖拽上传 PDF 文件(支持多选) +2. **支付截图**:点击或拖拽上传图片文件(支持多选) +3. **手机扫码上传**:扫描页面二维码,通过手机拍照上传支付截图 +4. **CSV 快捷上传**:已有发票数据 CSV 可直接上传,跳过提取和 LLM 识别 + +### 4.3 配置信息 + +填写以下配置项(也可通过上传 `config.json` 快速填充): + +| 字段 | 说明 | +|------|------| +| 账号 | 财务系统工号 | +| 密码 | 登录密码 | +| 默认姓名 | 报销人姓名 | +| 公务卡号 | 公务卡卡号 | +| 人员编号 | 人员编号 | +| 存放地点 | 出库单存放地点 | + +### 4.4 开始处理 + +点击"开始处理"按钮,系统自动执行: + +1. **发票提取**:从 PDF 提取发票信息 +2. **LLM 信息提取**:多模态 LLM 识别支付截图中的刷卡信息 +3. **发票分类**:自动区分差旅发票和普通发票 +4. **出库单生成**:普通发票自动生成易耗品出库单(差旅发票跳过) + +处理过程中可在日志面板实时查看进度。 + +### 4.5 编辑发票数据 + +处理完成后,发票数据以可编辑表格形式展示: + +- 直接点击单元格即可编辑 +- 修改后点击"提交到财务系统"时自动保存 +- 保存后会自动重新生成出库单 + +### 4.6 下载文件 + +处理完成后下载: + +| 文件 | 说明 | +|------|------| +| `invoice_summary.csv` | 发票汇总数据 | +| `易耗品、出库单.doc` | 自动填写的出库单(仅普通发票) | + +### 4.7 提交到财务系统 + +确认数据无误后,点击"🚀 提交到财务系统"按钮,系统自动: + +1. 登录信息门户 +2. 进入财务系统 +3. 创建报销单 +4. 填写基本信息 +5. 录入报销明细 +6. 录入支付方式 +7. 上传附件 + +--- + +## 五、CLI 执行流程 + +### 5.1 全流程执行 + +```bash +uv run python src/main.py +``` + +执行步骤: + +``` +Step 1: 发票提取 + ├── 扫描 PDF 发票文件 + ├── 提取发票信息 + ├── 生成 invoice_summary.csv + └── 自动分类(差旅/普通) + +Step 2: LLM 信息提取 + ├── 扫描支付截图 + ├── 多模态 LLM 识别刷卡信息 + ├── 回填 CSV 刷卡字段 + └── 更新 invoice_summary.csv + +Step 3: 报销提交 + ├── 登录信息门户 + ├── 进入财务系统 + ├── 创建报销单 + ├── 填写基本信息 + ├── 录入报销明细 + ├── 录入支付方式 + ├── 上传附件 + └── 提交(需手动确认) +``` + +### 5.2 分步执行 + +```bash +# 仅发票提取 +uv run python src/main.py --step invoice + +# 仅浏览器填报 +uv run python src/main.py --step submit +``` + +### 5.3 上传目录 + +CLI 模式自动发现 `src/web/uploads/` 下最新的会话文件夹,该文件夹包含上传的 PDF 和图片。 + +--- + +## 六、页面元素速查 + +### 基本信息页 + +| 字段 | 选择器 | 操作 | +|------|--------|------| +| 报销说明 | `#EXPENEXPLAIN` | fill | +| 项目代码 | `#PROJECTCODE` | click → 弹窗选择 | +| 项目弹窗 | `#promodal .fixed-table-body tbody tr` | 点击第一行 | +| 下一步按钮 | `#saveAndNext` | click | + +### 报销明细页 + +| 字段 | 选择器 | 操作 | +|------|--------|------| +| 增加按钮 | `#insertDetail` | click | +| 经济事项代码 | `#economicscode2` | click → 弹窗选择 | +| 经济科目弹窗 | `#econmodal .fixed-table-body tbody tr` | 点击第 3 行 | +| 单据数 | `input[name="expenPwCommondetail.HOWBILLS"]` | fill | +| 报销总金额 | `#je_zwzcdz` | fill | +| 确定按钮 | `#detailAdd` | click | + +### 支付方式页 + +| 字段 | 选择器 | 操作 | +|------|--------|------| +| 增加按钮 | `#insertPay` | click | +| 人员编号 | `#personid2` | fill | +| 人员姓名 | `#accountname2` | fill | +| 刷卡日期 | `#receiptdate2` | fill | +| 公务卡号 | `#localaccount2` | fill | +| 刷卡金额 | `#receiptmoney2` | fill | +| 实报金额 | `#money2` | fill | +| 商户 | `#merchant2` | fill | +| 备注 | `#smark2` | fill | +| 确定按钮 | `#payAdd` | click | + +### 附件清单页 + +| 字段 | 选择器 | 操作 | +|------|--------|------| +| 增加按钮 | `#insertAcc` | click | +| 附件类型 | `#fjlx` | select_option → '1'(发票) | +| 附件说明 | `#fpsmxx` | fill | +| 文件上传 | `#file` | set_input_files | +| 确定按钮 | `#cjtj` | click | + +### 提交 + +| 操作 | 选择器 | +|------|--------| +| 提交按钮 | `#submit` | +| 提交按钮(备用) | `#submit2` | + +--- + +## 七、数据流向 + +``` +PDF 发票 + 支付截图 + │ + ▼ extract_invoices() + │ + ├── 读取 PDF 发票 + ├── 提取发票信息 + ├── 自动分类(差旅/普通) + └── 输出: invoice_summary.csv + │ + ▼ enrich_with_llm() + ├── 多模态 LLM 识别支付截图 + ├── 回填刷卡字段 + └── 更新 invoice_summary.csv + │ + ▼ fill_consumable_from_template() + ├── 仅普通发票 + ├── 从模板复制出库单 + └── 自动填写出库单 + │ + ▼ run_bot() + ├── 登录信息门户 + ├── 进入财务系统 + ├── 创建报销单 + ├── 填写基本信息 + ├── 录入报销明细 + ├── 录入支付方式 + ├── 上传附件 + └── 提交 +``` + +--- + +## 八、关键设计说明 + +### 8.1 浏览器启动 + +系统使用 Playwright 的 `sync_playwright()` 启动 Chromium 浏览器,支持 `headless` 模式。每次运行均启动新浏览器实例,需重新登录。可通过 `headless` 参数控制是否显示浏览器窗口。 + +### 8.2 明细录入策略 + +系统采用"一条总明细"策略:将所有发票合并为一条报销明细,报销总金额为所有发票刷卡金额之和,单据数为发票总张数。支付方式则逐张发票分别录入,每张发票对应一条支付记录。 + +### 8.3 经济科目选择 + +系统在经济科目弹窗中固定选择第 3 行。如需更改科目,修改 `rows[2]` 的索引即可。 + +### 8.4 项目选择 + +系统在项目选择弹窗中固定选择第 1 行。如需更改项目,修改 `first_row` 的选择逻辑即可。 + +### 8.5 网络报销链接动态获取 + +单点登录页的"网络报销"链接参数每次不同,系统通过 `a:has(img[src*="wlbx"])` 精确定位链接,动态提取 `href` 属性后导航,不硬编码 URL。 + +### 8.6 发票类型分流 + +- **差旅发票**(高铁票/酒店住宿):不生成易耗品出库单,走差旅报销流程 +- **普通发票**:生成易耗品出库单,走普通报销流程 + +### 8.7 移动端同步 + +PC 端生成二维码指向移动端上传页面,手机端上传的图片通过轮询同步到 PC 端,实现跨设备协作。 + +--- + +## 九、配置说明 + +### config.json + +项目根目录 `config.json` 包含默认配置: + +```json +{ + "username": "202407021", + "password": "your_password", + "sso_login_url": "https://tyrz.fynu.edu.cn/sso/login", + "portal_url": "https://tyrz.fynu.edu.cn/oshall", + "reimburse_url": "http://210.45.32.214:8081", + "reimburse_page": "/expen/common/common?v=4.0", + "default_name": "王建锋", + "default_card_no": "6282880139161682", + "default_person_id": "202407021", + "consumable_storage": "躬行楼 C205", + "llm": { + "model": "qwen3.5-9b", + "api_base": "http://100.123.83.115:1234/v1", + "api_key": "123456" + } +} +``` + +| 字段 | 说明 | +|------|------| +| `username` | 财务系统工号 | +| `password` | 登录密码 | +| `sso_login_url` | SSO 登录地址 | +| `portal_url` | 信息门户地址 | +| `reimburse_url` | 报销系统地址 | +| `reimburse_page` | 报销录入页面路径 | +| `default_name` | 默认报销人姓名 | +| `default_card_no` | 默认公务卡号 | +| `default_person_id` | 默认人员编号 | +| `consumable_storage` | 出库单存放地点 | +| `llm.model` | LLM 模型名称 | +| `llm.api_base` | LLM API 地址 | +| `llm.api_key` | LLM API 密钥 | + +--- + +## 十、日志与调试 + +### 日志输出 + +- 控制台实时输出(INFO 级别) +- 文件日志:`logs/<日期>.log`(UTF-8 编码) +- Web 界面:实时 SSE 日志流 + +### 截图保存 + +每个关键步骤自动截图到 `images/` 目录: + +| 截图文件 | 对应步骤 | +|----------|----------| +| `debug_portal_loaded.png` | 登录成功 | +| `debug_step3_project_modal.png` | 项目弹窗打开 | +| `debug_step3_project_selected.png` | 项目选择完成 | +| `debug_step3_done.png` | 基本信息完成 | +| `debug_after_add_click.png` | 点击新增后 | +| `debug_item_total.png` | 总明细录入完成 | +| `debug_step5_done.png` | 支付方式完成 | +| `debug_step6_done.png` | 附件上传完成 | +| `debug_error.png` | 异常状态 | + +### 超时设置 + +- 页面默认超时:30 秒 +- 登录门户等待:最多 30 秒 +- 单点登录页等待:最多 15 秒 +- SSE 日志流超时:10 分钟(600 秒) + +--- + +## 十一、常见问题 + +| 问题 | 原因 | 解决方案 | +|------|------|----------| +| 登录超时 | SSO 需要手动验证码/微信扫码 | 手动完成验证后脚本继续 | +| 未找到财务系统入口 | 门户页面结构变化 | 检查 `images/debug_*` 截图定位 | +| 经济科目选择失败 | 弹窗加载延迟 | 检查超时设置,增加等待时间 | +| 附件上传失败 | PDF 文件不存在或路径错误 | 确认 PDF 在当前工作目录 | +| 金额不匹配 | 明细合计 ≠ 支付合计 | 检查 CSV 数据中刷卡金额 | +| 提交被拦截 | 必填项为空 | 检查 `logs/<日期>.log` 定位失败步骤 | +| LLM 多模态提取失败 | llama-index 版本不兼容 | 确保使用 llama-index-core >= 0.14.x,多模态消息使用 `blocks` 格式 | +| 出库单生成失败 | 缺少 pywin32 或模板文件 | 安装 `pywin32`,确保项目根目录有 `易耗品、出库单.doc` 模板 | +| 差旅发票生成了出库单 | 分类不准确 | 检查发票内容是否包含"高铁票""酒店住宿"等关键词 | + +--- + +## 十二、相关 CLI 命令 + +### 单独填写出库单 + +```bash +uv run python -m src.doc.fill_consumable_doc --csv invoice_summary.csv --doc "易耗品、出库单.doc" +``` + +### 分步执行管道 + +```bash +# 仅发票提取 +uv run python src/main.py --step invoice + +# 仅浏览器填报 +uv run python src/main.py --step submit +``` + +### 启动 Web 服务 + +```bash +uv run python src/web/app.py +# 访问: http://localhost:5000 +``` + +--- + +## 附录:API 文档 + +详细的 API 接口文档见 [API.md](./API.md)。 \ No newline at end of file diff --git a/images/debug_after_add_click.png b/images/debug_after_add_click.png new file mode 100644 index 0000000..682687c Binary files /dev/null and b/images/debug_after_add_click.png differ diff --git a/images/debug_error.png b/images/debug_error.png new file mode 100644 index 0000000..a286725 Binary files /dev/null and b/images/debug_error.png differ diff --git a/images/debug_item_total.png b/images/debug_item_total.png new file mode 100644 index 0000000..54b7f10 Binary files /dev/null and b/images/debug_item_total.png differ diff --git a/images/debug_portal_loaded.png b/images/debug_portal_loaded.png new file mode 100644 index 0000000..276166d Binary files /dev/null and b/images/debug_portal_loaded.png differ diff --git a/images/debug_step3_done.png b/images/debug_step3_done.png new file mode 100644 index 0000000..54b301e Binary files /dev/null and b/images/debug_step3_done.png differ diff --git a/images/debug_step3_project_modal.png b/images/debug_step3_project_modal.png new file mode 100644 index 0000000..0ebdae8 Binary files /dev/null and b/images/debug_step3_project_modal.png differ diff --git a/images/debug_step3_project_selected.png b/images/debug_step3_project_selected.png new file mode 100644 index 0000000..94c3bb5 Binary files /dev/null and b/images/debug_step3_project_selected.png differ diff --git a/images/debug_step5_done.png b/images/debug_step5_done.png new file mode 100644 index 0000000..739a3b8 Binary files /dev/null and b/images/debug_step5_done.png differ diff --git a/images/debug_step6_done.png b/images/debug_step6_done.png new file mode 100644 index 0000000..a11df08 Binary files /dev/null and b/images/debug_step6_done.png differ diff --git a/images/debug_step6_error.png b/images/debug_step6_error.png new file mode 100644 index 0000000..a286725 Binary files /dev/null and b/images/debug_step6_error.png differ diff --git a/invoice_summary.csv b/invoice_summary.csv new file mode 100644 index 0000000..8e180a3 --- /dev/null +++ b/invoice_summary.csv @@ -0,0 +1,4 @@ +序号,发票类型,发票号码,开票日期,项目名称,规格型号,价税合计,销售方名称,出发站,到达站,车次,乘车日期,座位等级,人员姓名,刷卡日期,公务卡号,刷卡金额,备注,工号 +1,高铁票,26349119343000154520,2026/3/23,,,,,阜阳西,无锡东,G7221,2026/3/21,二等座,王建锋,,,,, +2,高铁票,26329166851000278168,2026/3/23,,,,,无锡东,阜阳西,G1826,2026/3/22,二等座,王建锋,,,,, +3,酒店住宿,26322000002199439186,2026/3/23,*住宿服务*住宿费 间 天 1 347.735849056604 347.74 6% 20.86,住宿费 间 天 1 347.735849056604 347.74 6% 20.86,368.6,,,,,,,,,,,, diff --git a/pipeline.log b/pipeline.log new file mode 100644 index 0000000..1099362 --- /dev/null +++ b/pipeline.log @@ -0,0 +1,1536 @@ +2026-06-08 13:31:01 [INFO ] pipeline: ============================================================ +2026-06-08 13:31:01 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:31:01 [INFO ] pipeline: ============================================================ +2026-06-08 13:31:01 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:31:02 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:32:29 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 319 字符 +2026-06-08 13:32:29 [INFO ] llm_extractor: LLM 发票提取成功: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:32:29 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:32:29 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:33:55 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 271 字符 +2026-06-08 13:33:55 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:33:55 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:33:55 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:35:21 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 311 字符 +2026-06-08 13:35:21 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋3.21住宿368.6.pdf +2026-06-08 13:35:21 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 13:35:21 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 13:35:21 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 13:35:21 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 13:38:29 [INFO ] pipeline: ============================================================ +2026-06-08 13:38:29 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:38:29 [INFO ] pipeline: ============================================================ +2026-06-08 13:38:29 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:38:30 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:39:15 [INFO ] pipeline: ============================================================ +2026-06-08 13:39:15 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:39:15 [INFO ] pipeline: ============================================================ +2026-06-08 13:39:15 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:39:16 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:39:26 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 354 字符 +2026-06-08 13:39:26 [INFO ] llm_extractor: LLM 发票提取成功: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:39:26 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:39:26 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:39:35 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 318 字符 +2026-06-08 13:39:35 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:39:35 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:39:35 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:39:46 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 309 字符 +2026-06-08 13:39:46 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋3.21住宿368.6.pdf +2026-06-08 13:39:46 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 13:39:46 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 13:39:46 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 13:39:46 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 13:40:04 [INFO ] pipeline: ============================================================ +2026-06-08 13:40:04 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:40:04 [INFO ] pipeline: ============================================================ +2026-06-08 13:40:04 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:40:05 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:40:13 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 317 字符 +2026-06-08 13:40:13 [INFO ] llm_extractor: LLM 发票提取成功: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:40:13 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:40:13 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:40:23 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 318 字符 +2026-06-08 13:40:23 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:40:23 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:40:23 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:40:34 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 309 字符 +2026-06-08 13:40:34 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋3.21住宿368.6.pdf +2026-06-08 13:40:34 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 13:40:34 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 13:40:34 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 13:40:34 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 13:45:56 [INFO ] pipeline: ============================================================ +2026-06-08 13:45:56 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:45:56 [INFO ] pipeline: ============================================================ +2026-06-08 13:45:56 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:45:57 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:46:05 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 317 字符 +2026-06-08 13:46:05 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000154520", + "开票日期": "2026/3/23", + "项目名称": "高铁票", + "规格型号": "", + "价税合计": "258.00", + "销售方名称": "中国铁路", + "出发站": "阜阳西", + "到达站": "无锡", + "车次": "G7221", + "乘车日期": "2026/3/21", + "座位等级": "二等座", + "人员姓名": "王建锋", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:46:05 [INFO ] llm_extractor: LLM 发票提取成功: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:46:05 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:46:05 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:46:15 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 318 字符 +2026-06-08 13:46:15 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26329166851000278168", + "开票日期": "2026/3/23", + "项目名称": "高铁票", + "规格型号": "", + "价税合计": "259.00", + "销售方名称": "中国铁路", + "出发站": "无锡东", + "到达站": "阜阳西", + "车次": "G1826", + "乘车日期": "2026/3/22", + "座位等级": "二等座", + "人员姓名": "王建锋", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:46:15 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:46:15 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:46:15 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:46:23 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 309 字符 +2026-06-08 13:46:23 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26322000002199439186", + "开票日期": "2026/3/23", + "项目名称": "*住宿服务*住宿费", + "规格型号": "间 天", + "价税合计": "368.60", + "销售方名称": "滨湖区雪浪山庄花园酒店", + "出发站": "", + "到达站": "", + "车次": "", + "乘车日期": "", + "座位等级": "", + "人员姓名": "", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:46:23 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋3.21住宿368.6.pdf +2026-06-08 13:46:23 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 13:46:23 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 13:46:23 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 13:46:23 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 13:47:51 [INFO ] pipeline: ============================================================ +2026-06-08 13:47:51 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:47:51 [INFO ] pipeline: ============================================================ +2026-06-08 13:47:51 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:47:52 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:48:01 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 316 字符 +2026-06-08 13:48:01 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000154520", + "开票日期": "2026/3/23", + "项目名称": "票价", + "规格型号": "", + "价税合计": "258.00", + "销售方名称": "中国铁路", + "出发站": "阜阳西", + "到达站": "无锡", + "车次": "G7221", + "乘车日期": "2026/3/21", + "座位等级": "二等座", + "人员姓名": "王建锋", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:48:01 [INFO ] llm_extractor: LLM 发票提取成功: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:48:01 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:48:01 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:48:09 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 324 字符 +2026-06-08 13:48:09 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26329166851000278168", + "开票日期": "2026/3/23", + "项目名称": "铁路旅客运输服务费", + "规格型号": "", + "价税合计": "259.00", + "销售方名称": "中国铁路", + "出发站": "无锡东", + "到达站": "阜阳西", + "车次": "G1826", + "乘车日期": "2026/3/22", + "座位等级": "二等座", + "人员姓名": "王建锋", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:48:09 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:48:09 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:48:09 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:48:17 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 309 字符 +2026-06-08 13:48:17 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26322000002199439186", + "开票日期": "2026/3/23", + "项目名称": "*住宿服务*住宿费", + "规格型号": "间 天", + "价税合计": "368.60", + "销售方名称": "滨湖区雪浪山庄花园酒店", + "出发站": "", + "到达站": "", + "车次": "", + "乘车日期": "", + "座位等级": "", + "人员姓名": "", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:48:17 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋3.21住宿368.6.pdf +2026-06-08 13:48:17 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 13:48:17 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 13:48:17 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 13:48:17 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 13:53:40 [INFO ] pipeline: ============================================================ +2026-06-08 13:53:40 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:53:40 [INFO ] pipeline: ============================================================ +2026-06-08 13:53:40 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:53:41 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:53:42 [ERROR] llm_extractor: LLM 请求失败: Error code: 400 - {'error': {'message': "'reasoning_effort' must be a string", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}} +2026-06-08 13:53:42 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 王建峰-阜阳西-无锡东258.pdf (Error code: 400 - {'error': {'message': "'reasoning_effort' must be a string", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}}) +2026-06-08 13:53:42 [INFO ] llm_extractor: 正则回退解析完成: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:53:42 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:53:42 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:53:42 [ERROR] llm_extractor: LLM 请求失败: Error code: 400 - {'error': {'message': "'reasoning_effort' must be a string", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}} +2026-06-08 13:53:42 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 王建锋-无锡东-阜阳西259.pdf (Error code: 400 - {'error': {'message': "'reasoning_effort' must be a string", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}}) +2026-06-08 13:53:42 [INFO ] llm_extractor: 正则回退解析完成: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:53:42 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:53:42 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:53:42 [ERROR] llm_extractor: LLM 请求失败: Error code: 400 - {'error': {'message': "'reasoning_effort' must be a string", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}} +2026-06-08 13:53:42 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 王建锋3.21住宿368.6.pdf (Error code: 400 - {'error': {'message': "'reasoning_effort' must be a string", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}}) +2026-06-08 13:53:42 [INFO ] llm_extractor: 正则回退解析完成: 王建锋3.21住宿368.6.pdf +2026-06-08 13:53:42 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 13:53:42 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 13:53:42 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 13:53:42 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 13:54:26 [INFO ] pipeline: ============================================================ +2026-06-08 13:54:26 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:54:26 [INFO ] pipeline: ============================================================ +2026-06-08 13:54:26 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:54:28 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:54:28 [ERROR] llm_extractor: LLM 请求失败: Error code: 400 - {'error': {'message': "Invalid 'reasoning_effort' value: 'on'. Supported values: none, minimal, low, medium, high, xhigh.", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}} +2026-06-08 13:54:28 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 王建峰-阜阳西-无锡东258.pdf (Error code: 400 - {'error': {'message': "Invalid 'reasoning_effort' value: 'on'. Supported values: none, minimal, low, medium, high, xhigh.", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}}) +2026-06-08 13:54:28 [INFO ] llm_extractor: 正则回退解析完成: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:54:28 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:54:28 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:54:28 [ERROR] llm_extractor: LLM 请求失败: Error code: 400 - {'error': {'message': "Invalid 'reasoning_effort' value: 'on'. Supported values: none, minimal, low, medium, high, xhigh.", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}} +2026-06-08 13:54:28 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 王建锋-无锡东-阜阳西259.pdf (Error code: 400 - {'error': {'message': "Invalid 'reasoning_effort' value: 'on'. Supported values: none, minimal, low, medium, high, xhigh.", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}}) +2026-06-08 13:54:28 [INFO ] llm_extractor: 正则回退解析完成: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:54:28 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:54:28 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:54:29 [ERROR] llm_extractor: LLM 请求失败: Error code: 400 - {'error': {'message': "Invalid 'reasoning_effort' value: 'on'. Supported values: none, minimal, low, medium, high, xhigh.", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}} +2026-06-08 13:54:29 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 王建锋3.21住宿368.6.pdf (Error code: 400 - {'error': {'message': "Invalid 'reasoning_effort' value: 'on'. Supported values: none, minimal, low, medium, high, xhigh.", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'invalid_value'}}) +2026-06-08 13:54:29 [INFO ] llm_extractor: 正则回退解析完成: 王建锋3.21住宿368.6.pdf +2026-06-08 13:54:29 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 13:54:29 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 13:54:29 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 13:54:29 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 13:54:42 [INFO ] pipeline: ============================================================ +2026-06-08 13:54:42 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:54:42 [INFO ] pipeline: ============================================================ +2026-06-08 13:54:42 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:54:43 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:54:57 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 317 字符 +2026-06-08 13:54:57 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000154520", + "开票日期": "2026/3/23", + "项目名称": "高铁票", + "规格型号": "", + "价税合计": "258.00", + "销售方名称": "中国铁路", + "出发站": "阜阳西", + "到达站": "无锡", + "车次": "G7221", + "乘车日期": "2026/3/21", + "座位等级": "二等座", + "人员姓名": "王建锋", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:54:57 [INFO ] llm_extractor: LLM 发票提取成功: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:54:57 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 13:54:57 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:55:08 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 318 字符 +2026-06-08 13:55:08 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26329166851000278168", + "开票日期": "2026/3/23", + "项目名称": "高铁票", + "规格型号": "", + "价税合计": "259.00", + "销售方名称": "中国铁路", + "出发站": "无锡东", + "到达站": "阜阳西", + "车次": "G1826", + "乘车日期": "2026/3/22", + "座位等级": "二等座", + "人员姓名": "王建锋", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:55:08 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:55:08 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 13:55:08 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 13:55:17 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 309 字符 +2026-06-08 13:55:17 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26322000002199439186", + "开票日期": "2026/3/23", + "项目名称": "*住宿服务*住宿费", + "规格型号": "间 天", + "价税合计": "368.60", + "销售方名称": "滨湖区雪浪山庄花园酒店", + "出发站": "", + "到达站": "", + "车次": "", + "乘车日期": "", + "座位等级": "", + "人员姓名": "", + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "备注": "", + "工号": "" +} +2026-06-08 13:55:17 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋3.21住宿368.6.pdf +2026-06-08 13:55:17 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 13:55:17 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 13:55:17 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 13:55:17 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 13:56:00 [INFO ] pipeline: ============================================================ +2026-06-08 13:56:00 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 13:56:00 [INFO ] pipeline: ============================================================ +2026-06-08 13:56:00 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 13:56:02 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:09:24 [INFO ] pipeline: ============================================================ +2026-06-08 14:09:24 [INFO ] pipeline: [1/3] 发票提取 +2026-06-08 14:09:24 [INFO ] pipeline: ============================================================ +2026-06-08 14:09:24 [INFO ] extractor: 发现 3 个 PDF 文件 +2026-06-08 14:09:25 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:09:31 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-08 14:09:31 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000154520", + "开票日期": "2026/3/23", + "乘车日期": "2026/3/21", + "出发站": "阜阳西", + "到达站": "无锡东", + "座位等级": "二等座", + "车次": "G7221", + "人员姓名": "王建锋", + "票价": "258.00" +} +2026-06-08 14:09:31 [INFO ] llm_extractor: LLM 发票提取成功: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 14:09:31 [INFO ] extractor: [高铁票] 已解析: 王建峰-阜阳西-无锡东258.pdf +2026-06-08 14:09:31 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:09:35 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-08 14:09:35 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26329166851000278168", + "开票日期": "2026/3/23", + "乘车日期": "2026/3/22", + "出发站": "无锡东", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1826", + "人员姓名": "王建锋", + "票价": "259.00" +} +2026-06-08 14:09:35 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 14:09:35 [INFO ] extractor: [高铁票] 已解析: 王建锋-无锡东-阜阳西259.pdf +2026-06-08 14:09:35 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:09:38 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 95 字符 +2026-06-08 14:09:38 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26322000002199439186", + "开票日期": "2026/3/23", + "价税合计": 368.60 +} +2026-06-08 14:09:38 [INFO ] llm_extractor: LLM 发票提取成功: 王建锋3.21住宿368.6.pdf +2026-06-08 14:09:38 [INFO ] extractor: [酒店住宿] 已解析: 王建锋3.21住宿368.6.pdf +2026-06-08 14:09:38 [INFO ] extractor: 共处理 3 张发票 +2026-06-08 14:09:38 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 14:09:38 [INFO ] pipeline: [1/3] 发票提取 完成 +2026-06-08 14:14:14 [INFO ] extractor: 发现 5 个 PDF 文件 +2026-06-08 14:14:16 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:14:22 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 14:14:22 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331138", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "张国庆", + "票价": "115.50" +} +2026-06-08 14:14:22 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331138-电子发票.pdf +2026-06-08 14:14:22 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-08 14:14:22 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:14:27 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 14:14:27 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000335414", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "王建锋", + "票价": "115.50" +} +2026-06-08 14:14:27 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000335414-电子发票.pdf +2026-06-08 14:14:27 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-08 14:14:27 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:14:32 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 14:14:32 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003550275", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "张国庆", + "票价": "117.50" +} +2026-06-08 14:14:32 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003550275-电子发票.pdf +2026-06-08 14:14:32 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-08 14:14:32 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:14:37 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 14:14:37 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003595208", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "王建锋", + "票价": "117.50" +} +2026-06-08 14:14:37 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003595208-电子发票.pdf +2026-06-08 14:14:37 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-08 14:14:37 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:14:40 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 94 字符 +2026-06-08 14:14:40 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26342000001715702281", + "开票日期": "2026/6/3", + "价税合计": 536.00 +} +2026-06-08 14:14:40 [INFO ] llm_extractor: LLM 发票提取成功: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 14:14:40 [INFO ] extractor: [酒店住宿] 已解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 14:14:40 [INFO ] extractor: 共处理 5 张发票 +2026-06-08 14:14:40 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 14:14:40 [WARNING] llm_extractor: 未找到 PDF-图片配对文件,跳过信息提取 +2026-06-08 14:14:40 [INFO ] fill_consumable_doc: 开始填写出库单: 易耗品、出库单.doc +2026-06-08 14:14:50 [INFO ] fill_consumable_doc: 出库单填写完成 +2026-06-08 14:50:57 [INFO ] extractor: 发现 5 个 PDF 文件 +2026-06-08 14:50:59 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:51:13 [ERROR] llm_extractor: LLM 请求失败: Error code: 502 +2026-06-08 14:51:13 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 26349119343000331138-电子发票.pdf (Error code: 502) +2026-06-08 14:51:13 [INFO ] llm_extractor: 正则回退解析完成: 26349119343000331138-电子发票.pdf +2026-06-08 14:51:13 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-08 14:51:13 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:51:26 [ERROR] llm_extractor: LLM 请求失败: Error code: 502 +2026-06-08 14:51:26 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 26349119343000335414-电子发票.pdf (Error code: 502) +2026-06-08 14:51:26 [INFO ] llm_extractor: 正则回退解析完成: 26349119343000335414-电子发票.pdf +2026-06-08 14:51:26 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-08 14:51:26 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:51:39 [ERROR] llm_extractor: LLM 请求失败: Error code: 502 +2026-06-08 14:51:39 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 26349119423003550275-电子发票.pdf (Error code: 502) +2026-06-08 14:51:39 [INFO ] llm_extractor: 正则回退解析完成: 26349119423003550275-电子发票.pdf +2026-06-08 14:51:39 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-08 14:51:39 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:53:56 [INFO ] extractor: 发现 5 个 PDF 文件 +2026-06-08 14:53:58 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:54:12 [ERROR] llm_extractor: LLM 请求失败: Error code: 502 +2026-06-08 14:54:12 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 26349119343000331138-电子发票.pdf (Error code: 502) +2026-06-08 14:54:12 [INFO ] llm_extractor: 正则回退解析完成: 26349119343000331138-电子发票.pdf +2026-06-08 14:54:12 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-08 14:54:12 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:54:25 [ERROR] llm_extractor: LLM 请求失败: Error code: 502 +2026-06-08 14:54:25 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 26349119343000335414-电子发票.pdf (Error code: 502) +2026-06-08 14:54:25 [INFO ] llm_extractor: 正则回退解析完成: 26349119343000335414-电子发票.pdf +2026-06-08 14:54:25 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-08 14:54:25 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:54:38 [ERROR] llm_extractor: LLM 请求失败: Error code: 502 +2026-06-08 14:54:38 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 26349119423003550275-电子发票.pdf (Error code: 502) +2026-06-08 14:54:38 [INFO ] llm_extractor: 正则回退解析完成: 26349119423003550275-电子发票.pdf +2026-06-08 14:54:38 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-08 14:54:38 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:54:52 [ERROR] llm_extractor: LLM 请求失败: Error code: 502 +2026-06-08 14:54:52 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: 26349119423003595208-电子发票.pdf (Error code: 502) +2026-06-08 14:54:52 [INFO ] llm_extractor: 正则回退解析完成: 26349119423003595208-电子发票.pdf +2026-06-08 14:54:52 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-08 14:54:52 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://127.0.0.1:1234/v1) +2026-06-08 14:55:39 [INFO ] extractor: 发现 5 个 PDF 文件 +2026-06-08 14:55:41 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 14:55:54 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 14:55:54 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331138", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "张国庆", + "票价": "115.50" +} +2026-06-08 14:55:54 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331138-电子发票.pdf +2026-06-08 14:55:54 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-08 14:55:54 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 14:56:02 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 14:56:02 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000335414", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "王建锋", + "票价": "115.50" +} +2026-06-08 14:56:02 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000335414-电子发票.pdf +2026-06-08 14:56:02 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-08 14:56:02 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 14:56:11 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 14:56:11 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003550275", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "张国庆", + "票价": "117.50" +} +2026-06-08 14:56:11 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003550275-电子发票.pdf +2026-06-08 14:56:11 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-08 14:56:11 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 14:56:25 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 14:56:25 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003595208", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "王建锋", + "票价": "117.50" +} +2026-06-08 14:56:25 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003595208-电子发票.pdf +2026-06-08 14:56:25 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-08 14:56:25 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 14:56:31 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 94 字符 +2026-06-08 14:56:31 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26342000001715702281", + "开票日期": "2026/6/3", + "价税合计": 536.00 +} +2026-06-08 14:56:31 [INFO ] llm_extractor: LLM 发票提取成功: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 14:56:31 [INFO ] extractor: [酒店住宿] 已解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 14:56:31 [INFO ] extractor: 共处理 5 张发票 +2026-06-08 14:56:31 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 14:56:31 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 5 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 14:56:31 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 14:56:31 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145318_279_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145319_280_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145320_281_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145321_282_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145323_283_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 14:56:49 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145318_279_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145319_280_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145320_281_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145321_282_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 14:56:49 [WARNING] llm_extractor: LLM 多模态提取失败,回退到 OCR+正则: 微信图片_20260608145323_283_42.jpg (cannot import name 'ImageDocument' from 'llama_index.core.llms' (D:\阜阳师范大学\财务报销\自动报销系统\.venv\Lib\site-packages\llama_index\core\llms\__init__.py)) +2026-06-08 14:56:49 [ERROR] llm_extractor: OCR 回退提取失败: Engine 'paddle_static' is unavailable because dependency 'paddlepaddle' is not installed. +2026-06-08 14:56:49 [INFO ] llm_extractor: LLM 提取完成: 匹配 5/5 行,更新 0 个字段 +2026-06-08 14:56:49 [INFO ] fill_consumable_doc: 开始填写出库单: 易耗品、出库单.doc +2026-06-08 14:56:57 [INFO ] fill_consumable_doc: 出库单填写完成 +2026-06-08 15:13:59 [INFO ] extractor: 发现 5 个 PDF 文件 +2026-06-08 15:14:01 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 15:14:11 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 15:14:11 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331138", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "张国庆", + "票价": "115.50" +} +2026-06-08 15:14:11 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331138-电子发票.pdf +2026-06-08 15:14:11 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-08 15:14:11 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 15:14:21 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 15:14:21 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000335414", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "王建锋", + "票价": "115.50" +} +2026-06-08 15:14:21 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000335414-电子发票.pdf +2026-06-08 15:14:21 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-08 15:14:21 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 15:14:30 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 15:14:30 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003550275", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "张国庆", + "票价": "117.50" +} +2026-06-08 15:14:30 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003550275-电子发票.pdf +2026-06-08 15:14:30 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-08 15:14:30 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 15:14:39 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 15:14:39 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003595208", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "王建锋", + "票价": "117.50" +} +2026-06-08 15:14:39 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003595208-电子发票.pdf +2026-06-08 15:14:39 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-08 15:14:39 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 15:14:45 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 96 字符 +2026-06-08 15:14:45 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26342000001715702281", + "开票日期": "2026/6/3", + "价税合计": "536.00" +} +2026-06-08 15:14:45 [INFO ] llm_extractor: LLM 发票提取成功: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 15:14:45 [INFO ] extractor: [酒店住宿] 已解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 15:14:45 [INFO ] extractor: 共处理 5 张发票 +2026-06-08 15:14:45 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 15:14:45 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 5 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 15:14:46 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 15:16:20 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145318_279_42.jpg +2026-06-08 15:16:20 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 15:17:35 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145319_280_42.jpg +2026-06-08 15:17:35 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 15:19:04 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145320_281_42.jpg +2026-06-08 15:19:04 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 15:20:14 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145321_282_42.jpg +2026-06-08 15:20:14 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 15:21:11 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145323_283_42.jpg +2026-06-08 15:21:11 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 15:21:11 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 15:21:11 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 15:22:26 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145318_279_42.jpg +2026-06-08 15:22:26 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 15:23:46 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145319_280_42.jpg +2026-06-08 15:23:46 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 15:24:24 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145320_281_42.jpg +2026-06-08 15:24:24 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 16:01:24 [INFO ] extractor: 发现 7 个 PDF 文件 +2026-06-08 16:01:26 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:01:37 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:01:37 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331138", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "张国庆", + "票价": "115.50" +} +2026-06-08 16:01:37 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331138-电子发票.pdf +2026-06-08 16:01:37 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-08 16:01:37 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:01:45 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:01:45 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331314", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "一等座", + "车次": "G1967", + "人员姓名": "陈曙光", + "票价": "167.00" +} +2026-06-08 16:01:45 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331314-电子发票.pdf +2026-06-08 16:01:45 [INFO ] extractor: [高铁票] 已解析: 26349119343000331314-电子发票.pdf +2026-06-08 16:01:45 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:01:54 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:01:54 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000335414", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "王建锋", + "票价": "115.50" +} +2026-06-08 16:01:54 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000335414-电子发票.pdf +2026-06-08 16:01:54 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-08 16:01:54 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:02:02 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:02:02 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003550275", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "张国庆", + "票价": "117.50" +} +2026-06-08 16:02:02 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003550275-电子发票.pdf +2026-06-08 16:02:02 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-08 16:02:03 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:02:10 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:02:10 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003552366", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "一等座", + "车次": "G1318", + "人员姓名": "陈曙光", + "票价": "189.50" +} +2026-06-08 16:02:10 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003552366-电子发票.pdf +2026-06-08 16:02:10 [INFO ] extractor: [高铁票] 已解析: 26349119423003552366-电子发票.pdf +2026-06-08 16:02:11 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:02:19 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:02:19 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003595208", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "王建锋", + "票价": "117.50" +} +2026-06-08 16:02:19 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003595208-电子发票.pdf +2026-06-08 16:02:19 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-08 16:02:19 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:02:23 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 94 字符 +2026-06-08 16:02:23 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26342000001715702281", + "开票日期": "2026/6/3", + "价税合计": 536.00 +} +2026-06-08 16:02:23 [INFO ] llm_extractor: LLM 发票提取成功: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 16:02:23 [INFO ] extractor: [酒店住宿] 已解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 16:02:23 [INFO ] extractor: 共处理 7 张发票 +2026-06-08 16:02:23 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 16:02:23 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 16:02:24 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:02:26 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145318_279_42.jpg +2026-06-08 16:02:26 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 16:02:28 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145319_280_42.jpg +2026-06-08 16:02:28 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 16:02:29 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145320_281_42.jpg +2026-06-08 16:02:29 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 16:02:31 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145321_282_42.jpg +2026-06-08 16:02:31 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 16:02:33 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145323_283_42.jpg +2026-06-08 16:02:33 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 16:02:33 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 16:02:34 [INFO ] llm_extractor: LLM 提取完成: 匹配 5/7 行,更新 0 个字段 +2026-06-08 16:02:34 [INFO ] fill_consumable_doc: 开始填写出库单: 易耗品、出库单.doc +2026-06-08 16:02:43 [INFO ] fill_consumable_doc: 出库单填写完成 +2026-06-08 16:22:03 [INFO ] extractor: 发现 7 个 PDF 文件 +2026-06-08 16:22:05 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:22:13 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:22:13 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331138", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "张国庆", + "票价": "115.50" +} +2026-06-08 16:22:13 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331138-电子发票.pdf +2026-06-08 16:22:13 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-08 16:22:13 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:22:19 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:22:19 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331314", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "一等座", + "车次": "G1967", + "人员姓名": "陈曙光", + "票价": "167.00" +} +2026-06-08 16:22:19 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331314-电子发票.pdf +2026-06-08 16:22:19 [INFO ] extractor: [高铁票] 已解析: 26349119343000331314-电子发票.pdf +2026-06-08 16:22:20 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:22:27 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:22:27 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000335414", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "王建锋", + "票价": "115.50" +} +2026-06-08 16:22:27 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000335414-电子发票.pdf +2026-06-08 16:22:27 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-08 16:22:27 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:22:34 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:22:34 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003550275", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "张国庆", + "票价": "117.50" +} +2026-06-08 16:22:34 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003550275-电子发票.pdf +2026-06-08 16:22:34 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-08 16:22:34 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:22:41 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:22:41 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003552366", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "一等座", + "车次": "G1318", + "人员姓名": "陈曙光", + "票价": "189.50" +} +2026-06-08 16:22:41 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003552366-电子发票.pdf +2026-06-08 16:22:41 [INFO ] extractor: [高铁票] 已解析: 26349119423003552366-电子发票.pdf +2026-06-08 16:22:42 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:22:49 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:22:49 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003595208", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "王建锋", + "票价": "117.50" +} +2026-06-08 16:22:49 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003595208-电子发票.pdf +2026-06-08 16:22:49 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-08 16:22:49 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:22:53 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 94 字符 +2026-06-08 16:22:53 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26342000001715702281", + "开票日期": "2026/6/3", + "价税合计": 536.00 +} +2026-06-08 16:22:53 [INFO ] llm_extractor: LLM 发票提取成功: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 16:22:53 [INFO ] extractor: [酒店住宿] 已解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 16:22:53 [INFO ] extractor: 共处理 7 张发票 +2026-06-08 16:22:53 [INFO ] extractor: 差旅发票: 7 张, 普通发票: 0 张 +2026-06-08 16:22:53 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 16:22:53 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 16:22:54 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:22:56 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145318_279_42.jpg +2026-06-08 16:22:56 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 16:22:58 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145319_280_42.jpg +2026-06-08 16:22:58 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 16:22:59 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145320_281_42.jpg +2026-06-08 16:22:59 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 16:23:01 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145321_282_42.jpg +2026-06-08 16:23:01 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 16:23:03 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145323_283_42.jpg +2026-06-08 16:23:03 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 16:23:03 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 16:23:03 [INFO ] llm_extractor: LLM 提取完成: 匹配 5/7 行,更新 0 个字段 +2026-06-08 16:23:03 [INFO ] fill_consumable_doc: 纯差旅发票,跳过易耗品出库单生成 +2026-06-08 16:37:34 [INFO ] extractor: 发现 7 个 PDF 文件 +2026-06-08 16:37:36 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:37:45 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:37:45 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331138", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "张国庆", + "票价": "115.50" +} +2026-06-08 16:37:45 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331138-电子发票.pdf +2026-06-08 16:37:45 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-08 16:37:45 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:37:51 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:37:51 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331314", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "一等座", + "车次": "G1967", + "人员姓名": "陈曙光", + "票价": "167.00" +} +2026-06-08 16:37:51 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331314-电子发票.pdf +2026-06-08 16:37:51 [INFO ] extractor: [高铁票] 已解析: 26349119343000331314-电子发票.pdf +2026-06-08 16:37:51 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:37:58 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:37:58 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000335414", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "王建锋", + "票价": "115.50" +} +2026-06-08 16:37:58 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000335414-电子发票.pdf +2026-06-08 16:37:58 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-08 16:37:58 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:38:04 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:38:04 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003550275", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "张国庆", + "票价": "117.50" +} +2026-06-08 16:38:04 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003550275-电子发票.pdf +2026-06-08 16:38:04 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-08 16:38:04 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:38:11 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:38:11 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003552366", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "一等座", + "车次": "G1318", + "人员姓名": "陈曙光", + "票价": "189.50" +} +2026-06-08 16:38:11 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003552366-电子发票.pdf +2026-06-08 16:38:11 [INFO ] extractor: [高铁票] 已解析: 26349119423003552366-电子发票.pdf +2026-06-08 16:38:11 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:38:19 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 198 字符 +2026-06-08 16:38:19 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003595208", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "王建锋", + "票价": "117.50" +} +2026-06-08 16:38:19 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003595208-电子发票.pdf +2026-06-08 16:38:19 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-08 16:38:19 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1) +2026-06-08 16:38:23 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 96 字符 +2026-06-08 16:38:23 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26342000001715702281", + "开票日期": "2026/6/3", + "价税合计": "536.00" +} +2026-06-08 16:38:23 [INFO ] llm_extractor: LLM 发票提取成功: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 16:38:23 [INFO ] extractor: [酒店住宿] 已解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-08 16:38:23 [INFO ] extractor: 共处理 7 张发票 +2026-06-08 16:38:23 [INFO ] extractor: 差旅发票: 7 张, 普通发票: 0 张 +2026-06-08 16:38:23 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-08 16:38:23 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 16:38:24 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:38:25 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145318_279_42.jpg +2026-06-08 16:38:25 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 16:38:27 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145319_280_42.jpg +2026-06-08 16:38:27 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 16:38:30 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145320_281_42.jpg +2026-06-08 16:38:30 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 16:38:38 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145321_282_42.jpg +2026-06-08 16:38:38 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 16:38:39 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145323_283_42.jpg +2026-06-08 16:38:39 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 16:38:39 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 16:38:39 [INFO ] llm_extractor: PDF=26349119343000331138-电子发票.pdf, 发票号=26349119343000331138, 刷卡信息={'人员姓名': '', '刷卡日期': '', '公务卡号': '', '刷卡金额': ''} +2026-06-08 16:38:39 [INFO ] llm_extractor: PDF=26349119343000331314-电子发票.pdf, 发票号=26349119343000331314, 刷卡信息={'人员姓名': '', '刷卡日期': '', '公务卡号': '', '刷卡金额': ''} +2026-06-08 16:38:40 [INFO ] llm_extractor: PDF=26349119343000335414-电子发票.pdf, 发票号=26349119343000335414, 刷卡信息={'人员姓名': '', '刷卡日期': '', '公务卡号': '', '刷卡金额': ''} +2026-06-08 16:38:40 [INFO ] llm_extractor: PDF=26349119423003550275-电子发票.pdf, 发票号=26349119423003550275, 刷卡信息={'人员姓名': '', '刷卡日期': '', '公务卡号': '', '刷卡金额': ''} +2026-06-08 16:38:40 [INFO ] llm_extractor: PDF=26349119423003552366-电子发票.pdf, 发票号=26349119423003552366, 刷卡信息={'人员姓名': '', '刷卡日期': '', '公务卡号': '', '刷卡金额': ''} +2026-06-08 16:38:40 [INFO ] llm_extractor: LLM 提取完成: 匹配 5/7 行,更新 0 个字段 +2026-06-08 16:38:40 [INFO ] fill_consumable_doc: 纯差旅发票,跳过易耗品出库单生成 +2026-06-08 16:45:50 [INFO ] pipeline: ============================================================ +2026-06-08 16:45:50 [INFO ] pipeline: [2/3] LLM 信息提取 +2026-06-08 16:45:50 [INFO ] pipeline: ============================================================ +2026-06-08 16:45:50 [WARNING] llm_extractor: 未找到 PDF-图片配对文件,跳过信息提取 +2026-06-08 16:45:50 [INFO ] pipeline: [2/3] LLM 信息提取 完成 +2026-06-08 16:48:56 [INFO ] pipeline: ============================================================ +2026-06-08 16:48:56 [INFO ] pipeline: [2/3] LLM 信息提取 +2026-06-08 16:48:56 [INFO ] pipeline: ============================================================ +2026-06-08 16:48:56 [WARNING] llm_extractor: 未找到 PDF-图片配对文件,跳过信息提取 +2026-06-08 16:48:56 [INFO ] pipeline: [2/3] LLM 信息提取 完成 +2026-06-08 16:54:29 [INFO ] pipeline: ============================================================ +2026-06-08 16:54:29 [INFO ] pipeline: [2/3] LLM 信息提取 +2026-06-08 16:54:29 [INFO ] pipeline: ============================================================ +2026-06-08 16:54:29 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 16:54:30 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:54:31 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:54:31 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 16:54:31 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145319_280_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:54:31 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 16:54:31 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145320_281_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:54:31 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 16:54:31 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145321_282_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...//Z', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:54:31 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 16:54:31 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145323_283_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:54:31 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 16:54:31 [INFO ] llm_extractor: 在 uploads 会话目录找到配对: 179d55ea2a95 +2026-06-08 16:54:31 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 16:54:31 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:54:31 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:55:39 [INFO ] pipeline: ============================================================ +2026-06-08 16:55:39 [INFO ] pipeline: [2/3] LLM 信息提取 +2026-06-08 16:55:39 [INFO ] pipeline: ============================================================ +2026-06-08 16:55:39 [INFO ] pipeline: 使用 uploads 目录: 71dc1d795be5 +2026-06-08 16:55:39 [WARNING] llm_extractor: 未找到 PDF-图片配对文件,跳过信息提取 +2026-06-08 16:55:39 [INFO ] pipeline: [2/3] LLM 信息提取 完成 +2026-06-08 16:56:22 [INFO ] pipeline: ============================================================ +2026-06-08 16:56:22 [INFO ] pipeline: [2/3] LLM 信息提取 +2026-06-08 16:56:22 [INFO ] pipeline: ============================================================ +2026-06-08 16:56:22 [INFO ] pipeline: 使用 uploads 目录: 179d55ea2a95 +2026-06-08 16:56:22 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 16:56:23 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:56:24 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:56:24 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 16:56:24 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145319_280_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:56:24 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 16:56:24 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145320_281_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:56:24 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 16:56:24 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145321_282_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...//Z', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:56:24 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 16:56:24 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145323_283_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:56:24 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 16:56:24 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 16:56:24 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:56:24 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:59:20 [INFO ] pipeline: ============================================================ +2026-06-08 16:59:20 [INFO ] pipeline: [2/3] LLM 信息提取 +2026-06-08 16:59:20 [INFO ] pipeline: ============================================================ +2026-06-08 16:59:20 [INFO ] pipeline: 使用 uploads 目录: 179d55ea2a95 +2026-06-08 16:59:20 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 16:59:21 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:59:21 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:59:21 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 16:59:21 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145319_280_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:59:21 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 16:59:21 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145320_281_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:59:21 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 16:59:21 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145321_282_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...//Z', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:59:21 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 16:59:21 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145323_283_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 16:59:21 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 16:59:21 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 16:59:21 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 16:59:21 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 17:00:34 [INFO ] pipeline: ============================================================ +2026-06-08 17:00:34 [INFO ] pipeline: [2/3] LLM 信息提取 +2026-06-08 17:00:34 [INFO ] pipeline: ============================================================ +2026-06-08 17:00:34 [INFO ] pipeline: 使用 uploads 目录: 179d55ea2a95 +2026-06-08 17:00:34 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 17:00:35 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 17:00:35 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 17:00:35 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 17:00:35 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145319_280_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 17:00:35 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 17:00:35 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145320_281_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 17:00:35 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 17:00:35 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145321_282_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...//Z', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 17:00:35 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 17:00:35 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145323_283_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 17:00:35 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 17:00:35 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 17:00:35 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 17:00:35 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (2 validation errors for ChatMessage +blocks.0 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'text', 'text': ...提取刷卡信息。'}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found +blocks.1 + Unable to extract tag using discriminator 'block_type' [type=union_tag_not_found, input_value={'type': 'image_url', 'im...9k=', 'detail': 'high'}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.13/v/union_tag_not_found) +2026-06-08 17:03:44 [INFO ] pipeline: ============================================================ +2026-06-08 17:03:44 [INFO ] pipeline: [2/3] LLM 信息提取 +2026-06-08 17:03:44 [INFO ] pipeline: ============================================================ +2026-06-08 17:03:44 [INFO ] pipeline: 使用 uploads 目录: 179d55ea2a95 +2026-06-08 17:03:44 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-08 17:03:45 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-08 17:03:54 [INFO ] llm_extractor: LLM 多模态原始响应: { + "人员姓名": "", + "刷卡日期": "2026/6/1", + "公务卡号": "6282****1682", + "刷卡金额": "231.00" +} +2026-06-08 17:03:54 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145318_279_42.jpg, 结果: {'人员姓名': '', '刷卡日期': '2026/6/1', '公务卡号': '6282****1682', '刷卡金额': '231.00'} +2026-06-08 17:03:54 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-08 17:04:02 [INFO ] llm_extractor: LLM 多模态原始响应: { + "人员姓名": "", + "刷卡日期": "2026/6/1", + "公务卡号": "6282****1682", + "刷卡金额": "167.00" +} +2026-06-08 17:04:02 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145319_280_42.jpg, 结果: {'人员姓名': '', '刷卡日期': '2026/6/1', '公务卡号': '6282****1682', '刷卡金额': '167.00'} +2026-06-08 17:04:02 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-08 17:04:09 [INFO ] llm_extractor: LLM 多模态原始响应: { + "人员姓名": "", + "刷卡日期": "2026/6/2", + "公务卡号": "6282****1682", + "刷卡金额": "535.86" +} +2026-06-08 17:04:09 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145320_281_42.jpg, 结果: {'人员姓名': '', '刷卡日期': '2026/6/2', '公务卡号': '6282****1682', '刷卡金额': '535.86'} +2026-06-08 17:04:09 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-08 17:04:16 [INFO ] llm_extractor: LLM 多模态原始响应: { + "人员姓名": "", + "刷卡日期": "2026/6/3", + "公务卡号": "6282****1682", + "刷卡金额": "189.50" +} +2026-06-08 17:04:16 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145321_282_42.jpg, 结果: {'人员姓名': '', '刷卡日期': '2026/6/3', '公务卡号': '6282****1682', '刷卡金额': '189.50'} +2026-06-08 17:04:16 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen/qwen3.6-27b, base=http://100.123.83.113:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-08 17:04:26 [INFO ] llm_extractor: LLM 多模态原始响应: { + "人员姓名": "", + "刷卡日期": "2026/6/3", + "公务卡号": "6282****1682", + "刷卡金额": "235.00" +} +2026-06-08 17:04:26 [INFO ] llm_extractor: LLM 刷卡信息提取成功: 微信图片_20260608145323_283_42.jpg, 结果: {'人员姓名': '', '刷卡日期': '2026/6/3', '公务卡号': '6282****1682', '刷卡金额': '235.00'} +2026-06-08 17:04:26 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-08 17:04:26 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-08 17:04:26 [INFO ] llm_extractor: PDF=dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf, 发票号=26342000001715702281, 刷卡信息={'人员姓名': '', '刷卡日期': '2026/6/2', '公务卡号': '6282****1682', '刷卡金额': '535.86'} +2026-06-08 17:04:26 [INFO ] llm_extractor: PDF=26349119343000331138-电子发票.pdf, 发票号=26349119343000331138, 刷卡信息={'人员姓名': '', '刷卡日期': '2026/6/1', '公务卡号': '6282****1682', '刷卡金额': '231.00'} +2026-06-08 17:04:26 [INFO ] llm_extractor: PDF=26349119343000331314-电子发票.pdf, 发票号=26349119343000331314, 刷卡信息={'人员姓名': '', '刷卡日期': '2026/6/1', '公务卡号': '6282****1682', '刷卡金额': '167.00'} +2026-06-08 17:04:26 [INFO ] llm_extractor: PDF=26349119343000335414-电子发票.pdf, 发票号=26349119343000335414, 刷卡信息={'人员姓名': '', '刷卡日期': '2026/6/3', '公务卡号': '6282****1682', '刷卡金额': '189.50'} +2026-06-08 17:04:26 [INFO ] llm_extractor: PDF=26349119423003550275-电子发票.pdf, 发票号=26349119423003550275, 刷卡信息={'人员姓名': '', '刷卡日期': '2026/6/3', '公务卡号': '6282****1682', '刷卡金额': '235.00'} +2026-06-08 17:04:26 [INFO ] llm_extractor: LLM 提取完成: 匹配 5/7 行,更新 15 个字段 +2026-06-08 17:04:26 [INFO ] pipeline: [2/3] LLM 信息提取 完成 +2026-06-09 11:20:44 [INFO ] extractor: 发现 7 个 PDF 文件 +2026-06-09 11:20:52 [INFO ] llm_extractor: 开始请求 LLM (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 11:21:28 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 11:21:28 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331138", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "张国庆", + "票价": "115.50" +} + + +2026-06-09 11:21:28 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331138-电子发票.pdf +2026-06-09 11:21:28 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-09 11:21:28 [INFO ] llm_extractor: 开始请求 LLM (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 11:22:02 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 11:22:02 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331314", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "一等座", + "车次": "G1967", + "人员姓名": "陈曙光", + "票价": "167.00" +} + + +2026-06-09 11:22:02 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331314-电子发票.pdf +2026-06-09 11:22:02 [INFO ] extractor: [高铁票] 已解析: 26349119343000331314-电子发票.pdf +2026-06-09 11:22:02 [INFO ] llm_extractor: 开始请求 LLM (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 11:22:36 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 11:22:36 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000335414", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "王建锋", + "票价": "115.50" +} + + +2026-06-09 11:22:36 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000335414-电子发票.pdf +2026-06-09 11:22:36 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-09 11:22:36 [INFO ] llm_extractor: 开始请求 LLM (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 11:23:09 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 11:23:09 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003550275", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "张国庆", + "票价": "117.50" +} + + +2026-06-09 11:23:09 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003550275-电子发票.pdf +2026-06-09 11:23:09 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-09 11:23:09 [INFO ] llm_extractor: 开始请求 LLM (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 11:23:42 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 11:23:42 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003552366", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "一等座", + "车次": "G1318", + "人员姓名": "陈曙光", + "票价": "189.50" +} + + +2026-06-09 11:23:42 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003552366-电子发票.pdf +2026-06-09 11:23:42 [INFO ] extractor: [高铁票] 已解析: 26349119423003552366-电子发票.pdf +2026-06-09 11:23:42 [INFO ] llm_extractor: 开始请求 LLM (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 11:24:15 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 11:24:15 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003595208", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "王建锋", + "票价": "117.50" +} + + +2026-06-09 11:24:15 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003595208-电子发票.pdf +2026-06-09 11:24:15 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-09 11:24:15 [INFO ] llm_extractor: 开始请求 LLM (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 11:24:45 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 154 字符 +2026-06-09 11:24:45 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "普通发票", + "发票号码": "26342000001715702281", + "开票日期": "2026/6/3", + "项目名称": "*生产生活服务*住宿服务", + "规格型号": "", + "价税合计": "536.00", + "销售方名称": "安徽栢景酒店 +2026-06-09 11:24:45 [WARNING] llm_extractor: LLM 发票提取失败,回退到正则解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf (Unterminated string starting at: line 8 column 12 (char 147)) +2026-06-09 11:24:45 [INFO ] llm_extractor: 正则回退解析完成: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-09 11:24:45 [INFO ] extractor: [普通发票] 已解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-09 11:24:45 [INFO ] extractor: 共处理 7 张发票 +2026-06-09 11:24:45 [INFO ] extractor: 差旅发票: 6 张, 普通发票: 1 张 +2026-06-09 11:24:45 [INFO ] extractor: CSV 已保存: invoice_summary.csv +2026-06-09 11:24:45 [INFO ] llm_extractor: 文件名匹配 0 组,剩余 7 个 PDF、5 张图片,尝试金额匹配... +2026-06-09 11:24:45 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-09 11:24:47 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (request (2067 tokens) exceeds the available context size (768 tokens), try increasing it) +2026-06-09 11:24:47 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1, image=微信图片_20260608145319_280_42.jpg) +2026-06-09 11:24:48 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145319_280_42.jpg (request (2067 tokens) exceeds the available context size (768 tokens), try increasing it) +2026-06-09 11:24:48 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1, image=微信图片_20260608145320_281_42.jpg) +2026-06-09 11:24:50 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145320_281_42.jpg (request (2067 tokens) exceeds the available context size (768 tokens), try increasing it) +2026-06-09 11:24:50 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1, image=微信图片_20260608145321_282_42.jpg) +2026-06-09 11:24:51 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145321_282_42.jpg (request (2067 tokens) exceeds the available context size (768 tokens), try increasing it) +2026-06-09 11:24:51 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1, image=微信图片_20260608145323_283_42.jpg) +2026-06-09 11:24:52 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145323_283_42.jpg (request (2067 tokens) exceeds the available context size (768 tokens), try increasing it) +2026-06-09 11:24:52 [INFO ] llm_extractor: 金额匹配完成,共 5 组配对 +2026-06-09 11:24:52 [INFO ] llm_extractor: 找到 5 组 PDF-图片配对 +2026-06-09 11:24:52 [INFO ] llm_extractor: 开始多模态 LLM 请求 (model=qwen3.5-9b, base=http://100.123.83.115:1234/v1, image=微信图片_20260608145318_279_42.jpg) +2026-06-09 11:24:54 [ERROR] llm_extractor: LLM 多模态提取失败: 微信图片_20260608145318_279_42.jpg (request (2067 tokens) exceeds the available context size (768 tokens), try increasing it) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..40a0278 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,49 @@ +[project] +name = "auto-reimbursement-system" +version = "0.1.0" +description = "财务报销自动化系统" +requires-python = ">=3.12" +dependencies = [ + "flask>=3.0", + "playwright>=1.40", + "pdfplumber>=0.10", + "pywin32>=306", + "llama-index>=0.12.0", + "llama-index-llms-openai-like==0.7.2", +] + +[dependency-groups] +dev = [ + "pytest>=8.0", + "pytest-cov>=5.0", + "ruff>=0.9", + "mypy>=1.14", + "deptry>=0.22", + "pre-commit>=4.0", +] + +[tool.ruff] +target-version = "py312" +line-length = 120 + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "N", "UP", "B"] +ignore = ["E501"] + +[tool.mypy] +python_version = "3.12" +strict = true +warn_return_any = true +warn_unused_configs = true +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "tests.*" +ignore_errors = true + +[tool.deptry] +ignore_notebooks = true + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["."] \ No newline at end of file diff --git a/app/__init__.py b/src/__init__.py similarity index 67% rename from app/__init__.py rename to src/__init__.py index 8c1e837..889b183 100644 --- a/app/__init__.py +++ b/src/__init__.py @@ -1,5 +1,6 @@ """财务报销自动化工具包""" +import datetime import io import logging import sys @@ -7,14 +8,20 @@ from pathlib import Path _LOG_FMT = "%(asctime)s [%(levelname)-5s] %(name)s: %(message)s" _LOG_DATE_FMT = "%Y-%m-%d %H:%M:%S" -_LOG_FILE = Path(__file__).resolve().parent.parent / "pipeline.log" +_LOG_DIR = Path(__file__).resolve().parent.parent / "logs" + + +def _get_log_file() -> Path: + """返回当日日志文件路径,如 logs/2026-06-09.log""" + _LOG_DIR.mkdir(parents=True, exist_ok=True) + return _LOG_DIR / f"{datetime.date.today():%Y-%m-%d}.log" def get_logger(name: str) -> logging.Logger: """获取带时间戳的日志记录器 输出格式: 2026-05-24 12:34:56 [INFO ] extractor: 扫描目录: ... - 日志同时输出到终端和项目根目录的 pipeline.log + 日志同时输出到终端和 logs/<日期>.log """ logger = logging.getLogger(name) if not logger.handlers: @@ -28,8 +35,8 @@ def get_logger(name: str) -> logging.Logger: logger.addHandler(stream_handler) # 文件输出 - file_handler = logging.FileHandler(str(_LOG_FILE), encoding="utf-8") + file_handler = logging.FileHandler(str(_get_log_file()), encoding="utf-8") file_handler.setFormatter(formatter) logger.addHandler(file_handler) - return logger \ No newline at end of file + return logger diff --git a/app/bot.py b/src/bot.py similarity index 80% rename from app/bot.py rename to src/bot.py index 4af1b12..a3bc1eb 100644 --- a/app/bot.py +++ b/src/bot.py @@ -10,6 +10,7 @@ import csv from pathlib import Path +from typing import Any from . import get_logger @@ -20,6 +21,7 @@ log = get_logger("bot") # 日期格式化 # ------------------------------------------------------------------ + def _format_date(date_str: str) -> str: """将 '2026/5/13' 或 '2026-5-13' 转为 '2026-05-13'""" if not date_str: @@ -30,31 +32,58 @@ def _format_date(date_str: str) -> str: return date_str +def _safe_float(value: str | None, default: float = 0.0) -> float: + """安全转换为浮点数,空值或转换失败时返回默认值""" + if value is None or str(value).strip() == "": + return default + try: + return float(value) + except (ValueError, TypeError): + return default + + # ------------------------------------------------------------------ # CSV 数据加载 # ------------------------------------------------------------------ -def load_invoice_data(csv_path: str, config: dict) -> list[dict]: - """从 CSV 加载发票数据,自动补全空白字段的默认值""" + +def load_invoice_data(csv_path: str, config: dict[str, str | Path]) -> list[dict[str, str | float | Any | Path]]: + """从 CSV 加载发票数据,自动补全空白字段的默认值 + + CSV 以支付记录为主键,每行包含 _invoices_json 字段(JSON 序列化的发票列表)。 + 本函数还原为发票级别的数据列表。 + """ + import json + invoices = [] with open(csv_path, encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: - invoices.append({ - "seq": row.get("序号", ""), - "invoice_no": row.get("发票号码", ""), - "invoice_date": row.get("开票日期", ""), - "item_name": row.get("项目名称", ""), - "spec_model": row.get("规格型号", ""), - "total_amount": float(row.get("价税合计", 0)), - "seller_name": row.get("销售方名称", ""), - "person_name": row.get("人员姓名") or config.get("default_name", ""), - "card_date": _format_date(row.get("刷卡日期") or ""), - "card_no": row.get("公务卡号") or config.get("default_card_no", ""), - "card_amount": float(row.get("刷卡金额") or "0"), - "remark": row.get("备注") or "", - "person_id": row.get("工号") or config.get("default_person_id", ""), - }) + invoices_json = row.get("_invoices_json", "") + if not invoices_json: + continue + try: + inv_list = json.loads(invoices_json) + for inv in inv_list: + invoices.append( + { + "seq": row.get("序号", ""), + "invoice_no": inv.get("发票号码", ""), + "invoice_date": inv.get("开票日期", ""), + "item_name": inv.get("项目名称", ""), + "spec_model": inv.get("规格型号", ""), + "total_amount": _safe_float(inv.get("价税合计")), + "seller_name": inv.get("销售方名称", ""), + "person_name": inv.get("人员姓名") or config.get("default_name", ""), + "card_date": _format_date(row.get("刷卡日期") or ""), + "card_no": row.get("公务卡号") or config.get("default_card_no", ""), + "card_amount": _safe_float(row.get("刷卡金额")), + "remark": row.get("备注") or "", + "person_id": row.get("工号") or config.get("default_person_id", ""), + } + ) + except json.JSONDecodeError: + log.warning(f"无法解析发票 JSON: {invoices_json[:50]}...") return invoices @@ -62,29 +91,31 @@ def load_invoice_data(csv_path: str, config: dict) -> list[dict]: # 报销机器人 # ------------------------------------------------------------------ + class ReimburseBot: """财务报销自动化机器人""" - def __init__(self, config: dict, headless: bool = False): + def __init__(self, config: dict[str, Any], headless: bool = False): self.config = config self.headless = headless self.work_dir: Path | None = None - self.browser = None - self.context = None - self.page = None + self.browser: Any = None + self.context: Any = None + self.page: Any = None from playwright.sync_api import sync_playwright + self._pw_ctx = sync_playwright() self.pw = self._pw_ctx.__enter__() - def launch(self): + def launch(self) -> None: """启动浏览器""" self.browser = self.pw.chromium.launch(headless=self.headless) self.context = self.browser.new_context(viewport={"width": 1920, "height": 1080}) self.page = self.context.new_page() self.page.set_default_timeout(30000) - def login_portal(self): + def login_portal(self) -> None: """登录信息门户""" log.info("登录信息门户...") @@ -113,7 +144,7 @@ class ReimburseBot: self._wait_for_portal() - def _wait_for_portal(self): + def _wait_for_portal(self) -> None: """等待跳转到统一信息平台""" for _ in range(30): self.page.wait_for_timeout(1000) @@ -125,7 +156,7 @@ class ReimburseBot: self._screenshot("portal_timeout") raise TimeoutError("登录超时,未跳转到信息门户") - def navigate_to_reimburse(self): + def navigate_to_reimburse(self) -> None: """从统一信息平台进入报销系统""" log.info("进入报销系统...") self._wait_for('text="快捷入口"', timeout=5000) @@ -170,7 +201,7 @@ class ReimburseBot: self.page.goto(common_url, wait_until="domcontentloaded", timeout=15000) self._wait_for('text="单据状态:"', timeout=5000) - def open_reimburse_menu(self): + def open_reimburse_menu(self) -> None: """点击「新增」创建新报销单""" log.info("创建新报销单...") self.page.wait_for_timeout(2000) @@ -180,14 +211,14 @@ class ReimburseBot: except Exception: try: self.page.click("text=新增", timeout=3000) - except Exception: + except Exception as err: self._screenshot("no_add_button") - raise RuntimeError("无法点击新增按钮") + raise RuntimeError("无法点击新增按钮") from err self.page.wait_for_timeout(3000) self._screenshot("after_add_click") - def fill_basic_info(self, description: str = "元器件采购报销"): + def fill_basic_info(self, description: str = "元器件采购报销") -> None: """填写基本信息""" log.info("填写基本信息...") @@ -223,7 +254,7 @@ class ReimburseBot: self._screenshot("step3_done") - def add_reimburse_items(self, invoices: list[dict]): + def add_reimburse_items(self, invoices: list[dict[str, Any]]) -> None: """录入报销明细(一条总明细)""" card_amount = sum(inv["card_amount"] for inv in invoices) log.info(f"录入报销明细 (合计 ¥{card_amount:.2f})...") @@ -255,7 +286,7 @@ class ReimburseBot: self._screenshot("item_total_error") raise - def fill_payment(self, invoices: list[dict]): + def fill_payment(self, invoices: list[dict[str, Any]]) -> None: """录入支付信息""" log.info("录入支付信息...") try: @@ -282,7 +313,7 @@ class ReimburseBot: self._screenshot("step5_done") - def upload_attachments(self, invoices: list[dict]): + def upload_attachments(self, invoices: list[dict[str, Any]]) -> None: """上传发票附件""" log.info("上传附件...") @@ -299,11 +330,11 @@ class ReimburseBot: file_path = attachment_files[i] if i < len(attachment_files) else None try: self._wait_for("#insertAcc", timeout=20000) # 等待增加按钮出现 - self.page.click("#insertAcc", timeout=5000) #点击增加按钮出现 + self.page.click("#insertAcc", timeout=5000) # 点击增加按钮出现 self._wait_for("#fjlx", timeout=5000) self.page.select_option("#fjlx", "1") explanation = f"{inv['item_name']} - {inv['invoice_no']}" - self.page.fill("#fpsmxx", explanation) + self.page.fill("#fpsmxx", explanation) except Exception: pass @@ -328,7 +359,7 @@ class ReimburseBot: self._screenshot("step6_done") - def submit(self): + def submit(self) -> None: """提交报销单""" log.info("提交报销单...") try: @@ -340,7 +371,7 @@ class ReimburseBot: self._screenshot("submit_error") raise - def close(self): + def close(self) -> None: """关闭浏览器""" if self.context: self.context.close() @@ -355,10 +386,10 @@ class ReimburseBot: # 辅助方法 # -------------------------------------------------------- - def _wait_for(self, selector: str, timeout: int = None): + def _wait_for(self, selector: str, timeout: int | None = None) -> None: self.page.wait_for_selector(selector, timeout=timeout) - def _screenshot(self, name: str): + def _screenshot(self, name: str) -> None: img_dir = Path(__file__).parent.parent / "images" img_dir.mkdir(exist_ok=True) self.page.screenshot(path=str(img_dir / f"debug_{name}.png")) @@ -368,7 +399,10 @@ class ReimburseBot: # 对外入口 # ------------------------------------------------------------------ -def run_bot(config: dict, invoices: list[dict], headless: bool = False, work_dir: Path | None = None): + +def run_bot( + config: dict[str, Any], invoices: list[dict[str, Any]], headless: bool = False, work_dir: Path | None = None +) -> None: """执行完整的浏览器填报流程""" if not config["username"] or not config["password"]: raise ValueError("缺少用户名或密码") @@ -396,7 +430,7 @@ def run_bot(config: dict, invoices: list[dict], headless: bool = False, work_dir bot.close() -def run_bot_web(config: dict, invoices: list[dict], work_dir: Path): +def run_bot_web(config: dict[str, Any], invoices: list[dict[str, Any]], work_dir: Path) -> None: """Web 模式填报 — headless,附件从指定目录读取""" if not config["username"] or not config["password"]: raise ValueError("缺少用户名或密码") @@ -420,4 +454,4 @@ def run_bot_web(config: dict, invoices: list[dict], work_dir: Path): pass raise finally: - bot.close() \ No newline at end of file + bot.close() diff --git a/app/config.py b/src/config.py similarity index 69% rename from app/config.py rename to src/config.py index 77f5016..4e7875a 100644 --- a/app/config.py +++ b/src/config.py @@ -10,7 +10,7 @@ from pathlib import Path _CONFIG_PATH = Path(__file__).parent.parent / "config.json" -def load_config() -> dict: +def load_config() -> dict[str, str | Path]: """加载并合并配置,缺失字段使用默认值""" raw = {} if _CONFIG_PATH.exists(): @@ -31,4 +31,19 @@ def load_config() -> dict: "default_person_id": raw.get("default_person_id", ""), "consumable_storage": raw.get("consumable_storage", "躬行楼 C205"), "attachment_dir": project_root / "attachments", - } \ No newline at end of file + } + + +def get_llm_config() -> dict[str, str]: + """加载 LLM 配置,缺失字段使用默认值""" + raw = {} + if _CONFIG_PATH.exists(): + with open(_CONFIG_PATH, encoding="utf-8") as f: + raw = json.load(f) + + llm_raw = raw.get("llm", {}) + return { + "model": llm_raw.get("model", "qwen-vl-max"), + "api_base": llm_raw.get("api_base", "http://localhost:8080/v1"), + "api_key": llm_raw.get("api_key", "lm-studio"), + } diff --git a/src/doc/README.md b/src/doc/README.md new file mode 100644 index 0000000..335a5c6 --- /dev/null +++ b/src/doc/README.md @@ -0,0 +1,49 @@ +--- + +## last_reviewed: 2026-06-09 + +# src/doc — 文档处理模块 + +负责发票信息提取、基于 LLM 的支付截图信息识别、以及将数据填入 Word 出库单模板。 + +## 模块清单 + + +| 文件 | 作用 | +| ------------------------ | -------------------------------------------------- | +| `extractor.py` | 编排入口:串联 PDF 读取 → LLM 提取 → 支付截图匹配 → 分类 | +| `pdf.py` | PDF 文件发现与文本提取(pdfplumber) | +| `llm_extractor.py` | 基于 LLM 的信息提取(发票文本 + 支付截图多模态) | +| `matcher.py` | 发票与支付截图按金额匹配,回填刷卡信息至发票记录 | +| `invoice.py` | 发票类型常量、分类逻辑、CSV 读写工具 | +| `fill_consumable_doc.py` | 将 CSV 数据填入易耗品出库单 Word 模板(pywin32 COM) | +| `prompt.py` | LLM 提示词模板加载 | +| `prompts/` | 提示词模板文件(`invoice_system.md`、`card_info_system.md`) | + + +## 数据流 + +``` +PDF 发票 → pdf.py → llm_extractor.py → [发票列表] +支付截图 → llm_extractor.py → [刷卡记录] + ↓ + matcher.py(按金额贪心匹配,容差 10 元) + ↓ + invoice.py 分类 → CSV(已回填刷卡日期/卡号/金额) + ↓ + fill_consumable_doc → 易耗品出库单.doc +``` + +## 依赖说明 + +- **pdfplumber** — PDF 文本提取 +- **pywin32** — Word COM 自动化(仅 Windows) +- **llama-index** — LLM 信息提取 + +## 注意事项 + +- `fill_consumable_doc.py` 依赖 Microsoft Word + COM,仅 Windows 可用 +- LLM 提取不会覆盖 CSV 中已有非空字段 +- 提示词模板位于 `prompts/` 目录,由 `prompt.py` 加载 +- LLM 提取失败时直接报错,无正则回退 + diff --git a/src/doc/__init__.py b/src/doc/__init__.py new file mode 100644 index 0000000..97668d1 --- /dev/null +++ b/src/doc/__init__.py @@ -0,0 +1,4 @@ +"""文档处理模块 + +包含发票提取、LLM 信息提取、出库单填写等功能。 +""" diff --git a/src/doc/extractor.py b/src/doc/extractor.py new file mode 100644 index 0000000..0074083 --- /dev/null +++ b/src/doc/extractor.py @@ -0,0 +1,65 @@ +"""发票提取编排 + +串联 PDF 读取 → LLM 提取 → 支付截图匹配 → 分类,生成支付记录列表。 + +对外接口: + extract_invoices(directory) -> tuple[list[dict], dict] +""" + +from .. import get_logger +from .invoice import classify_invoice_batch +from .llm_extractor import extract_invoice_from_text +from .matcher import match_invoices_to_cards +from .pdf import extract_text_from_pdf, find_pdf_files + +log = get_logger("extractor") + + +def extract_invoices( + directory: str = ".", +) -> tuple[list[dict[str, str]], dict[str, list[dict[str, str]]]]: + """扫描目录下所有 PDF,提取发票信息并匹配支付记录 + + Returns: + (payment_records, groups): 支付记录列表和按发票类型分组的字典 + groups = {'travel': [差旅发票], 'general': [普通发票]} + """ + pdf_files = find_pdf_files(directory) + if not pdf_files: + log.warning("未找到 PDF 文件") + return [], {"travel": [], "general": []} + + log.info(f"发现 {len(pdf_files)} 个 PDF 文件") + + all_invoices = [] + for pdf_path in pdf_files: + text = extract_text_from_pdf(pdf_path) + if not text: + log.warning(f"未能提取文本: {pdf_path.name}") + continue + + invoice = extract_invoice_from_text(text, pdf_path.name) + + if invoice and invoice.get("发票号码"): + all_invoices.append(invoice) + log.info(f"[{invoice['发票类型']}] 已解析: {pdf_path.name}") + else: + log.warning(f"未能解析: {pdf_path.name}") + + if all_invoices: + log.info(f"共处理 {len(all_invoices)} 张发票") + else: + log.warning("未成功解析任何发票") + + # 将支付截图与发票进行金额匹配,返回以支付记录为主键的列表 + payment_records = match_invoices_to_cards(all_invoices, directory) + + # 从支付记录中还原所有发票用于分类 + all_invoices_restored: list[dict[str, str]] = [] + for record in payment_records: + all_invoices_restored.extend(record.get("_matched_invoices", [])) + + groups = classify_invoice_batch(all_invoices_restored) + log.info(f"差旅发票: {len(groups['travel'])} 张, 普通发票: {len(groups['general'])} 张") + + return payment_records, groups diff --git a/app/fill_consumable_doc.py b/src/doc/fill_consumable_doc.py similarity index 63% rename from app/fill_consumable_doc.py rename to src/doc/fill_consumable_doc.py index 8a7495c..db1d8d7 100644 --- a/app/fill_consumable_doc.py +++ b/src/doc/fill_consumable_doc.py @@ -11,10 +11,11 @@ import re import shutil from datetime import date from pathlib import Path +from typing import Any -from . import get_logger -from .bot import load_invoice_data -from .config import load_config +from .. import get_logger +from ..bot import load_invoice_data +from ..config import load_config log = get_logger("fill_consumable_doc") @@ -86,7 +87,7 @@ def _today_cn_date() -> str: return f"{today.year}年{today.month}月{today.day}日" -def _apply_font(rng) -> None: +def _apply_font(rng: Any) -> None: """将范围字体设为宋体五号(含数字与英文)。""" font = rng.Font font.Name = TABLE_FONT_NAME @@ -97,7 +98,7 @@ def _apply_font(rng) -> None: font.Size = TABLE_FONT_SIZE -def _set_cell_value(cell, text: str) -> None: +def _set_cell_value(cell: Any, text: str) -> None: """写入单元格正文(不含末尾单元格标记)。""" rng = cell.Range rng.MoveEnd(WD_CHARACTER, -1) @@ -105,7 +106,7 @@ def _set_cell_value(cell, text: str) -> None: _apply_font(rng) -def _normalize_table_font(tbl) -> None: +def _normalize_table_font(tbl: Any) -> None: """填写完成后统一整张表的字体。""" for row in tbl.Rows: for cell in row.Cells: @@ -114,7 +115,7 @@ def _normalize_table_font(tbl) -> None: _apply_font(rng) -def _replace_date_in_doc(doc, new_date: str) -> None: +def _replace_date_in_doc(doc: Any, new_date: str) -> None: """仅替换表头段落中的日期文字,不改动段落其余部分。""" if not new_date: return @@ -135,7 +136,7 @@ def _replace_date_in_doc(doc, new_date: str) -> None: def fill_consumable_doc( csv_path: str | Path, doc_path: str | Path, - config: dict | None = None, + config: dict[str, Any] | None = None, backup: bool = True, ) -> Path: csv_path = Path(csv_path) @@ -148,59 +149,73 @@ def fill_consumable_doc( bak = doc_path.with_suffix(doc_path.suffix + ".bak") shutil.copy2(doc_path, bak) + import pythoncom import win32com.client - word = win32com.client.Dispatch("Word.Application") - word.Visible = False - word.DisplayAlerts = 0 - doc = word.Documents.Open(str(doc_path.resolve())) - + pythoncom.CoInitialize() try: - _replace_date_in_doc(doc, _today_cn_date()) + word = win32com.client.Dispatch("Word.Application") + word.Visible = False + word.DisplayAlerts = 0 + doc = word.Documents.Open(str(doc_path.resolve())) - tbl = doc.Tables(1) - storage = config.get("consumable_storage", "躬行楼 C205") + try: + _replace_date_in_doc(doc, _today_cn_date()) - for i, inv in enumerate(invoices): - row_idx = i + 2 - if row_idx > tbl.Rows.Count: - break + tbl = doc.Tables(1) + storage = config.get("consumable_storage", "躬行楼 C205") - parsed = parse_spec_model(inv.get("spec_model", "")) - card_amount = inv.get("card_amount") or 0 - qty_str = parsed["qty"] - qty_val = int(qty_str) if qty_str and qty_str.isdigit() else 0 + for i, inv in enumerate(invoices): + row_idx = i + 2 + if row_idx > tbl.Rows.Count: + break - # 金额填写刷卡金额,单价由刷卡金额反算 - amount = _format_money(card_amount) - unit_price = _format_money(card_amount / qty_val) if qty_val > 0 else _format_money(card_amount) + parsed = parse_spec_model(str(inv.get("spec_model", ""))) + # 当规格型号为空时,从项目名称提取产品信息 + if not parsed["product_name"]: + item_name = str(inv.get("item_name", "")) + # 去除 "*分类*" 前缀(如 "*电子工业设备*元件盒" -> "元件盒") + if "*" in item_name: + item_name = item_name.split("*")[-1].strip() + parsed["product_name"] = item_name - # 数量:去掉前导零 - qty = str(qty_val) if qty_val > 0 else "" + card_amount_raw = inv.get("card_amount") or 0 + card_amount: float = float(str(card_amount_raw).replace(",", "")) + qty_str = parsed["qty"] + qty_val = int(qty_str) if qty_str and qty_str.isdigit() else 0 - values = [ - str(inv.get("seq", i + 1)), - parsed["product_name"], - parsed["spec"], - parsed["unit"], - qty, - unit_price, - amount, - "", # 购货人签字 — 保持空白 - storage, - "", # 领用人签字 — 保持空白 - "", # 备注 — 保持空白,避免撑破版式 - ] + # 金额填写刷卡金额,单价由刷卡金额反算 + amount = _format_money(card_amount) + unit_price = _format_money(card_amount / qty_val) if qty_val > 0 else _format_money(card_amount) - for col_idx, val in enumerate(values, start=1): - _set_cell_value(tbl.Cell(row_idx, col_idx), val) + # 数量:去掉前导零;若无数量则默认为 1 + qty = str(qty_val) if qty_val > 0 else "1" - _normalize_table_font(tbl) + values = [ + str(inv.get("seq", i + 1)), + parsed["product_name"], + parsed["spec"], + parsed["unit"], + qty, + unit_price, + amount, + "", # 购货人签字 — 保持空白 + storage, + "", # 领用人签字 — 保持空白 + "", # 备注 — 保持空白,避免撑破版式 + ] - doc.Save() + for col_idx, val in enumerate(values, start=1): + _set_cell_value(tbl.Cell(row_idx, col_idx), str(val)) + + _normalize_table_font(tbl) + + doc.Save() + finally: + doc.Close() + word.Quit() finally: - doc.Close() - word.Quit() + pythoncom.CoUninitialize() return doc_path @@ -209,7 +224,7 @@ def fill_consumable_from_template( csv_path: str | Path, template_path: str | Path, output_path: str | Path, - config: dict | None = None, + config: dict[str, Any] | None = None, ) -> Path: """从模板复制并填写出库单(Web 会话每次从模板重新生成)。""" template_path = Path(template_path) diff --git a/src/doc/invoice.py b/src/doc/invoice.py new file mode 100644 index 0000000..877e04c --- /dev/null +++ b/src/doc/invoice.py @@ -0,0 +1,273 @@ +"""发票数据模型与 CSV 工具 + +定义发票类型常量、CSV 列结构,提供发票分类和 CSV 读写功能。 + +对外接口: + INVOICE_LEVEL_COLUMNS 发票级别 CSV 列定义 + PAYMENT_RECORD_COLUMNS 支付记录级别 CSV 列定义 + INVOICE_TYPE_* 发票类型常量 + is_travel_invoice(type) 判断是否为差旅发票 + classify_invoice_batch(invoices) 按类型分组 + load_csv(path) 读取支付记录 CSV + save_csv(payment_records, path) 保存支付记录 CSV + save_invoice_csv(payment_records, path) 保存发票级别 CSV +""" + +import csv +import json +from pathlib import Path + +from .. import get_logger + +log = get_logger("invoice") + +# ------------------------------------------------------------------ +# CSV 列定义 +# ------------------------------------------------------------------ + +# 发票级别 CSV 列(用于 invoice_summary.csv,每行一张发票) +INVOICE_LEVEL_COLUMNS = [ + "序号", + "发票类型", + "发票号码", + "开票日期", + "项目名称", + "规格型号", + "价税合计", + "销售方名称", + "出发站", + "到达站", + "车次", + "乘车日期", + "座位等级", + "人员姓名", + "刷卡日期", + "公务卡号", + "刷卡金额", + "备注", + "工号", +] + +# 支付记录级别 CSV 列(用于 payment_records.csv,每行一笔支付) +PAYMENT_RECORD_COLUMNS = [ + "序号", + # 支付信息 + "刷卡日期", + "公务卡号", + "刷卡金额", + # 发票聚合信息 + "关联发票数", + "发票详情", # 格式: 类型[号码]¥金额 | 类型[号码]¥金额 + "备注", + # 内部字段(用于下游解析) + "_invoices_json", # JSON 序列化的发票列表,供 bot/fill_doc 使用 + "工号", +] + +# ------------------------------------------------------------------ +# 发票类型常量 +# ------------------------------------------------------------------ + +INVOICE_TYPE_TRAIN = "高铁票" +INVOICE_TYPE_HOTEL = "酒店住宿" +INVOICE_TYPE_GENERAL = "普通发票" + +INVOICE_TYPE_TRAVEL = frozenset([INVOICE_TYPE_TRAIN, INVOICE_TYPE_HOTEL]) + + +def is_travel_invoice(invoice_type: str) -> bool: + """判断是否为差旅发票(高铁票/酒店住宿)""" + return invoice_type in INVOICE_TYPE_TRAVEL + + +def classify_invoice_batch(invoices: list[dict[str, str]]) -> dict[str, list[dict[str, str]]]: + """将发票列表按类型分组:{'travel': [...], 'general': [...]}""" + travel = [] + general = [] + for inv in invoices: + inv_type = inv.get("发票类型", INVOICE_TYPE_GENERAL) + if is_travel_invoice(inv_type): + travel.append(inv) + else: + general.append(inv) + return {"travel": travel, "general": general} + + +# ------------------------------------------------------------------ +# CSV 读写工具 +# ------------------------------------------------------------------ + + +def _clean_invoice_for_json(inv: dict[str, str]) -> dict[str, str]: + """清理发票字典中的内部字段,保留可序列化的字段""" + clean = {} + for k, v in inv.items(): + if k.startswith("_"): + continue + clean[k] = v + return clean + + +def load_csv(csv_path: Path) -> list[dict[str, str]] | None: + """读取支付记录 CSV 为 dict 列表,失败返回 None""" + try: + with open(csv_path, encoding="utf-8", newline="") as f: + reader = csv.DictReader(f) + fieldnames = reader.fieldnames or [] + missing = [c for c in PAYMENT_RECORD_COLUMNS if c not in fieldnames] + if missing: + log.error(f"CSV 缺少必要列: {missing}") + return None + return [row for row in reader] + except FileNotFoundError: + log.error(f"CSV 文件不存在: {csv_path.name}") + return None + except Exception as e: + log.error(f"CSV 读取失败: {e}") + return None + + +def load_invoice_csv(csv_path: Path) -> list[dict[str, str]] | None: + """读取发票级别 CSV 为 dict 列表(每行一张发票),失败返回 None""" + try: + with open(csv_path, encoding="utf-8", newline="") as f: + reader = csv.DictReader(f) + fieldnames = reader.fieldnames or [] + missing = [c for c in INVOICE_LEVEL_COLUMNS if c not in fieldnames] + if missing: + log.error(f"发票 CSV 缺少必要列: {missing}") + return None + return [row for row in reader] + except FileNotFoundError: + log.error(f"发票 CSV 文件不存在: {csv_path.name}") + return None + except Exception as e: + log.error(f"发票 CSV 读取失败: {e}") + return None + + +def load_invoices_from_csv(csv_path: Path) -> list[dict[str, str]] | None: + """从支付记录 CSV 中还原发票级别的数据(供 bot/fill_doc 使用) + + 读取 _invoices_json 字段,反序列化后展平为发票列表。 + """ + rows = load_csv(csv_path) + if rows is None: + return None + + invoices = [] + for row in rows: + invoices_json = row.get("_invoices_json", "") + if not invoices_json: + continue + try: + inv_list = json.loads(invoices_json) + for inv in inv_list: + # 从支付记录回填刷卡信息 + inv["刷卡日期"] = row.get("刷卡日期", inv.get("刷卡日期", "")) + inv["公务卡号"] = row.get("公务卡号", inv.get("公务卡号", "")) + inv["刷卡金额"] = row.get("刷卡金额", inv.get("刷卡金额", "")) + invoices.append(inv) + except json.JSONDecodeError: + log.warning(f"无法解析发票 JSON: {invoices_json[:50]}...") + return invoices + + +def save_csv( + payment_records: list[dict[str, str]], + output_path: str | Path = "payment_records.csv", +) -> None: + """将支付记录列表保存为 CSV(以支付记录为主键) + + 每条支付记录包含: + - 刷卡日期、公务卡号、刷卡金额(支付信息) + - 关联发票数、发票详情(发票聚合信息) + - _matched_invoices(内部字段,序列化为 JSON 存储在 CSV 中) + """ + csv_path = Path(output_path) + + with open(csv_path, "w", encoding="utf-8", newline="") as f: + writer = csv.writer(f) + writer.writerow(PAYMENT_RECORD_COLUMNS) + + for idx, record in enumerate(payment_records, 1): + # 序列化关联发票为 JSON + matched_invoices: list[dict[str, str]] = record.get("_matched_invoices", []) # type: ignore[assignment] + invoices_json = json.dumps( + [_clean_invoice_for_json(inv) for inv in matched_invoices], + ensure_ascii=False, + ) + + writer.writerow( + [ + idx, + record.get("刷卡日期", ""), + record.get("公务卡号", ""), + record.get("刷卡金额", ""), + record.get("关联发票数", str(len(matched_invoices))), + record.get("发票详情", ""), + record.get("备注", ""), + invoices_json, + record.get("工号", ""), + ] + ) + + log.info(f"支付记录 CSV 已保存: {csv_path.name}") + + +def save_invoice_csv( + payment_records: list[dict[str, str]], + output_path: str | Path = "invoice_summary.csv", +) -> None: + """将支付记录展平为发票级别 CSV(每行一张发票) + + 从 _matched_invoices 中还原每张发票,回填刷卡信息, + 生成以发票为主键的 CSV,用于人工填写报销单参考。 + """ + csv_path = Path(output_path) + + with open(csv_path, "w", encoding="utf-8", newline="") as f: + writer = csv.writer(f) + writer.writerow(INVOICE_LEVEL_COLUMNS) + + idx = 1 + for record in payment_records: + matched_invoices: list[dict[str, str]] = record.get("_matched_invoices", []) # type: ignore[assignment] + for inv in matched_invoices: + clean_inv = _clean_invoice_for_json(inv) + writer.writerow( + [ + idx, + clean_inv.get("发票类型", ""), + clean_inv.get("发票号码", ""), + clean_inv.get("开票日期", ""), + clean_inv.get("项目名称", ""), + clean_inv.get("规格型号", ""), + clean_inv.get("价税合计", ""), + clean_inv.get("销售方名称", ""), + clean_inv.get("出发站", ""), + clean_inv.get("到达站", ""), + clean_inv.get("车次", ""), + clean_inv.get("乘车日期", ""), + clean_inv.get("座位等级", ""), + clean_inv.get("人员姓名", ""), + record.get("刷卡日期", ""), + record.get("公务卡号", ""), + record.get("刷卡金额", ""), + record.get("备注", ""), + record.get("工号", ""), + ] + ) + idx += 1 + + log.info(f"发票级别 CSV 已保存: {csv_path.name}") + + +def save_csv_rows(csv_path: Path, rows: list[dict[str, str]]) -> None: + """将 dict 列表保存为支付记录 CSV(用于更新已有 CSV)""" + with open(csv_path, "w", encoding="utf-8", newline="") as f: + writer = csv.DictWriter(f, fieldnames=PAYMENT_RECORD_COLUMNS) + writer.writeheader() + writer.writerows(rows) + + log.info(f"CSV 已保存: {csv_path.name}") diff --git a/src/doc/llm_extractor.py b/src/doc/llm_extractor.py new file mode 100644 index 0000000..5a203f6 --- /dev/null +++ b/src/doc/llm_extractor.py @@ -0,0 +1,210 @@ +""" +LLM 信息提取 + +使用 LLM 从 PDF 文本中提取结构化数据,以及从支付截图中提取刷卡信息。 +支持 JSON 格式输出,字段与 CSV_COLUMNS 对齐。 + +对外接口: + extract_invoice_from_text(text, file_name) -> dict 从 PDF 文本提取发票信息 + extract_card_info_from_image(image_path) -> dict 从支付截图提取刷卡信息 +""" + +from __future__ import annotations + +import base64 +import json +from pathlib import Path +from typing import Any, cast + +from .. import get_logger +from .prompt import build_card_info_system_prompt, build_invoice_system_prompt + +log = get_logger("llm_extractor") + + +def _create_llm() -> Any: + """根据配置文件创建 LLM 实例。""" + try: + from llama_index.llms.openai_like import OpenAILike + except ImportError: + log.error("缺少 llama-index-llms-openai-like,请执行: uv pip install llama-index-llms-openai-like") + raise + + from ..config import get_llm_config + + llm_config = get_llm_config() + return OpenAILike( + model=llm_config["model"], + api_base=llm_config["api_base"], + api_key=llm_config.get("api_key", "lm-studio"), + temperature=0.1, + max_tokens=8192, + request_timeout=600.0, + is_chat_model=True, + ) + + +def _llm_query(system_prompt: str, user_content: str, max_tokens: int = 4096) -> str: + """发送请求到 LLM 并返回完整响应文本。""" + from llama_index.core.llms import ChatMessage + + from ..config import get_llm_config + + messages = [ + ChatMessage(role="system", content=system_prompt), + ChatMessage(role="user", content=user_content), + ] + + llm_config = get_llm_config() + llm = _create_llm() + log.info("开始请求 LLM (model=%s, base=%s)", llm_config["model"], llm_config["api_base"]) + + try: + parts = [] + for resp in llm.stream_chat( + messages, + temperature=0.1, + max_tokens=max_tokens, + extra_body={"reasoning_effort": "none"}, + ): + delta = resp.delta + if delta: + parts.append(delta) + text = "".join(parts) + log.info("LLM 请求完成,响应总长度: %d 字符", len(text)) + log.info("LLM 响应: %s", text) + return text + except Exception as e: + log.error("LLM 请求失败: %s", e) + raise + + +def _parse_json_response(text: str) -> dict[str, Any]: + """从 LLM 响应中提取 JSON,处理可能的 Markdown 包裹。""" + text = text.strip() + + # 处理 ```json ... ``` 包裹 + if "```" in text: + # 提取第一个代码块 + start = text.find("```") + 3 + end = text.find("```", start) + if end > start: + text = text[start:end].strip() + + # 去掉可能的前缀 (如 "json") + if text.lower().startswith("json"): + text = text[4:].strip() + + return cast(dict[str, Any], json.loads(text)) + + +def extract_invoice_from_text(text: str, file_name: str = "") -> dict[str, Any]: + """从 PDF 发票文本中提取结构化数据。 + + Args: + text: PDF 提取的文本内容。 + file_name: 原始文件名(用于日志)。 + + Returns: + 包含所有 CSV_COLUMNS 字段的字典。 + """ + system_prompt = build_invoice_system_prompt() + user_content = f"请分析以下发票文本并提取信息:\n\n文件名: {file_name}\n\n---\n\n{text}\n\n---" + + try: + response = _llm_query(system_prompt, user_content, max_tokens=4096) + result = _parse_json_response(response) + log.info("LLM 发票提取成功: %s", file_name) + return result + except Exception as e: + log.error("LLM 发票提取失败: %s (%s)", file_name, e) + raise + + +# ------------------------------------------------------------------ +# 支付截图信息提取(多模态) +# ------------------------------------------------------------------ + + +def _image_to_base64(image_path: Path) -> str: + """将图片文件读取为 base64 字符串。""" + with open(image_path, "rb") as f: + return base64.b64encode(f.read()).decode("utf-8") + + +def _llm_query_multimodal( + system_prompt: str, + text: str, + image_b64: str, + max_tokens: int = 4096, +) -> str: + """发送多模态请求(文本 + 图片)到 LLM。""" + from llama_index.core.base.llms.types import ImageBlock, TextBlock + from llama_index.core.llms import ChatMessage + + from ..config import get_llm_config + + messages = [ + ChatMessage(role="system", content=system_prompt), + ChatMessage( + role="user", + blocks=[ + TextBlock(text=text), + ImageBlock( + url=f"data:image/jpeg;base64,{image_b64}", + detail="high", + ), + ], + ), + ] + + llm_config = get_llm_config() + llm = _create_llm() + log.info( + "开始请求 LLM 多模态 (model=%s, base=%s)", + llm_config["model"], + llm_config["api_base"], + ) + + try: + parts = [] + for resp in llm.stream_chat( + messages, + temperature=0.1, + max_tokens=max_tokens, + extra_body={"reasoning_effort": "none"}, + ): + delta = resp.delta + if delta: + parts.append(delta) + text = "".join(parts) + log.info("LLM 多模态请求完成,响应总长度: %d 字符", len(text)) + log.info("LLM 多模态响应: %s", text) + return text + except Exception as e: + log.error("LLM 多模态请求失败: %s", e) + raise + + +def extract_card_info_from_image(image_path: Path) -> dict[str, Any]: + """从支付截图中提取刷卡信息。 + + Args: + image_path: 支付截图图片路径。 + + Returns: + 包含刷卡日期、刷卡金额、公务卡号的字典。 + """ + system_prompt = build_card_info_system_prompt() + user_text = f"请分析以下支付截图并提取信息:\n\n文件名: {image_path.name}" + + image_b64 = _image_to_base64(image_path) + + try: + response = _llm_query_multimodal(system_prompt, user_text, image_b64, max_tokens=4096) + result = _parse_json_response(response) + log.info("LLM 支付截图提取成功: %s", image_path.name) + return result + except Exception as e: + log.error("LLM 支付截图提取失败: %s (%s)", image_path.name, e) + raise diff --git a/src/doc/matcher.py b/src/doc/matcher.py new file mode 100644 index 0000000..32fb9c1 --- /dev/null +++ b/src/doc/matcher.py @@ -0,0 +1,395 @@ +"""发票与支付截图匹配 + +将提取到的发票数据与支付截图中的刷卡记录进行金额匹配, +输出以支付记录为主键的结果列表。 + +## 业务约束 + +- 发票数 >= 付款记录数(最少一张发票对应一张付款记录) +- 发票总金额 >= 付款总金额(发票只能比付款多,不能少) +- 若发票数 == 付款数,走一对一匹配,无需一对多 + +## 匹配流程 + +1. 扫描目录下图片文件,调用 LLM 提取刷卡信息(日期/金额/卡号) +2. 解析发票和刷卡记录的金额,进行总额校验 + - 发票总额 < 刷卡总额时发出 warning +3. 按金额降序排序 +4. 根据数量关系选择匹配策略: + - 数量相等 → 一对一匹配:按金额从大到小依次配对,相对容差内即匹配 + - 发票更多 → 一对多匹配:对每张刷卡记录贪心凑金额,相对容差内结束 +5. 构建以支付记录为主键的结果列表 +6. 未匹配的发票单独作为一条记录(无刷卡信息) +7. 清理内部字段,输出支付记录列表 + +## 容差计算 + +使用相对容差(默认 3%),以刷卡金额为基准: +- ¥2900 发票 vs ¥2850 刷卡 → 差 ¥50,容差 ¥85.5 → 匹配成功 +- ¥100 发票 vs ¥95 刷卡 → 差 ¥5,容差 ¥3.0 → 不匹配(需精确匹配或调整) + +## 一对多匹配细节 + +- 对每张刷卡记录,维护 remaining(剩余待匹配金额) +- 遍历未分配的发票(按金额降序): + - 若发票金额 + 容差 >= remaining,视为最后一张,匹配后退出 + - 否则发票金额不超过 remaining + 容差即可匹配 +- 匹配后 remaining 为负且超出容差时回滚最后一张发票 +- 每张发票只会被分配一次 + +## 对外接口 + + match_invoices_to_cards(invoices, directory, tolerance) -> list[dict] +""" + +from pathlib import Path +from typing import Any + +from .. import get_logger +from .llm_extractor import extract_card_info_from_image + +log = get_logger("matcher") + +# 支持的图片扩展名 +IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".bmp", ".webp"} + + +def _find_images(directory: str) -> list[Path]: + """在目录下查找支付截图图片文件""" + dir_path = Path(directory) + images = [f for f in dir_path.iterdir() if f.is_file() and f.suffix.lower() in IMAGE_EXTENSIONS] + return sorted(images) + + +def _safe_float(value: str | None, default: float = 0.0) -> float: + """安全转换为浮点数""" + if value is None or str(value).strip() == "": + return default + try: + return float(str(value).replace(",", "")) + except (ValueError, TypeError): + return default + + +def _extract_all_cards(directory: str) -> list[dict[str, Any]]: + """提取目录下所有支付截图的刷卡信息""" + images = _find_images(directory) + if not images: + log.warning("未找到支付截图图片") + return [] + + log.info(f"发现 {len(images)} 张支付截图") + cards = [] + for img_path in images: + try: + card_info = extract_card_info_from_image(img_path) + card_info["_source_file"] = img_path.name + cards.append(card_info) + log.info(f"[支付截图] 已解析: {img_path.name}") + except Exception as e: + log.warning(f"支付截图解析失败 {img_path.name}: {e}") + + log.info(f"共提取 {len(cards)} 条刷卡记录") + return cards + + +def _build_invoice_summary(invoices: list[dict[str, Any]]) -> str: + """将多张发票信息汇总为备注字符串""" + parts = [] + for inv in invoices: + person_name = inv.get("人员姓名") or inv.get("发票号码", "未知") + inv_type = inv.get("发票类型", "未知") + amount = inv.get("价税合计", "未知") + parts.append(f"{inv_type}[{person_name}]¥{amount}") + return " | ".join(parts) + + +def _relative_tolerance(base: float, rate: float = 0.05) -> float: + """根据基准金额计算相对容差(默认 5%)""" + return abs(base) * rate + + +def match_invoices_to_cards( + invoices: list[dict[str, Any]], + directory: str, + tolerance: float = 0.03, +) -> list[dict[str, Any]]: + """将发票与支付截图按金额匹配,输出以支付记录为主键的结果列表 + + 业务约束: + - 发票数 >= 付款记录数 + - 发票总金额 >= 付款总金额(发票只能比付款多,不能少) + - 若发票数 == 付款数,一对一匹配,无需一对多 + + Args: + invoices: 发票列表,需包含 "价税合计" 字段 + directory: 支付截图所在目录 + tolerance: 金额匹配容差比例(默认 0.05 = 5%) + + Returns: + 以支付记录为主键的结果列表,每条记录包含: + - 刷卡日期、公务卡号、刷卡金额(支付信息) + - 关联发票列表(_matched_invoices) + - 发票详情备注 + - 未匹配发票单独作为一条无刷卡信息的记录 + """ + cards = _extract_all_cards(directory) + if not cards: + log.warning("无刷卡记录可供匹配,发票将保持原状") + # 无刷卡记录时,每张发票作为独立记录返回 + return _invoices_to_records(invoices) + + # 解析金额 + for card in cards: + card["_amount"] = _safe_float(card.get("刷卡金额")) + for inv in invoices: + inv["_amount"] = _safe_float(inv.get("价税合计")) + + # 数据校验 + total_invoices = sum(inv["_amount"] for inv in invoices) + total_cards = sum(card["_amount"] for card in cards) + log.info( + f"金额校验: 发票总额 ¥{total_invoices:.2f}, 刷卡总额 ¥{total_cards:.2f}, " + f"发票数 {len(invoices)}, 刷卡数 {len(cards)}" + ) + + total_tolerance = _relative_tolerance(max(total_invoices, total_cards), tolerance) + if total_invoices < total_cards - total_tolerance: + log.warning( + f"发票总额 (¥{total_invoices:.2f}) 小于刷卡总额 (¥{total_cards:.2f})," + f"超出容差 {tolerance * 100:.0f}%,匹配结果可能有偏差" + ) + + # 按金额降序排序 + cards.sort(key=lambda c: c["_amount"], reverse=True) + invoices.sort(key=lambda i: i["_amount"], reverse=True) + + # 执行匹配,返回 {card_index: [invoice_indices]} 的映射 + card_to_invoices = _match(cards, invoices, tolerance) + + # 构建支付记录列表 + records = _build_payment_records(cards, invoices, card_to_invoices) + + # 清理内部字段 + for inv in invoices: + inv.pop("_amount", None) + for card in cards: + card.pop("_amount", None) + + # 统计 + matched_invoices = sum(len(inv_list) for inv_list in card_to_invoices.values()) + unmatched_count = len(invoices) - matched_invoices + log.info(f"匹配完成: {len(records)} 条支付记录, {matched_invoices}/{len(invoices)} 张发票已关联") + if unmatched_count: + log.info(f"未匹配发票: {unmatched_count} 张(已单独列为记录)") + + return records + + +def _match( + cards: list[dict[str, Any]], + invoices: list[dict[str, Any]], + tolerance: float, +) -> dict[int, list[int]]: + """执行匹配,返回 {card_index: [invoice_indices]} 的映射 + + tolerance 为相对容差比例(如 0.05 表示 5%) + """ + result: dict[int, list[int]] = {} + assigned: set[int] = set() + + if len(invoices) == len(cards): + _match_one_to_one(invoices, cards, tolerance, assigned, result) + else: + _match_one_to_many(invoices, cards, tolerance, assigned, result) + + return result + + +def _match_one_to_one( + invoices: list[dict[str, Any]], + cards: list[dict[str, Any]], + tolerance: float, + assigned: set[int], + result: dict[int, list[int]], +) -> None: + """一对一匹配:发票数等于刷卡数,按金额从大到小依次配对 + + tolerance 为相对容差比例,以刷卡金额为基准计算 + """ + for card_idx, card in enumerate(cards): + if card_idx >= len(invoices): + break + inv = invoices[card_idx] + diff = abs(inv["_amount"] - card["_amount"]) + card_tol = _relative_tolerance(card["_amount"], tolerance) + if diff <= card_tol: + assigned.add(card_idx) + result[card_idx] = [card_idx] + log.info( + f"[一对一] {inv.get('发票号码', '未知')} ¥{inv['_amount']:.2f} " + f"↔ {card.get('_source_file', '未知')} ¥{card['_amount']:.2f}" + ) + else: + log.warning( + f"[一对一] 金额偏差超出容差: " + f"{inv.get('发票号码', '未知')} ¥{inv['_amount']:.2f} " + f"vs ¥{card['_amount']:.2f} (差 ¥{diff:.2f}, 容差 ¥{card_tol:.2f})" + ) + + +def _match_one_to_many( + invoices: list[dict[str, Any]], + cards: list[dict[str, Any]], + tolerance: float, + assigned: set[int], + result: dict[int, list[int]], +) -> None: + """一对多匹配:一张刷卡可能对应多张发票,按金额从大到小贪心匹配 + + tolerance 为相对容差比例(如 0.05 表示 5%),以刷卡金额为基准计算 + + 匹配分两阶段: + 1. 精确匹配:先扫描金额完全相等(差值 <= 0.01 元)的发票-刷卡对,直接锁定 + 2. 贪心匹配:剩余未分配的发票和刷卡记录走贪心凑金额 + """ + + # ---- 阶段 1:精确匹配(金额差 <= 0.01 元视为相等)---- + exact_tolerance = 0.01 + for card_idx, card in enumerate(cards): + card_amount = card["_amount"] + if card_amount <= 0: + continue + + for idx, inv in enumerate(invoices): + if idx in assigned: + continue + inv_amount = inv["_amount"] + if inv_amount <= 0: + continue + + if abs(inv_amount - card_amount) <= exact_tolerance: + assigned.add(idx) + result[card_idx] = [idx] + log.info( + f"[一对多-精确] {inv.get('发票号码', '未知')} ¥{inv_amount:.2f} " + f"↔ {card.get('_source_file', '未知')} ¥{card_amount:.2f}" + ) + break # 每张刷卡只精确匹配一张发票 + + # ---- 阶段 2:贪心匹配(仅处理未精确匹配的刷卡记录)---- + for card_idx, card in enumerate(cards): + if card_idx in result: # 已在阶段 1 精确匹配 + continue + + card_amount = card["_amount"] + if card_amount <= 0: + continue + + # 以刷卡金额为基准计算相对容差 + card_tol = _relative_tolerance(card_amount, tolerance) + + remaining = card_amount + matched_indices: list[int] = [] + + for idx, inv in enumerate(invoices): + if idx in assigned: + continue + if remaining <= card_tol: + break + + inv_amount = inv["_amount"] + if inv_amount <= 0: + continue + + # 最后一张发票:金额 + 容差 >= remaining 即可 + # 中间发票:金额不超过 remaining + 容差 + if inv_amount + card_tol >= remaining: + is_match = True + else: + is_match = inv_amount <= remaining + card_tol + + if is_match: + assigned.add(idx) + matched_indices.append(idx) + remaining -= inv_amount + if remaining <= card_tol: + break + + # 回滚:如果匹配后 remaining 为负且超出容差 + if remaining < -card_tol and matched_indices: + last_idx = matched_indices.pop() + assigned.discard(last_idx) + remaining += invoices[last_idx]["_amount"] + + # 记录匹配结果 + if matched_indices: + result[card_idx] = matched_indices + for idx in matched_indices: + inv = invoices[idx] + log.info( + f"[一对多-贪心] {inv.get('发票号码', '未知')} ¥{inv['_amount']:.2f} " + f"→ {card.get('_source_file', '未知')} ¥{card['_amount']:.2f}" + ) + + +def _build_payment_records( + cards: list[dict[str, Any]], + invoices: list[dict[str, Any]], + card_to_invoices: dict[int, list[int]], +) -> list[dict[str, Any]]: + """构建以支付记录为主键的结果列表""" + records: list[dict[str, Any]] = [] + + # 已有匹配记录的支付 + for card_idx, inv_indices in card_to_invoices.items(): + card = cards[card_idx] + matched_invs = [invoices[idx] for idx in inv_indices] + + record = { + "刷卡日期": card.get("刷卡日期", ""), + "公务卡号": card.get("公务卡号", ""), + "刷卡金额": str(card["_amount"]), + "关联发票数": str(len(matched_invs)), + "发票详情": _build_invoice_summary(matched_invs), + "备注": "", + "_matched_invoices": matched_invs, + } + records.append(record) + + # 未匹配的发票,单独作为记录 + matched_indices = set() + for inv_indices in card_to_invoices.values(): + matched_indices.update(inv_indices) + + unmatched = [inv for idx, inv in enumerate(invoices) if idx not in matched_indices] + for inv in unmatched: + record = { + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "关联发票数": "1", + "发票详情": _build_invoice_summary([inv]), + "备注": "未匹配到支付记录", + "_matched_invoices": [inv], + } + records.append(record) + + return records + + +def _invoices_to_records(invoices: list[dict[str, Any]]) -> list[dict[str, Any]]: + """无刷卡记录时,将每张发票转为独立记录""" + records = [] + for inv in invoices: + record = { + "刷卡日期": "", + "公务卡号": "", + "刷卡金额": "", + "关联发票数": "1", + "发票详情": _build_invoice_summary([inv]), + "备注": "", + "_matched_invoices": [inv], + } + records.append(record) + return records diff --git a/src/doc/pdf.py b/src/doc/pdf.py new file mode 100644 index 0000000..9388280 --- /dev/null +++ b/src/doc/pdf.py @@ -0,0 +1,42 @@ +"""PDF 文件发现与文本提取 + +从 PDF 发票文件中提取原始文本内容。 + +对外接口: + find_pdf_files(directory) -> list[Path] 查找目录下所有 PDF + extract_text_from_pdf(filepath) -> str 提取 PDF 文本 +""" + +from pathlib import Path + +from .. import get_logger + +log = get_logger("pdf") + + +def find_pdf_files(directory: str = ".") -> list[Path]: + """查找目录下所有 PDF 文件(非递归)""" + pdf_dir = Path(directory) + if not pdf_dir.exists(): + return [] + return sorted(pdf_dir.glob("*.pdf")) + + +def extract_text_from_pdf(filepath: Path) -> str: + """从单个 PDF 中提取全部文本""" + try: + import pdfplumber + except ImportError as err: + raise ImportError("缺少 pdfplumber,请执行: uv pip install pdfplumber") from err + + try: + parts = [] + with pdfplumber.open(filepath) as pdf: + for page in pdf.pages: + text = page.extract_text() + if text: + parts.append(text) + return "\n".join(parts) + except Exception as e: + log.error(f"无法读取 {filepath.name}: {e}") + return "" diff --git a/src/doc/prompt.py b/src/doc/prompt.py new file mode 100644 index 0000000..8419306 --- /dev/null +++ b/src/doc/prompt.py @@ -0,0 +1,26 @@ +""" +LLM 提示词模板 + +从 src/prompts/ 目录加载 .md 文件作为提示词模板。 +""" + +import os + +_PROMPTS_DIR = os.path.join(os.path.dirname(__file__), "prompts") + + +def _load_prompt(filename: str) -> str: + """从 prompts 目录加载提示词文件内容。""" + path = os.path.join(_PROMPTS_DIR, filename) + with open(path, encoding="utf-8") as f: + return f.read() + + +def build_invoice_system_prompt() -> str: + """构建发票提取系统提示词。""" + return _load_prompt("invoice_system.md") + + +def build_card_info_system_prompt() -> str: + """构建支付截图信息提取系统提示词。""" + return _load_prompt("card_info_system.md") diff --git a/src/doc/prompts/card_info_system.md b/src/doc/prompts/card_info_system.md new file mode 100644 index 0000000..e9c50d5 --- /dev/null +++ b/src/doc/prompts/card_info_system.md @@ -0,0 +1,11 @@ +# 支付截图信息提取系统提示词 + +你是财务支付截图信息提取助手。你的任务是从支付截图(银行转账记录、微信/支付宝付款凭证等)中提取结构化信息,并以 JSON 格式返回。 + +需要提取的字段(全部必填,无法识别时返回空字符串): + +1. 刷卡日期: 支付发生的日期,格式为 YYYY/M/D +2. 刷卡金额: 实际支付金额,只保留数字(如 123.45) +3. 公务卡号: 付款银行卡号,如果截图中有显示则提取,没有则返回空字符串 + +严格只输出 JSON,不要输出任何其他文字、Markdown 标记或解释。 \ No newline at end of file diff --git a/src/doc/prompts/invoice_system.md b/src/doc/prompts/invoice_system.md new file mode 100644 index 0000000..f5c4307 --- /dev/null +++ b/src/doc/prompts/invoice_system.md @@ -0,0 +1,33 @@ +# 发票提取系统提示词 + +你是财务文档信息提取助手。你的任务是从发票文本中提取结构化信息,并以 JSON 格式返回。 + +需要提取的字段(全部必填,无法识别时返回空字符串): +先判断发票类型,如果是高铁票/或者火车票,返回如下字段: +1. 发票类型: "高铁票" +2. 发票号码: 发票的唯一编号 +3. 开票日期: 格式为 YYYY/M/D +4. 乘车日期: 格式为 YYYY/M/D +5. 出发站: 没有留空 +6. 到达站: 没有留空 +7. 座位等级: 没有留空 +8. 车次: 没有留空 +9. 人员姓名: 没有留空 +10. 价税合计:就是票价,找不到票价信息才填`0`,能够找到尽量填写找到的信息 + +如果是酒店住宿(酒店住宿通产包含关键字:住宿服务,酒店,生产生活服务等,请仔细分析,这种发票和普通发票类似),返回如下字段: +1. 发票类型: "酒店住宿" +2. 发票号码: 发票的唯一编号 +3. 开票日期: 格式为 YYYY/M/D +4. 价税合计: 金额数字 + +如果是普通发票,返回如下字段: +1. 发票类型: "普通发票" +2. 发票号码: 发票的唯一编号 +3. 开票日期: 格式为 YYYY/M/D +4. 项目名称: 商品或服务名称,总结的人能看懂 +5. 规格型号: 规格描述 +6. 价税合计: 金额数字 +7. 销售方名称: 卖方全称 + +严格只输出 JSON,不要输出任何其他文字、Markdown 标记或解释。 \ No newline at end of file diff --git a/run.py b/src/main.py similarity index 67% rename from run.py rename to src/main.py index 12bccff..71cdb83 100644 --- a/run.py +++ b/src/main.py @@ -1,17 +1,14 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ 财务报销自动化 依次执行: 1. 发票提取 — 从 PDF 发票提取信息,生成 invoice_summary.csv - 2. OCR 识别 — 从支付截图识别刷卡信息,回填 CSV - 3. 报销提交 — 打开浏览器登录财务系统并自动填报 + 2. 报销提交 — 打开浏览器登录财务系统并自动填报 用法: python run.py # 全流程 python run.py --step invoice # 仅发票提取 - python run.py --step ocr # 仅 OCR 识别 python run.py --step submit # 仅浏览器填报 python run.py -u 工号 -p 密码 # 覆盖登录凭据 """ @@ -21,28 +18,30 @@ import sys from pathlib import Path # 确保项目根目录在 sys.path 中 -sys.path.insert(0, str(Path(__file__).parent.resolve())) +sys.path.insert(0, str(Path(__file__).parent.resolve().parent)) -from app.pipeline import run_pipeline +from src.pipeline import run_pipeline -def main(): +def main() -> None: parser = argparse.ArgumentParser( - description="财务报销自动化 - 发票提取 → OCR 识别 → 浏览器填报", + description="财务报销自动化 - 发票提取 → 浏览器填报", ) parser.add_argument( "--step", - choices=["all", "invoice", "ocr", "submit"], + choices=["all", "invoice", "submit"], default="all", help="执行步骤 (默认: all)", ) parser.add_argument( - "-u", "--username", + "-u", + "--username", default=None, help="信息门户登录账号(覆盖 config.json)", ) parser.add_argument( - "-p", "--password", + "-p", + "--password", default=None, help="信息门户登录密码(覆盖 config.json)", ) @@ -57,4 +56,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/pipeline.py b/src/pipeline.py new file mode 100644 index 0000000..021d218 --- /dev/null +++ b/src/pipeline.py @@ -0,0 +1,136 @@ +""" +报销全流程编排 + +将发票提取 → 浏览器填报串联为一条管道, +数据在内存中流转,同时生成 CSV 中间产物。 + +发票类型区分: + - 差旅发票(高铁票/酒店住宿):不生成易耗品出库单,走差旅报销流程 + - 普通发票:生成易耗品出库单,走普通报销流程 +""" + +from pathlib import Path +from typing import Any + +from . import get_logger +from .config import load_config +from .doc.extractor import extract_invoices +from .doc.invoice import ( + classify_invoice_batch, + load_invoices_from_csv, + save_invoice_csv, +) +from .doc.invoice import ( + save_csv as save_payment_csv, +) + +log = get_logger("pipeline") + + +def _find_upload_directory(project_dir: Path) -> Path | None: + """自动发现 uploads 目录下最新且包含 PDF 的会话文件夹""" + uploads_base = project_dir / "src" / "web" / "uploads" + if not uploads_base.is_dir(): + return None + + # 只选包含 PDF 的目录 + valid_dirs = [d for d in uploads_base.iterdir() if d.is_dir() and list(d.glob("*.pdf"))] + if not valid_dirs: + return None + + # 按修改时间排序,取最新 + session_dirs = sorted( + valid_dirs, + key=lambda d: d.stat().st_mtime, + reverse=True, + ) + return session_dirs[0] + + +def _classify_from_csv(csv_path: Path) -> dict[str, list[dict[str, Any]]]: + """从已生成的 CSV 中读取发票数据并按类型分组""" + rows = load_invoices_from_csv(csv_path) + if rows is None: + return {"travel": [], "general": []} + return classify_invoice_batch(rows) + + +def run_pipeline(step: str = "all", username: str | None = None, password: str | None = None) -> int: + """执行报销流程 + + Args: + step: all | invoice | submit + username: 覆盖 config.json 中的用户名 + password: 覆盖 config.json 中的密码 + """ + config = load_config() + if username: + config["username"] = username + if password: + config["password"] = password + + # 工作目录(项目根目录) + project_dir = Path(__file__).parent.parent + + # -------------------------------------------------- + # Step 1: 发票提取 + # -------------------------------------------------- + payment_records: list[dict[str, str]] | None = None + groups: dict[str, list[dict[str, str]]] | None = None + + if step in ("all", "invoice"): + log.info("=" * 60) + log.info("[1/2] 发票提取") + log.info("=" * 60) + + payment_records, groups = extract_invoices(str(project_dir)) + if not payment_records: + log.error("未提取到任何发票数据") + return 1 + + save_payment_csv(payment_records, project_dir / "payment_records.csv") + save_invoice_csv(payment_records, project_dir / "invoice_summary.csv") + + # 打印分类结果 + log.info(f"发票分类: 差旅 {len(groups['travel'])} 张, 普通 {len(groups['general'])} 张") + + if step == "invoice": + log.info("[1/2] 发票提取 完成") + return 0 + + # -------------------------------------------------- + # Step 2: 浏览器填报 + # -------------------------------------------------- + if step in ("all", "submit"): + log.info("=" * 60) + log.info("[2/2] 报销提交") + log.info("=" * 60) + + from .bot import load_invoice_data, run_bot + + csv_path = project_dir / "payment_records.csv" + bot_invoices = load_invoice_data(str(csv_path), config) + + # 根据发票类型选择填报模式 + if groups is None: + groups = _classify_from_csv(csv_path) + + if groups["travel"] and not groups["general"]: + log.info("检测到纯差旅发票,使用差旅报销模式") + # TODO: 差旅报销填报流程 + run_bot(config, bot_invoices) + else: + log.info("检测到普通发票,使用普通报销模式") + run_bot(config, bot_invoices) + + if step == "submit": + log.info("[2/2] 报销提交 完成") + return 0 + + # -------------------------------------------------- + # 全流程完成 + # -------------------------------------------------- + log.info("=" * 60) + log.info("全流程执行完毕") + log.info("=" * 60) + return 0 diff --git a/src/web/README.md b/src/web/README.md new file mode 100644 index 0000000..e0c9336 --- /dev/null +++ b/src/web/README.md @@ -0,0 +1,83 @@ +--- +last_reviewed: 2026-06-09 +--- + +# src/web 模块设计说明 + +## 设计思路 + +`src/web` 是一个基于 Flask 的轻量级 Web 界面,为财务报销自动化管道提供可视化操作入口。核心设计原则: + +- **会话隔离**:每次上传生成独立 `session_id`,文件、日志、配置、结果各自隔离在 `uploads//` 目录下,避免并发冲突。 +- **异步处理**:耗时的 PDF 提取、LLM 调用在后台线程执行,前端通过 SSE 实时查看日志流,不阻塞 HTTP 连接。 +- **前后端分离最小化**:前端使用原生 JS + Bootstrap 5,不引入构建工具,保持单页应用轻量可维护。 +- **双模式支持**:PDF 发票提取模式和 CSV 快捷上传模式,后者跳过 LLM 识别和 PDF 解析,直接处理已有发票数据。 + +## 文件结构 + +``` +src/web/ +├── app.py # Flask 应用入口,路由、管道编排、日志收集 +├── templates/ +│ ├── index.html # PC 端主界面(上传、配置、处理、编辑、提交) +│ └── mobile_upload.html # 移动端上传页面(拍照/相册选择) +└── static/ + ├── css/ + │ └── index.css # 全局样式(上传区、日志面板、可编辑表格) + └── js/ + └── index.js # 前端逻辑(上传、SSE 日志、表格编辑、二维码同步) +``` + +## 数据流 + +``` +用户上传文件 → 创建 session → 文件写入 uploads// + ↓ +后台线程执行管道: extract_invoices() → enrich_with_llm() → save_csv() + ↓ +结果写入 session 目录: invoice_summary.csv / result.json / session.log + ↓ +前端 SSE 轮询 result.json 变化 → 显示完成状态 + ↓ +前端加载 CSV 数据 → 可编辑表格展示 → 用户修改后保存 + ↓ +用户点击提交 → run_financial_submit() → bot 自动填报财务系统 +``` + +## API 路由 + +| 方法 | 路径 | 功能 | +|------|------|------| +| GET | `/` | 主界面 | +| POST | `/api/session` | 创建会话,返回 session_id | +| POST | `/api/upload/` | 上传 PDF/图片 | +| POST | `/api/upload-csv/` | 上传 CSV 发票数据 | +| GET | `/api/files/` | 列出会话文件 | +| POST | `/api/process/` | 启动管道(后台线程) | +| GET | `/api/logs/` | SSE 日志流 | +| GET | `/api/data/` | 获取发票数据 JSON | +| POST | `/api/save/` | 保存前端编辑的发票数据 | +| GET | `/api/download//` | 下载生成文件 | +| POST | `/api/submit-financial/` | 手动触发财务系统填报 | +| GET | `/mobile/` | 移动端上传页面 | + +## 关键机制 + +### 日志收集 + +`_SSELogHandler` 将管道日志写入 `session.log`,SSE 端点通过文件偏移量增量读取,实现前端实时日志展示。日志收集器在管道启动时安装,完成后移除,确保线程安全。 + +### 发票类型分流 + +- **差旅发票**(高铁票/酒店住宿):不生成易耗品出库单,走差旅报销流程 +- **普通发票**:生成易耗品出库单(Word 文档),走普通报销流程 + +`classify_invoice_batch()` 根据发票内容自动分类,`_try_fill_consumable_doc()` 仅对普通发票生成出库单。 + +### 移动端同步 + +PC 端生成二维码指向 `/mobile/`,手机端上传的图片通过 `syncFiles()` 轮询同步到 PC 端内存中的 `imgFiles` 列表,实现跨设备协作。文件来源标记(`__source`)区分本地选择和服务器同步,避免重复。 + +### 配置管理 + +配置分两层:项目级 `config.json` 提供默认值,会话级 `uploads//config.json` 存储当次会话覆盖值。前端支持通过上传 `config.json` 快速填充配置表单。 \ No newline at end of file diff --git a/web/app.py b/src/web/app.py similarity index 58% rename from web/app.py rename to src/web/app.py index 39c447a..f87a331 100644 --- a/web/app.py +++ b/src/web/app.py @@ -1,14 +1,17 @@ """ 财务报销自动化 — Web 界面 -用户上传 PDF 发票和支付截图,配置账号信息,自动完成: - 1. 发票提取 2. OCR 识别 3. 浏览器填报(可选) +用户上传 PDF 发票,配置账号信息,自动完成: + 1. 发票提取 2. 浏览器填报(可选) -启动: python web/app.py +发票类型区分: + - 差旅发票(高铁票/酒店住宿):不生成易耗品出库单,走差旅报销流程 + - 普通发票:生成易耗品出库单,走普通报销流程 + +启动: uv run python src/web/app.py 访问: http://localhost:5000 """ -import io import json import logging import sys @@ -16,29 +19,38 @@ import threading import time import uuid from pathlib import Path +from typing import Any from urllib.parse import quote from flask import Flask, Response, jsonify, render_template, request, stream_with_context # 确保项目根目录在 sys.path -PROJECT_ROOT = Path(__file__).parent.parent.resolve() +PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent sys.path.insert(0, str(PROJECT_ROOT)) -from app.config import load_config as load_project_config -from app.extractor import extract_invoices, save_csv, save_markdown -from app.fill_consumable_doc import ( +from src import get_logger # noqa: E402, I001 +from src.config import load_config as load_project_config # noqa: E402, I001 +from src.doc.extractor import ( # noqa: E402, I001 + extract_invoices, +) +from src.doc.fill_consumable_doc import ( # noqa: E402, I001 CONSUMABLE_DOC_FILENAME, fill_consumable_from_template, ) -from app import get_logger -from app.ocr import enrich_with_ocr, _save_csv as save_ocr_csv, save_markdown_from_csv, _load_csv as load_ocr_csv +from src.doc.invoice import ( # noqa: E402, I001 + classify_invoice_batch, + load_csv, + load_invoice_csv, + save_csv as save_payment_csv, + save_invoice_csv, +) fill_log = get_logger("fill_consumable_doc") CONSUMABLE_TEMPLATE = PROJECT_ROOT / CONSUMABLE_DOC_FILENAME app = Flask(__name__, template_folder="templates") -UPLOAD_BASE = PROJECT_ROOT / "web" / "uploads" +UPLOAD_BASE = PROJECT_ROOT / "src" / "web" / "uploads" SESSION_LOG_FILE = "session.log" SESSION_RESULT_FILE = "result.json" @@ -47,6 +59,7 @@ SESSION_RESULT_FILE = "result.json" # 日志收集器 — 捕获管道日志到文件,SSE 端点通过 tail -f 读取 # ================================================================ + class _SSELogHandler(logging.Handler): """将日志写入指定文件(线程安全)""" @@ -55,7 +68,7 @@ class _SSELogHandler(logging.Handler): self._lock = threading.Lock() self._file = open(log_path, "w", encoding="utf-8") - def emit(self, record: logging.LogRecord): + def emit(self, record: logging.LogRecord) -> None: try: msg = self.format(record) + "\n" with self._lock: @@ -64,7 +77,7 @@ class _SSELogHandler(logging.Handler): except Exception: pass - def close_file(self): + def close_file(self) -> None: try: self._file.close() except Exception: @@ -82,7 +95,7 @@ def _install_log_collector(session_dir: Path) -> _SSELogHandler: handler.setFormatter(fmt) handler.setLevel(logging.INFO) - for name in ["extractor", "ocr", "pipeline", "bot", "fill_consumable_doc"]: + for name in ["extractor", "llm_extractor", "matcher", "pipeline", "bot", "fill_consumable_doc"]: logger = logging.getLogger(name) logger.setLevel(logging.INFO) logger.addHandler(handler) @@ -90,8 +103,8 @@ def _install_log_collector(session_dir: Path) -> _SSELogHandler: return handler -def _remove_log_collector(handler: _SSELogHandler): - for name in ["extractor", "ocr", "pipeline", "bot", "fill_consumable_doc"]: +def _remove_log_collector(handler: _SSELogHandler) -> None: + for name in ["extractor", "llm_extractor", "matcher", "pipeline", "bot", "fill_consumable_doc"]: logging.getLogger(name).removeHandler(handler) handler.close_file() @@ -101,7 +114,7 @@ def _remove_log_collector(handler: _SSELogHandler): # ================================================================ -def _load_session_config(session_dir: Path) -> dict: +def _load_session_config(session_dir: Path) -> dict[str, Any]: config = load_project_config() cfg_path = session_dir / "config.json" if cfg_path.exists(): @@ -110,31 +123,95 @@ def _load_session_config(session_dir: Path) -> dict: return config +def _resolve_payment_csv(session_dir: Path) -> Path | None: + """查找支付记录 CSV(payment_records.csv)""" + csv_path = session_dir / "payment_records.csv" + if csv_path.exists(): + return csv_path + for f in session_dir.glob("*.csv"): + if f.name != SESSION_RESULT_FILE: + return f + return None + + def _resolve_invoice_csv(session_dir: Path) -> Path | None: + """查找发票级别 CSV(invoice_summary.csv)""" csv_path = session_dir / "invoice_summary.csv" if csv_path.exists(): return csv_path for f in session_dir.glob("*.csv"): - return f + if f.name != SESSION_RESULT_FILE: + return f return None -def _try_fill_consumable_doc(session_dir: Path, config: dict) -> dict: - """根据 CSV 填写易耗品出库单,供会话目录下载。""" +def _has_general_invoices(rows: list[dict[str, str]]) -> bool: + """检查发票列表中是否包含普通发票(需要生成易耗品出库单)""" + invoices = [] + for row in rows: + # 支付记录格式:从 _invoices_json 还原 + invoices_json = row.get("_invoices_json", "") + if invoices_json: + try: + invoices.extend(json.loads(invoices_json)) + except json.JSONDecodeError: + pass + # 发票级别格式:直接使用 + elif "发票类型" in row: + invoices.append(row) + if not invoices: + return False + groups = classify_invoice_batch(invoices) + return len(groups["general"]) > 0 + + +def _get_invoice_groups(rows: list[dict[str, str]]) -> dict[str, int]: + """统计发票类型分布""" + invoices = [] + for row in rows: + # 支付记录格式:从 _invoices_json 还原 + invoices_json = row.get("_invoices_json", "") + if invoices_json: + try: + invoices.extend(json.loads(invoices_json)) + except json.JSONDecodeError: + pass + # 发票级别格式:直接使用 + elif "发票类型" in row: + invoices.append(row) + groups = classify_invoice_batch(invoices) + return { + "travel_count": len(groups["travel"]), + "general_count": len(groups["general"]), + } + + +def _try_fill_consumable_doc(session_dir: Path, config: dict[str, Any]) -> dict[str, Any]: + """根据 CSV 填写易耗品出库单,供会话目录下载。 + + 仅当存在普通发票时才生成出库单。纯差旅发票跳过。 + """ if not CONSUMABLE_TEMPLATE.exists(): fill_log.warning("出库单模板不存在: %s", CONSUMABLE_TEMPLATE) return {"ok": False, "error": "出库单模板不存在,请将模板放在项目根目录"} - csv_path = _resolve_invoice_csv(session_dir) + csv_path = _resolve_payment_csv(session_dir) if csv_path is None: return {"ok": False, "error": "未找到发票 CSV"} + # 检查是否有普通发票 + rows = load_csv(csv_path) + if rows is None: + return {"ok": False, "error": "CSV 读取失败"} + + if not _has_general_invoices(rows): + fill_log.info("纯差旅发票,跳过易耗品出库单生成") + return {"ok": False, "skipped": True, "error": "差旅发票无需生成易耗品出库单"} + out_doc = session_dir / CONSUMABLE_DOC_FILENAME try: fill_log.info("开始填写出库单: %s", out_doc.name) - fill_consumable_from_template( - csv_path, CONSUMABLE_TEMPLATE, out_doc, config=config - ) + fill_consumable_from_template(csv_path, CONSUMABLE_TEMPLATE, out_doc, config=config) fill_log.info("出库单填写完成") return {"ok": True, "doc_filename": CONSUMABLE_DOC_FILENAME} except ImportError: @@ -145,11 +222,16 @@ def _try_fill_consumable_doc(session_dir: Path, config: dict) -> dict: return {"ok": False, "error": str(e)} -def _append_doc_download(result: dict, session_id: str, doc_fill: dict) -> None: +def _append_doc_download(result: dict[str, Any], session_id: str, doc_fill: dict[str, Any]) -> None: if doc_fill.get("ok"): fn = doc_fill["doc_filename"] result["doc_url"] = f"/api/download/{session_id}/{quote(fn)}" result["doc_ok"] = True + elif doc_fill.get("skipped"): + # 差旅发票,跳过出库单生成(不是错误) + result["doc_ok"] = None + result["doc_skipped"] = True + result["doc_message"] = doc_fill.get("error", "") else: result["doc_ok"] = False result["doc_error"] = doc_fill.get("error", "未知错误") @@ -159,78 +241,110 @@ def _append_doc_download(result: dict, session_id: str, doc_fill: dict) -> None: # 管道入口 # ================================================================ -def run_pipeline_web(session_dir: Path, config: dict): - """在 Web 会话目录中执行提取+OCR,结果写入 session 目录下的文件 - + +def run_pipeline_web(session_dir: Path, config: dict[str, Any]) -> dict[str, Any]: + """在 Web 会话目录中执行发票提取,结果写入 session 目录下的文件 + 注意:不再自动提交财务系统。提交通由 /api/submit-financial/ 触发。 """ start = time.time() # ---- Step 1: 发票提取 ---- - invoices = extract_invoices(str(session_dir)) + invoices, groups = extract_invoices(str(session_dir)) if not invoices: return {"ok": False, "error": "未提取到任何发票数据"} - save_csv(invoices, session_dir / "invoice_summary.csv") - save_markdown(invoices, session_dir / "invoice_summary.md") + # 保存两个 CSV:支付记录级别(供 bot/出库单使用)和发票级别(供人工参考) + save_payment_csv(invoices, session_dir / "payment_records.csv") + save_invoice_csv(invoices, session_dir / "invoice_summary.csv") - # ---- Step 2: OCR 识别 ---- - csv_path = session_dir / "invoice_summary.csv" - rows = load_ocr_csv(csv_path) - if rows is None: - return {"ok": False, "error": "CSV 读取失败"} - - rows = enrich_with_ocr(rows, str(session_dir)) - save_ocr_csv(csv_path, rows) - save_markdown_from_csv(csv_path, rows) + # 统计发票总数 + invoice_count = sum(len(inv.get("_matched_invoices", [])) for inv in invoices) elapsed = time.time() - start result = { "ok": True, "elapsed": f"{elapsed:.1f}s", - "invoice_count": len(rows), + "invoice_count": invoice_count, "csv_url": f"/api/download/{session_dir.name}/invoice_summary.csv", - "md_url": f"/api/download/{session_dir.name}/invoice_summary.md", + # 发票类型统计 + "travel_count": len(groups["travel"]), + "general_count": len(groups["general"]), } doc_fill = _try_fill_consumable_doc(session_dir, config) _append_doc_download(result, session_dir.name, doc_fill) return result -def run_csv_pipeline_web(session_dir: Path, config: dict, csv_filename: str): - """直接使用上传的 CSV 文件,跳过 PDF 提取和 OCR""" +def run_csv_pipeline_web(session_dir: Path, config: dict[str, Any], csv_filename: str) -> dict[str, Any]: + """直接使用上传的 CSV 文件,跳过 PDF 提取""" start = time.time() csv_path = session_dir / csv_filename if not csv_path.exists(): return {"ok": False, "error": "CSV 文件不存在"} - # 读取 CSV 行数 - rows = load_ocr_csv(csv_path) + # 尝试读取支付记录格式 + rows = load_csv(csv_path) if rows is None: - return {"ok": False, "error": "CSV 读取失败"} + # 尝试读取发票级别格式 + invoice_rows = load_invoice_csv(csv_path) + if invoice_rows is None: + return {"ok": False, "error": "CSV 读取失败"} + # 发票级别格式:直接统计 + type_stats = _get_invoice_groups(invoice_rows) + elapsed = time.time() - start + return { + "ok": True, + "elapsed": f"{elapsed:.1f}s", + "invoice_count": len(invoice_rows), + "csv_url": f"/api/download/{session_dir.name}/{csv_filename}", + "travel_count": type_stats["travel_count"], + "general_count": type_stats["general_count"], + } + + # 支付记录格式:统计发票类型 + type_stats = _get_invoice_groups(rows) elapsed = time.time() - start result = { "ok": True, "elapsed": f"{elapsed:.1f}s", - "invoice_count": len(rows), + "invoice_count": type_stats["travel_count"] + type_stats["general_count"], "csv_url": f"/api/download/{session_dir.name}/{csv_filename}", + "travel_count": type_stats["travel_count"], + "general_count": type_stats["general_count"], } doc_fill = _try_fill_consumable_doc(session_dir, config) _append_doc_download(result, session_dir.name, doc_fill) return result -def run_financial_submit(session_dir: Path, config: dict) -> dict: - """执行财务系统填报(从前端确认后调用)""" - csv_path = session_dir / "invoice_summary.csv" +def run_financial_submit(session_dir: Path, config: dict[str, Any]) -> dict[str, Any]: + """执行财务系统填报(从前端确认后调用) + + 根据发票类型选择填报模式: + - 纯差旅发票:差旅报销模式(TODO) + - 含普通发票:普通报销模式 + """ + csv_path = session_dir / "payment_records.csv" if not csv_path.exists(): return {"ok": False, "error": "未找到发票数据,请先处理"} - from app.bot import load_invoice_data, run_bot_web + from src.bot import load_invoice_data, run_bot_web bot_invoices = load_invoice_data(str(csv_path), config) + + # 判断发票类型 + rows = load_csv(csv_path) + if rows: + invoice_groups = _get_invoice_groups(rows) + if invoice_groups["travel_count"] and not invoice_groups["general_count"]: + fill_log.info("检测到纯差旅发票,使用差旅报销模式") + # TODO: 差旅报销填报流程 + else: + fill_log.info("检测到普通发票,使用普通报销模式") + run_bot_web(config, bot_invoices, session_dir) return {"ok": True} @@ -239,13 +353,14 @@ def run_financial_submit(session_dir: Path, config: dict) -> dict: # Flask 路由 # ================================================================ + @app.route("/") -def index(): +def index() -> Any: return render_template("index.html") @app.route("/api/session", methods=["POST"]) -def create_session(): +def create_session() -> Any: """创建上传会话,返回 session_id""" sid = uuid.uuid4().hex[:12] session_dir = UPLOAD_BASE / sid @@ -254,7 +369,7 @@ def create_session(): @app.route("/api/upload/", methods=["POST"]) -def upload_file(session_id: str): +def upload_file(session_id: str) -> Any: """上传 PDF 或图片""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): @@ -270,8 +385,8 @@ def upload_file(session_id: str): @app.route("/api/upload-csv/", methods=["POST"]) -def upload_csv(session_id: str): - """上传 CSV 发票数据文件(跳过 PDF 提取和 OCR)""" +def upload_csv(session_id: str) -> Any: + """上传 CSV 发票数据文件(跳过 PDF 提取)""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): return session_dir @@ -286,23 +401,20 @@ def upload_csv(session_id: str): @app.route("/api/files/", methods=["GET"]) -def list_files(session_id: str): +def list_files(session_id: str) -> Any: """列出会话目录中的文件""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): return session_dir pdfs = sorted(f.name for f in session_dir.glob("*.pdf")) - imgs = sorted( - f.name for ext in {".png", ".jpg", ".jpeg", ".bmp", ".webp"} - for f in session_dir.glob(f"*{ext}") - ) + imgs = sorted(f.name for ext in {".png", ".jpg", ".jpeg", ".bmp", ".webp"} for f in session_dir.glob(f"*{ext}")) return jsonify({"pdfs": pdfs, "images": imgs}) @app.route("/api/process/", methods=["POST"]) -def start_process(session_id: str): - """启动管道处理(仅提取+OCR,不自动提交财务系统)""" +def start_process(session_id: str) -> Any: + """启动管道处理(仅发票提取,不自动提交财务系统)""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): return session_dir @@ -320,7 +432,7 @@ def start_process(session_id: str): # 在后台线程执行 handler = _install_log_collector(session_dir) - def _run(): + def _run() -> None: result = {"ok": False, "error": "未知错误"} try: if mode == "csv": @@ -338,7 +450,7 @@ def start_process(session_id: str): result = run_pipeline_web(session_dir, config) except BaseException as e: result = {"ok": False, "error": str(e)} - if isinstance(e, (KeyboardInterrupt, SystemExit)): + if isinstance(e, KeyboardInterrupt | SystemExit): raise finally: try: @@ -356,13 +468,13 @@ def start_process(session_id: str): @app.route("/api/logs/") -def stream_logs(session_id: str): +def stream_logs(session_id: str) -> Any: """SSE 日志流""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): return session_dir - def generate(): + def generate() -> Any: # 先发送已有日志 log_file = session_dir / SESSION_LOG_FILE last_size = 0 @@ -399,7 +511,7 @@ def stream_logs(session_id: str): @app.route("/api/download//") -def download_file(session_id: str, filename: str): +def download_file(session_id: str, filename: str) -> Any: """下载生成的文件""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): @@ -415,8 +527,6 @@ def download_file(session_id: str, filename: str): mimetype = "application/msword" elif safe_name.endswith(".csv"): mimetype = "text/csv; charset=utf-8" - elif safe_name.endswith(".md"): - mimetype = "text/markdown; charset=utf-8" else: mimetype = "application/octet-stream" @@ -428,42 +538,86 @@ def download_file(session_id: str, filename: str): ) +@app.route("/api/config/", methods=["GET"]) +def get_session_config(session_id: str) -> Any: + """获取当前会话的配置(供前端回填表单)""" + session_dir = _validate_session(session_id) + if isinstance(session_dir, tuple): + return session_dir + + config = load_project_config() + cfg_path = session_dir / "config.json" + if cfg_path.exists(): + with open(cfg_path, encoding="utf-8") as f: + config.update(json.load(f)) + # 只返回前端需要的字段 + return jsonify( + { + "username": config.get("username", ""), + "password": "", # 不返回密码 + "default_name": config.get("default_name", ""), + "default_card_no": config.get("default_card_no", ""), + "default_person_id": config.get("default_person_id", ""), + "consumable_storage": config.get("consumable_storage", ""), + } + ) + + @app.route("/api/data/", methods=["GET"]) -def get_invoice_data(session_id: str): +def get_invoice_data(session_id: str) -> Any: """读取发票数据并返回 JSON(供前端表格编辑)""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): return session_dir - csv_path = session_dir / "invoice_summary.csv" - if not csv_path.exists(): - # CSV 模式下可能是其他文件名 - csv_files = list(session_dir.glob("*.csv")) - csv_files = [f for f in csv_files if f.name != SESSION_RESULT_FILE] - if csv_files: - csv_path = csv_files[0] - else: - return jsonify({"error": "未找到发票数据,请先处理"}), 404 + # 优先读取支付记录 CSV + payment_csv = session_dir / "payment_records.csv" + if payment_csv.exists(): + rows = load_csv(payment_csv) + if rows is not None: + data: list[dict[str, Any]] = [] + for i, row in enumerate(rows): + entry: dict[str, Any] = dict(row) + entry["__row"] = i + data.append(entry) + fields = [k for k in rows[0].keys() if not k.startswith("__")] if rows else [] + return jsonify({"csv_filename": payment_csv.name, "fields": fields, "data": data}) - rows = load_ocr_csv(csv_path) - if rows is None: - return jsonify({"error": "CSV 读取失败"}), 500 + # 回退到发票级别 CSV + invoice_csv = session_dir / "invoice_summary.csv" + if invoice_csv.exists(): + rows = load_invoice_csv(invoice_csv) + if rows is not None: + invoice_data: list[dict[str, Any]] = [] + for i, row in enumerate(rows): + entry2: dict[str, Any] = dict(row) + entry2["__row"] = i + invoice_data.append(entry2) + fields = [k for k in rows[0].keys() if not k.startswith("__")] if rows else [] + return jsonify({"csv_filename": invoice_csv.name, "fields": fields, "data": invoice_data}) - # 添加行号用于编辑追踪 - data = [] - for i, row in enumerate(rows): - entry = dict(row) - entry["__row"] = i - data.append(entry) + # 最后尝试任意 CSV + csv_files = list(session_dir.glob("*.csv")) + csv_files = [f for f in csv_files if f.name != SESSION_RESULT_FILE] + if csv_files: + csv_path = csv_files[0] + rows = load_csv(csv_path) + if rows is None: + rows = load_invoice_csv(csv_path) + if rows is not None: + fallback_data: list[dict[str, Any]] = [] + for i, row in enumerate(rows): + entry3: dict[str, Any] = dict(row) + entry3["__row"] = i + fallback_data.append(entry3) + fields = [k for k in rows[0].keys() if not k.startswith("__")] if rows else [] + return jsonify({"csv_filename": csv_path.name, "fields": fields, "data": fallback_data}) - # 返回原始字段顺序(去掉内部字段) - fields = [k for k in rows[0].keys() if not k.startswith('__')] if rows else [] - - return jsonify({"csv_filename": csv_path.name, "fields": fields, "data": data}) + return jsonify({"error": "未找到发票数据,请先处理"}), 404 @app.route("/api/save/", methods=["POST"]) -def save_invoice_data(session_id: str): +def save_invoice_data(session_id: str) -> Any: """保存前端编辑后的发票数据到 CSV""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): @@ -478,7 +632,7 @@ def save_invoice_data(session_id: str): return jsonify({"error": "CSV 文件不存在"}), 404 # 读取原 CSV 获取字段顺序(使用第一个数据的 keys) - original_rows = load_ocr_csv(csv_path) + original_rows = load_csv(csv_path) if original_rows is None or len(original_rows) == 0: return jsonify({"error": "无法读取原始 CSV 结构"}), 500 @@ -494,21 +648,24 @@ def save_invoice_data(session_id: str): row = {k: entry.get(k, "") for k in fieldnames} writer.writerow(row) - resp = {"ok": True} + resp: dict[str, str | bool | None] = {"ok": True} config = _load_session_config(session_dir) doc_fill = _try_fill_consumable_doc(session_dir, config) if doc_fill.get("ok"): fn = doc_fill["doc_filename"] resp["doc_url"] = f"/api/download/{session_id}/{quote(fn)}" resp["doc_ok"] = True + elif doc_fill.get("skipped"): + resp["doc_ok"] = None + resp["doc_skipped"] = True else: resp["doc_ok"] = False - resp["doc_error"] = doc_fill.get("error") + resp["doc_error"] = doc_fill.get("error") or "" return jsonify(resp) @app.route("/api/submit-financial/", methods=["POST"]) -def submit_financial(session_id: str): +def submit_financial(session_id: str) -> Any: """手动触发财务系统填报""" session_dir = _validate_session(session_id) if isinstance(session_dir, tuple): @@ -530,7 +687,7 @@ def submit_financial(session_id: str): # 在后台线程执行提交 handler = _install_log_collector(session_dir) - def _run(): + def _run() -> None: result = {"ok": False, "error": "未知错误"} try: submit_result = run_financial_submit(session_dir, config) @@ -540,13 +697,17 @@ def submit_financial(session_id: str): result = submit_result except BaseException as e: result = {"ok": False, "error": str(e)} - if isinstance(e, (KeyboardInterrupt, SystemExit)): + if isinstance(e, KeyboardInterrupt | SystemExit): raise finally: try: tmp_path = session_dir / (SESSION_RESULT_FILE + ".tmp") with open(tmp_path, "w", encoding="utf-8") as f: - json.dump({"ok": True, "submit_ok": result.get("ok"), "submit_error": result.get("error")}, f, ensure_ascii=False) + json.dump( + {"ok": True, "submit_ok": result.get("ok"), "submit_error": result.get("error")}, + f, + ensure_ascii=False, + ) tmp_path.replace(session_dir / SESSION_RESULT_FILE) except Exception: pass @@ -561,14 +722,15 @@ def submit_financial(session_id: str): # 辅助函数 # ================================================================ -def _validate_session(session_id: str): + +def _validate_session(session_id: str) -> Path | tuple[Response, int]: session_dir = UPLOAD_BASE / session_id if not session_dir.exists(): return jsonify({"error": "会话不存在"}), 404 return session_dir -def _build_web_config(body: dict) -> dict: +def _build_web_config(body: dict[str, Any]) -> dict[str, Any]: """从请求体构建配置""" config = load_project_config() for key in ( @@ -590,7 +752,7 @@ def _escape_sse(text: str) -> str: @app.route("/mobile/") -def mobile_upload(session_id: str): +def mobile_upload(session_id: str) -> Any: """移动端上传页面""" session_dir = UPLOAD_BASE / session_id if not session_dir.exists(): @@ -599,12 +761,12 @@ def mobile_upload(session_id: str): @app.route("/api/mobile-upload/", methods=["POST"]) -def mobile_upload_file(session_id: str): +def mobile_upload_file(session_id: str) -> Any: """移动端上传图片(复用 PC 上传逻辑)""" return upload_file(session_id) if __name__ == "__main__": UPLOAD_BASE.mkdir(parents=True, exist_ok=True) - print(f"启动 Web 服务: http://localhost:5000") - app.run(host="0.0.0.0", port=5000, debug=True, threaded=True, use_reloader=False) \ No newline at end of file + print("启动 Web 服务: http://localhost:5000") + app.run(host="0.0.0.0", port=5000, debug=True, threaded=True, use_reloader=False) diff --git a/web/static/css/index.css b/src/web/static/css/index.css similarity index 100% rename from web/static/css/index.css rename to src/web/static/css/index.css diff --git a/web/static/js/index.js b/src/web/static/js/index.js similarity index 99% rename from web/static/js/index.js rename to src/web/static/js/index.js index cf338e8..703c7ae 100644 --- a/web/static/js/index.js +++ b/src/web/static/js/index.js @@ -238,7 +238,6 @@ function showDownloadLinks(result) { const items = []; if (result.csv_url) items.push({ label: 'invoice_summary.csv', url: result.csv_url }); - if (result.md_url) items.push({ label: 'invoice_summary.md', url: result.md_url }); if (result.doc_url) items.push({ label: '易耗品、出库单.doc', url: result.doc_url }); lastDownloadUrls = {}; @@ -369,7 +368,6 @@ async function saveInvoiceData() { if (d.doc_url || d.doc_ok === false) { showDownloadLinks({ csv_url: lastDownloadUrls['invoice_summary.csv'], - md_url: lastDownloadUrls['invoice_summary.md'], doc_url: d.doc_url, doc_ok: d.doc_ok, doc_error: d.doc_error, diff --git a/web/templates/index.html b/src/web/templates/index.html similarity index 98% rename from web/templates/index.html rename to src/web/templates/index.html index 4c516fc..607bd46 100644 --- a/web/templates/index.html +++ b/src/web/templates/index.html @@ -11,7 +11,7 @@

财务报销自动化

-

上传发票 PDF 和支付截图,自动提取、OCR 识别并填报

+

上传发票 PDF 和支付截图,自动提取、LLM 识别并填报

@@ -45,7 +45,7 @@
-
📊 CSV 快捷上传 (已有发票数据 CSV 可直接上传,跳过提取和 OCR)
+
📊 CSV 快捷上传 (已有发票数据 CSV 可直接上传,跳过提取和 LLM 识别)
📊
点击或拖拽上传 CSV 文件
diff --git a/web/templates/mobile_upload.html b/src/web/templates/mobile_upload.html similarity index 100% rename from web/templates/mobile_upload.html rename to src/web/templates/mobile_upload.html diff --git a/src/web/uploads/af9065343bc3/26349119343000331138-电子发票.pdf b/src/web/uploads/af9065343bc3/26349119343000331138-电子发票.pdf new file mode 100644 index 0000000..dea6ee4 Binary files /dev/null and b/src/web/uploads/af9065343bc3/26349119343000331138-电子发票.pdf differ diff --git a/src/web/uploads/af9065343bc3/26349119343000331314-电子发票.pdf b/src/web/uploads/af9065343bc3/26349119343000331314-电子发票.pdf new file mode 100644 index 0000000..600d4ef Binary files /dev/null and b/src/web/uploads/af9065343bc3/26349119343000331314-电子发票.pdf differ diff --git a/src/web/uploads/af9065343bc3/26349119343000335414-电子发票.pdf b/src/web/uploads/af9065343bc3/26349119343000335414-电子发票.pdf new file mode 100644 index 0000000..a3343dd Binary files /dev/null and b/src/web/uploads/af9065343bc3/26349119343000335414-电子发票.pdf differ diff --git a/src/web/uploads/af9065343bc3/26349119423003550275-电子发票.pdf b/src/web/uploads/af9065343bc3/26349119423003550275-电子发票.pdf new file mode 100644 index 0000000..c1235d4 Binary files /dev/null and b/src/web/uploads/af9065343bc3/26349119423003550275-电子发票.pdf differ diff --git a/src/web/uploads/af9065343bc3/26349119423003552366-电子发票.pdf b/src/web/uploads/af9065343bc3/26349119423003552366-电子发票.pdf new file mode 100644 index 0000000..36f776d Binary files /dev/null and b/src/web/uploads/af9065343bc3/26349119423003552366-电子发票.pdf differ diff --git a/src/web/uploads/af9065343bc3/26349119423003595208-电子发票.pdf b/src/web/uploads/af9065343bc3/26349119423003595208-电子发票.pdf new file mode 100644 index 0000000..9267c4a Binary files /dev/null and b/src/web/uploads/af9065343bc3/26349119423003595208-电子发票.pdf differ diff --git a/src/web/uploads/af9065343bc3/config.json b/src/web/uploads/af9065343bc3/config.json new file mode 100644 index 0000000..8c02fab --- /dev/null +++ b/src/web/uploads/af9065343bc3/config.json @@ -0,0 +1,13 @@ +{ + "sso_login_url": "https://tyrz.fynu.edu.cn/sso/login", + "portal_url": "https://tyrz.fynu.edu.cn/oshall", + "reimburse_url": "http://210.45.32.214:8081", + "reimburse_page": "/expen/common/common?v=4.0", + "username": "202407021", + "password": "wang!1624155937", + "default_name": "王建锋", + "default_card_no": "6282880139161682", + "default_person_id": "202407021", + "consumable_storage": "新工科 D605", + "attachment_dir": "D:\\阜阳师范大学\\财务报销\\自动报销系统\\attachments" +} \ No newline at end of file diff --git a/src/web/uploads/af9065343bc3/dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf b/src/web/uploads/af9065343bc3/dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf new file mode 100644 index 0000000..bd95f36 Binary files /dev/null and b/src/web/uploads/af9065343bc3/dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf differ diff --git a/src/web/uploads/af9065343bc3/invoice_summary.csv b/src/web/uploads/af9065343bc3/invoice_summary.csv new file mode 100644 index 0000000..327592e --- /dev/null +++ b/src/web/uploads/af9065343bc3/invoice_summary.csv @@ -0,0 +1,8 @@ +序号,发票类型,发票号码,开票日期,项目名称,规格型号,价税合计,销售方名称,出发站,到达站,车次,乘车日期,座位等级,人员姓名,刷卡日期,公务卡号,刷卡金额,备注,工号 +1,高铁票,26349119423003552366,2026/6/3,,,189.50,,合肥南,阜阳西,G1318,2026/6/3,一等座,陈曙光,2026/6/3,6282****1682,189.5,, +2,高铁票,26349119343000331314,2026/6/3,,,167.00,,阜阳西,合肥南,G1967,2026/6/2,一等座,陈曙光,2026/6/1,6282****1682,167.0,, +3,酒店住宿,26342000001715702281,2026/6/3,,,536.00,,,,,,,,2026/6/2,6282****1682,535.86,, +4,高铁票,26349119423003550275,2026/6/3,,,117.50,,合肥南,阜阳西,G1318,2026/6/3,二等座,张国庆,2026/6/3,6282****1682,235.0,, +5,高铁票,26349119423003595208,2026/6/5,,,117.50,,合肥南,阜阳西,G1318,2026/6/3,二等座,王建锋,2026/6/3,6282****1682,235.0,, +6,高铁票,26349119343000331138,2026/6/3,,,115.50,,阜阳西,合肥南,G1967,2026/6/2,二等座,张国庆,2026/6/1,6282****1682,231.0,, +7,高铁票,26349119343000335414,2026/6/5,,,115.50,,阜阳西,合肥南,G1967,2026/6/2,二等座,王建锋,2026/6/1,6282****1682,231.0,, diff --git a/src/web/uploads/af9065343bc3/payment_records.csv b/src/web/uploads/af9065343bc3/payment_records.csv new file mode 100644 index 0000000..ff665b6 --- /dev/null +++ b/src/web/uploads/af9065343bc3/payment_records.csv @@ -0,0 +1,6 @@ +序号,刷卡日期,公务卡号,刷卡金额,关联发票数,发票详情,备注,_invoices_json,工号 +1,2026/6/3,6282****1682,189.5,1,高铁票[陈曙光]¥189.50,,"[{""发票类型"": ""高铁票"", ""发票号码"": ""26349119423003552366"", ""开票日期"": ""2026/6/3"", ""乘车日期"": ""2026/6/3"", ""出发站"": ""合肥南"", ""到达站"": ""阜阳西"", ""座位等级"": ""一等座"", ""车次"": ""G1318"", ""人员姓名"": ""陈曙光"", ""价税合计"": ""189.50""}]", +2,2026/6/1,6282****1682,167.0,1,高铁票[陈曙光]¥167.00,,"[{""发票类型"": ""高铁票"", ""发票号码"": ""26349119343000331314"", ""开票日期"": ""2026/6/3"", ""乘车日期"": ""2026/6/2"", ""出发站"": ""阜阳西"", ""到达站"": ""合肥南"", ""座位等级"": ""一等座"", ""车次"": ""G1967"", ""人员姓名"": ""陈曙光"", ""价税合计"": ""167.00""}]", +3,2026/6/2,6282****1682,535.86,1,酒店住宿[26342000001715702281]¥536.00,,"[{""发票类型"": ""酒店住宿"", ""发票号码"": ""26342000001715702281"", ""开票日期"": ""2026/6/3"", ""价税合计"": ""536.00""}]", +4,2026/6/3,6282****1682,235.0,2,高铁票[张国庆]¥117.50 | 高铁票[王建锋]¥117.50,,"[{""发票类型"": ""高铁票"", ""发票号码"": ""26349119423003550275"", ""开票日期"": ""2026/6/3"", ""乘车日期"": ""2026/6/3"", ""出发站"": ""合肥南"", ""到达站"": ""阜阳西"", ""座位等级"": ""二等座"", ""车次"": ""G1318"", ""人员姓名"": ""张国庆"", ""价税合计"": ""117.50""}, {""发票类型"": ""高铁票"", ""发票号码"": ""26349119423003595208"", ""开票日期"": ""2026/6/5"", ""乘车日期"": ""2026/6/3"", ""出发站"": ""合肥南"", ""到达站"": ""阜阳西"", ""座位等级"": ""二等座"", ""车次"": ""G1318"", ""人员姓名"": ""王建锋"", ""价税合计"": ""117.50""}]", +5,2026/6/1,6282****1682,231.0,2,高铁票[张国庆]¥115.50 | 高铁票[王建锋]¥115.50,,"[{""发票类型"": ""高铁票"", ""发票号码"": ""26349119343000331138"", ""开票日期"": ""2026/6/3"", ""乘车日期"": ""2026/6/2"", ""出发站"": ""阜阳西"", ""到达站"": ""合肥南"", ""座位等级"": ""二等座"", ""车次"": ""G1967"", ""人员姓名"": ""张国庆"", ""价税合计"": ""115.50""}, {""发票类型"": ""高铁票"", ""发票号码"": ""26349119343000335414"", ""开票日期"": ""2026/6/5"", ""乘车日期"": ""2026/6/2"", ""出发站"": ""阜阳西"", ""到达站"": ""合肥南"", ""座位等级"": ""二等座"", ""车次"": ""G1967"", ""人员姓名"": ""王建锋"", ""价税合计"": ""115.50""}]", diff --git a/src/web/uploads/af9065343bc3/result.json b/src/web/uploads/af9065343bc3/result.json new file mode 100644 index 0000000..dcc24a3 --- /dev/null +++ b/src/web/uploads/af9065343bc3/result.json @@ -0,0 +1 @@ +{"ok": true, "elapsed": "30.3s", "invoice_count": 7, "csv_url": "/api/download/af9065343bc3/invoice_summary.csv", "travel_count": 7, "general_count": 0, "doc_ok": null, "doc_skipped": true, "doc_message": "差旅发票无需生成易耗品出库单"} \ No newline at end of file diff --git a/src/web/uploads/af9065343bc3/session.log b/src/web/uploads/af9065343bc3/session.log new file mode 100644 index 0000000..8ac0108 --- /dev/null +++ b/src/web/uploads/af9065343bc3/session.log @@ -0,0 +1,166 @@ +2026-06-09 16:19:41 [INFO ] extractor: 发现 7 个 PDF 文件 +2026-06-09 16:19:44 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:19:47 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 16:19:47 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331138", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "张国庆", + "价税合计": "115.50" +} +2026-06-09 16:19:47 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331138-电子发票.pdf +2026-06-09 16:19:47 [INFO ] extractor: [高铁票] 已解析: 26349119343000331138-电子发票.pdf +2026-06-09 16:19:47 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:19:49 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 16:19:49 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000331314", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "一等座", + "车次": "G1967", + "人员姓名": "陈曙光", + "价税合计": "167.00" +} +2026-06-09 16:19:49 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000331314-电子发票.pdf +2026-06-09 16:19:49 [INFO ] extractor: [高铁票] 已解析: 26349119343000331314-电子发票.pdf +2026-06-09 16:19:49 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:19:52 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 16:19:52 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119343000335414", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/2", + "出发站": "阜阳西", + "到达站": "合肥南", + "座位等级": "二等座", + "车次": "G1967", + "人员姓名": "王建锋", + "价税合计": "115.50" +} +2026-06-09 16:19:52 [INFO ] llm_extractor: LLM 发票提取成功: 26349119343000335414-电子发票.pdf +2026-06-09 16:19:52 [INFO ] extractor: [高铁票] 已解析: 26349119343000335414-电子发票.pdf +2026-06-09 16:19:52 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:19:54 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 16:19:54 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003550275", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "张国庆", + "价税合计": "117.50" +} +2026-06-09 16:19:54 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003550275-电子发票.pdf +2026-06-09 16:19:54 [INFO ] extractor: [高铁票] 已解析: 26349119423003550275-电子发票.pdf +2026-06-09 16:19:54 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:19:56 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 16:19:56 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003552366", + "开票日期": "2026/6/3", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "一等座", + "车次": "G1318", + "人员姓名": "陈曙光", + "价税合计": "189.50" +} +2026-06-09 16:19:56 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003552366-电子发票.pdf +2026-06-09 16:19:56 [INFO ] extractor: [高铁票] 已解析: 26349119423003552366-电子发票.pdf +2026-06-09 16:19:56 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:19:59 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 200 字符 +2026-06-09 16:19:59 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "高铁票", + "发票号码": "26349119423003595208", + "开票日期": "2026/6/5", + "乘车日期": "2026/6/3", + "出发站": "合肥南", + "到达站": "阜阳西", + "座位等级": "二等座", + "车次": "G1318", + "人员姓名": "王建锋", + "价税合计": "117.50" +} +2026-06-09 16:19:59 [INFO ] llm_extractor: LLM 发票提取成功: 26349119423003595208-电子发票.pdf +2026-06-09 16:19:59 [INFO ] extractor: [高铁票] 已解析: 26349119423003595208-电子发票.pdf +2026-06-09 16:19:59 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:20:00 [INFO ] llm_extractor: LLM 请求完成,响应总长度: 96 字符 +2026-06-09 16:20:00 [INFO ] llm_extractor: LLM 响应: { + "发票类型": "酒店住宿", + "发票号码": "26342000001715702281", + "开票日期": "2026/6/3", + "价税合计": "536.00" +} +2026-06-09 16:20:00 [INFO ] llm_extractor: LLM 发票提取成功: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-09 16:20:00 [INFO ] extractor: [酒店住宿] 已解析: dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf +2026-06-09 16:20:00 [INFO ] extractor: 共处理 7 张发票 +2026-06-09 16:20:00 [INFO ] matcher: 发现 5 张支付截图 +2026-06-09 16:20:01 [INFO ] llm_extractor: 开始请求 LLM 多模态 (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:20:03 [INFO ] llm_extractor: LLM 多模态请求完成,响应总长度: 70 字符 +2026-06-09 16:20:03 [INFO ] llm_extractor: LLM 多模态响应: { + "刷卡日期": "2026/6/1", + "刷卡金额": "231.00", + "公务卡号": "6282****1682" +} +2026-06-09 16:20:03 [INFO ] llm_extractor: LLM 支付截图提取成功: 微信图片_20260608145318_279_42.jpg +2026-06-09 16:20:03 [INFO ] matcher: [支付截图] 已解析: 微信图片_20260608145318_279_42.jpg +2026-06-09 16:20:03 [INFO ] llm_extractor: 开始请求 LLM 多模态 (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:20:05 [INFO ] llm_extractor: LLM 多模态请求完成,响应总长度: 70 字符 +2026-06-09 16:20:05 [INFO ] llm_extractor: LLM 多模态响应: { + "刷卡日期": "2026/6/1", + "刷卡金额": "167.00", + "公务卡号": "6282****1682" +} +2026-06-09 16:20:05 [INFO ] llm_extractor: LLM 支付截图提取成功: 微信图片_20260608145319_280_42.jpg +2026-06-09 16:20:05 [INFO ] matcher: [支付截图] 已解析: 微信图片_20260608145319_280_42.jpg +2026-06-09 16:20:05 [INFO ] llm_extractor: 开始请求 LLM 多模态 (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:20:07 [INFO ] llm_extractor: LLM 多模态请求完成,响应总长度: 70 字符 +2026-06-09 16:20:07 [INFO ] llm_extractor: LLM 多模态响应: { + "刷卡日期": "2026/6/2", + "刷卡金额": "535.86", + "公务卡号": "6282****1682" +} +2026-06-09 16:20:07 [INFO ] llm_extractor: LLM 支付截图提取成功: 微信图片_20260608145320_281_42.jpg +2026-06-09 16:20:07 [INFO ] matcher: [支付截图] 已解析: 微信图片_20260608145320_281_42.jpg +2026-06-09 16:20:07 [INFO ] llm_extractor: 开始请求 LLM 多模态 (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:20:09 [INFO ] llm_extractor: LLM 多模态请求完成,响应总长度: 70 字符 +2026-06-09 16:20:09 [INFO ] llm_extractor: LLM 多模态响应: { + "刷卡日期": "2026/6/3", + "刷卡金额": "189.50", + "公务卡号": "6282****1682" +} +2026-06-09 16:20:09 [INFO ] llm_extractor: LLM 支付截图提取成功: 微信图片_20260608145321_282_42.jpg +2026-06-09 16:20:09 [INFO ] matcher: [支付截图] 已解析: 微信图片_20260608145321_282_42.jpg +2026-06-09 16:20:09 [INFO ] llm_extractor: 开始请求 LLM 多模态 (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:20:11 [INFO ] llm_extractor: LLM 多模态请求完成,响应总长度: 70 字符 +2026-06-09 16:20:11 [INFO ] llm_extractor: LLM 多模态响应: { + "刷卡日期": "2026/6/3", + "刷卡金额": "235.00", + "公务卡号": "6282****1682" +} +2026-06-09 16:20:11 [INFO ] llm_extractor: LLM 支付截图提取成功: 微信图片_20260608145323_283_42.jpg +2026-06-09 16:20:11 [INFO ] matcher: [支付截图] 已解析: 微信图片_20260608145323_283_42.jpg +2026-06-09 16:20:11 [INFO ] matcher: 共提取 5 条刷卡记录 +2026-06-09 16:20:11 [INFO ] matcher: 金额校验: 发票总额 ¥1358.50, 刷卡总额 ¥1358.36, 发票数 7, 刷卡数 5 +2026-06-09 16:20:11 [INFO ] matcher: [一对多-精确] 26349119423003552366 ¥189.50 ↔ 微信图片_20260608145321_282_42.jpg ¥189.50 +2026-06-09 16:20:11 [INFO ] matcher: [一对多-精确] 26349119343000331314 ¥167.00 ↔ 微信图片_20260608145319_280_42.jpg ¥167.00 +2026-06-09 16:20:11 [INFO ] matcher: [一对多-贪心] 26342000001715702281 ¥536.00 → 微信图片_20260608145320_281_42.jpg ¥535.86 +2026-06-09 16:20:11 [INFO ] matcher: [一对多-贪心] 26349119423003550275 ¥117.50 → 微信图片_20260608145323_283_42.jpg ¥235.00 +2026-06-09 16:20:11 [INFO ] matcher: [一对多-贪心] 26349119423003595208 ¥117.50 → 微信图片_20260608145323_283_42.jpg ¥235.00 +2026-06-09 16:20:11 [INFO ] matcher: [一对多-贪心] 26349119343000331138 ¥115.50 → 微信图片_20260608145318_279_42.jpg ¥231.00 +2026-06-09 16:20:11 [INFO ] matcher: [一对多-贪心] 26349119343000335414 ¥115.50 → 微信图片_20260608145318_279_42.jpg ¥231.00 +2026-06-09 16:20:11 [INFO ] matcher: 匹配完成: 5 条支付记录, 7/7 张发票已关联 +2026-06-09 16:20:11 [INFO ] extractor: 差旅发票: 7 张, 普通发票: 0 张 +2026-06-09 16:20:11 [INFO ] fill_consumable_doc: 纯差旅发票,跳过易耗品出库单生成 diff --git a/src/web/uploads/af9065343bc3/微信图片_20260608145318_279_42.jpg b/src/web/uploads/af9065343bc3/微信图片_20260608145318_279_42.jpg new file mode 100644 index 0000000..667336f Binary files /dev/null and b/src/web/uploads/af9065343bc3/微信图片_20260608145318_279_42.jpg differ diff --git a/src/web/uploads/af9065343bc3/微信图片_20260608145319_280_42.jpg b/src/web/uploads/af9065343bc3/微信图片_20260608145319_280_42.jpg new file mode 100644 index 0000000..4babcb0 Binary files /dev/null and b/src/web/uploads/af9065343bc3/微信图片_20260608145319_280_42.jpg differ diff --git a/src/web/uploads/af9065343bc3/微信图片_20260608145320_281_42.jpg b/src/web/uploads/af9065343bc3/微信图片_20260608145320_281_42.jpg new file mode 100644 index 0000000..c52451b Binary files /dev/null and b/src/web/uploads/af9065343bc3/微信图片_20260608145320_281_42.jpg differ diff --git a/src/web/uploads/af9065343bc3/微信图片_20260608145321_282_42.jpg b/src/web/uploads/af9065343bc3/微信图片_20260608145321_282_42.jpg new file mode 100644 index 0000000..2212145 Binary files /dev/null and b/src/web/uploads/af9065343bc3/微信图片_20260608145321_282_42.jpg differ diff --git a/src/web/uploads/af9065343bc3/微信图片_20260608145323_283_42.jpg b/src/web/uploads/af9065343bc3/微信图片_20260608145323_283_42.jpg new file mode 100644 index 0000000..30beab5 Binary files /dev/null and b/src/web/uploads/af9065343bc3/微信图片_20260608145323_283_42.jpg differ diff --git a/src/web/uploads/c913da2afca0/26349119343000331138-电子发票.pdf b/src/web/uploads/c913da2afca0/26349119343000331138-电子发票.pdf new file mode 100644 index 0000000..dea6ee4 Binary files /dev/null and b/src/web/uploads/c913da2afca0/26349119343000331138-电子发票.pdf differ diff --git a/src/web/uploads/c913da2afca0/26349119343000331314-电子发票.pdf b/src/web/uploads/c913da2afca0/26349119343000331314-电子发票.pdf new file mode 100644 index 0000000..600d4ef Binary files /dev/null and b/src/web/uploads/c913da2afca0/26349119343000331314-电子发票.pdf differ diff --git a/src/web/uploads/c913da2afca0/26349119343000335414-电子发票.pdf b/src/web/uploads/c913da2afca0/26349119343000335414-电子发票.pdf new file mode 100644 index 0000000..a3343dd Binary files /dev/null and b/src/web/uploads/c913da2afca0/26349119343000335414-电子发票.pdf differ diff --git a/src/web/uploads/c913da2afca0/26349119423003550275-电子发票.pdf b/src/web/uploads/c913da2afca0/26349119423003550275-电子发票.pdf new file mode 100644 index 0000000..c1235d4 Binary files /dev/null and b/src/web/uploads/c913da2afca0/26349119423003550275-电子发票.pdf differ diff --git a/src/web/uploads/c913da2afca0/26349119423003552366-电子发票.pdf b/src/web/uploads/c913da2afca0/26349119423003552366-电子发票.pdf new file mode 100644 index 0000000..36f776d Binary files /dev/null and b/src/web/uploads/c913da2afca0/26349119423003552366-电子发票.pdf differ diff --git a/src/web/uploads/c913da2afca0/26349119423003595208-电子发票.pdf b/src/web/uploads/c913da2afca0/26349119423003595208-电子发票.pdf new file mode 100644 index 0000000..9267c4a Binary files /dev/null and b/src/web/uploads/c913da2afca0/26349119423003595208-电子发票.pdf differ diff --git a/src/web/uploads/c913da2afca0/config.json b/src/web/uploads/c913da2afca0/config.json new file mode 100644 index 0000000..8c02fab --- /dev/null +++ b/src/web/uploads/c913da2afca0/config.json @@ -0,0 +1,13 @@ +{ + "sso_login_url": "https://tyrz.fynu.edu.cn/sso/login", + "portal_url": "https://tyrz.fynu.edu.cn/oshall", + "reimburse_url": "http://210.45.32.214:8081", + "reimburse_page": "/expen/common/common?v=4.0", + "username": "202407021", + "password": "wang!1624155937", + "default_name": "王建锋", + "default_card_no": "6282880139161682", + "default_person_id": "202407021", + "consumable_storage": "新工科 D605", + "attachment_dir": "D:\\阜阳师范大学\\财务报销\\自动报销系统\\attachments" +} \ No newline at end of file diff --git a/src/web/uploads/c913da2afca0/dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf b/src/web/uploads/c913da2afca0/dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf new file mode 100644 index 0000000..bd95f36 Binary files /dev/null and b/src/web/uploads/c913da2afca0/dzfp_26342000001715702281_阜阳师范大学_20260603111046.pdf differ diff --git a/src/web/uploads/c913da2afca0/result.json b/src/web/uploads/c913da2afca0/result.json new file mode 100644 index 0000000..c698753 --- /dev/null +++ b/src/web/uploads/c913da2afca0/result.json @@ -0,0 +1 @@ +{"ok": false, "error": "Error code: 502"} \ No newline at end of file diff --git a/src/web/uploads/c913da2afca0/session.log b/src/web/uploads/c913da2afca0/session.log new file mode 100644 index 0000000..2220dfd --- /dev/null +++ b/src/web/uploads/c913da2afca0/session.log @@ -0,0 +1,4 @@ +2026-06-09 16:09:29 [INFO ] extractor: 发现 7 个 PDF 文件 +2026-06-09 16:09:31 [INFO ] llm_extractor: 开始请求 LLM (model=qwen/qwen3.5-9b, base=http://100.123.83.115:1234/v1) +2026-06-09 16:09:50 [ERROR] llm_extractor: LLM 请求失败: Error code: 502 +2026-06-09 16:09:50 [ERROR] llm_extractor: LLM 发票提取失败: 26349119343000331138-电子发票.pdf (Error code: 502) diff --git a/src/web/uploads/c913da2afca0/微信图片_20260608145318_279_42.jpg b/src/web/uploads/c913da2afca0/微信图片_20260608145318_279_42.jpg new file mode 100644 index 0000000..667336f Binary files /dev/null and b/src/web/uploads/c913da2afca0/微信图片_20260608145318_279_42.jpg differ diff --git a/src/web/uploads/c913da2afca0/微信图片_20260608145319_280_42.jpg b/src/web/uploads/c913da2afca0/微信图片_20260608145319_280_42.jpg new file mode 100644 index 0000000..4babcb0 Binary files /dev/null and b/src/web/uploads/c913da2afca0/微信图片_20260608145319_280_42.jpg differ diff --git a/src/web/uploads/c913da2afca0/微信图片_20260608145320_281_42.jpg b/src/web/uploads/c913da2afca0/微信图片_20260608145320_281_42.jpg new file mode 100644 index 0000000..c52451b Binary files /dev/null and b/src/web/uploads/c913da2afca0/微信图片_20260608145320_281_42.jpg differ diff --git a/src/web/uploads/c913da2afca0/微信图片_20260608145321_282_42.jpg b/src/web/uploads/c913da2afca0/微信图片_20260608145321_282_42.jpg new file mode 100644 index 0000000..2212145 Binary files /dev/null and b/src/web/uploads/c913da2afca0/微信图片_20260608145321_282_42.jpg differ diff --git a/src/web/uploads/c913da2afca0/微信图片_20260608145323_283_42.jpg b/src/web/uploads/c913da2afca0/微信图片_20260608145323_283_42.jpg new file mode 100644 index 0000000..30beab5 Binary files /dev/null and b/src/web/uploads/c913da2afca0/微信图片_20260608145323_283_42.jpg differ diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..356699c --- /dev/null +++ b/tasks.py @@ -0,0 +1,25 @@ +import os + + +def run_task(task_name): + print(f"Running {task_name}...") + os.system( + "uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=term-missing" + if task_name == "test" + else "uv run ruff check . && uv run ruff format --check . && uv run mypy src/main.py && uv run deptry ." + if task_name == "check" + else "uv run python src/main.py" + if task_name == "run" + else "uv sync && uv run pre-commit install" + if task_name == "install" + else "rm -rf .venv __pycache__ .pytest_cache .mypy_cache .ruff_cache" + if task_name == "clean" + else "" + ) + + +if __name__ == "__main__": + import sys + + if len(sys.argv) > 1: + run_task(sys.argv[1]) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..401aa00 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,19 @@ +"""配置模块单元测试""" + +from src.config import load_config + + +class TestLoadConfig: + """配置加载""" + + def test_returns_dict(self) -> None: + config = load_config() + assert isinstance(config, dict) + + def test_has_username(self) -> None: + config = load_config() + assert "username" in config + + def test_has_password(self) -> None: + config = load_config() + assert "password" in config diff --git a/tests/test_invoice.py b/tests/test_invoice.py new file mode 100644 index 0000000..f5c16b1 --- /dev/null +++ b/tests/test_invoice.py @@ -0,0 +1,89 @@ +"""发票模块单元测试 + +覆盖发票分类、CSV 列定义、常量校验。 +""" + +from src.doc.invoice import ( + INVOICE_LEVEL_COLUMNS, + INVOICE_TYPE_GENERAL, + INVOICE_TYPE_HOTEL, + INVOICE_TYPE_TRAIN, + INVOICE_TYPE_TRAVEL, + PAYMENT_RECORD_COLUMNS, + classify_invoice_batch, + is_travel_invoice, +) + + +class TestInvoiceConstants: + """发票常量校验""" + + def test_travel_types_contain_train(self) -> None: + assert INVOICE_TYPE_TRAIN in INVOICE_TYPE_TRAVEL + + def test_travel_types_contain_hotel(self) -> None: + assert INVOICE_TYPE_HOTEL in INVOICE_TYPE_TRAVEL + + def test_general_not_in_travel(self) -> None: + assert INVOICE_TYPE_GENERAL not in INVOICE_TYPE_TRAVEL + + def test_invoice_columns_not_empty(self) -> None: + assert len(INVOICE_LEVEL_COLUMNS) > 0 + + def test_payment_columns_not_empty(self) -> None: + assert len(PAYMENT_RECORD_COLUMNS) > 0 + + +class TestIsTravelInvoice: + """差旅发票判断""" + + def test_train_is_travel(self) -> None: + assert is_travel_invoice(INVOICE_TYPE_TRAIN) is True + + def test_hotel_is_travel(self) -> None: + assert is_travel_invoice(INVOICE_TYPE_HOTEL) is True + + def test_general_not_travel(self) -> None: + assert is_travel_invoice(INVOICE_TYPE_GENERAL) is False + + def test_unknown_not_travel(self) -> None: + assert is_travel_invoice("未知类型") is False + + +class TestClassifyInvoiceBatch: + """发票分类""" + + def test_empty_list(self) -> None: + result = classify_invoice_batch([]) + assert result == {"travel": [], "general": []} + + def test_all_travel(self) -> None: + invoices = [ + {"发票类型": INVOICE_TYPE_TRAIN, "发票号码": "001"}, + {"发票类型": INVOICE_TYPE_HOTEL, "发票号码": "002"}, + ] + result = classify_invoice_batch(invoices) + assert len(result["travel"]) == 2 + assert len(result["general"]) == 0 + + def test_all_general(self) -> None: + invoices = [ + {"发票类型": INVOICE_TYPE_GENERAL, "发票号码": "001"}, + ] + result = classify_invoice_batch(invoices) + assert len(result["travel"]) == 0 + assert len(result["general"]) == 1 + + def test_mixed(self) -> None: + invoices = [ + {"发票类型": INVOICE_TYPE_TRAIN, "发票号码": "001"}, + {"发票类型": INVOICE_TYPE_GENERAL, "发票号码": "002"}, + ] + result = classify_invoice_batch(invoices) + assert len(result["travel"]) == 1 + assert len(result["general"]) == 1 + + def test_missing_type_defaults_to_general(self) -> None: + invoices = [{"发票号码": "001"}] + result = classify_invoice_batch(invoices) + assert len(result["general"]) == 1 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..6a182b4 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2644 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform == 'darwin'", + "python_full_version >= '3.15' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.15' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.15' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')", + "python_full_version == '3.14.*' and sys_platform == 'darwin'", + "python_full_version == '3.14.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.14.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.14.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')", + "python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version < '3.14' and sys_platform == 'darwin'", + "python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/21/151624b51cd92553d95424daf4bf19f19ce9be9002d19253e7e7ce67197b/aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480", size = 757402, upload-time = "2026-06-07T21:06:40.311Z" }, + { url = "https://files.pythonhosted.org/packages/c2/82/280619e0bd7bf2454987e19282616e84762255dd9c8468f62382e8c191f1/aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d", size = 512310, upload-time = "2026-06-07T21:06:42.207Z" }, + { url = "https://files.pythonhosted.org/packages/55/b2/2aac325583aaa1353045f96dffa586d8a34e8322e14a7ba49cffeb103ab4/aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2", size = 512448, upload-time = "2026-06-07T21:06:43.813Z" }, + { url = "https://files.pythonhosted.org/packages/8a/72/a60607cb849faa8af8a356c9329ea2eb6f395d49e82cc82ccba1fd8deb8f/aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2", size = 1766854, upload-time = "2026-06-07T21:06:45.391Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d3/d9fe1c9ec7557ab4d0d82bebaa728c6418f0b93295ec2f4ab015f7710cc7/aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a", size = 1740884, upload-time = "2026-06-07T21:06:47.413Z" }, + { url = "https://files.pythonhosted.org/packages/c1/dc/f2cecfaf9337ba3e63f181500814ff502aa3d00d9c7ec93a9d23d10a27b2/aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264", size = 1810034, upload-time = "2026-06-07T21:06:50.165Z" }, + { url = "https://files.pythonhosted.org/packages/66/d7/2ff65c5e65c0d7476daf7e15c032e0805e36811185b9623e3238ad6c763e/aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842", size = 1904054, upload-time = "2026-06-07T21:06:52.035Z" }, + { url = "https://files.pythonhosted.org/packages/20/9c/d445818389df371f56d141d881153ba23183c4735a03f7356ffb43f7757d/aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c", size = 1790278, upload-time = "2026-06-07T21:06:54.049Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/bf04cb4d865fc6101c2229a294ad744973b72e513fdc5a6b791e6983d72a/aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95", size = 1591795, upload-time = "2026-06-07T21:06:55.911Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b4/4dac0038960427ba832f6609dfb4ea5437d7fd80c72001b9e48f834f428b/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199", size = 1728397, upload-time = "2026-06-07T21:06:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/7cd4e8ad7aa3b75f17d56bb5498dd604a93d4e6eece822ba0568c413fff0/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817", size = 1766504, upload-time = "2026-06-07T21:07:00.009Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/fc01d9fcad0f73fed3f3d361f1f94f975947b50dff82919f6dc2bf4316cc/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a", size = 1777806, upload-time = "2026-06-07T21:07:02.064Z" }, + { url = "https://files.pythonhosted.org/packages/41/09/47e2d090bddcc8fb4ccb4c314aadc32d7c5d9bb55f50f6ad1c92fc15d501/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4", size = 1580707, upload-time = "2026-06-07T21:07:03.942Z" }, + { url = "https://files.pythonhosted.org/packages/3d/36/f1a4ce904ae0b6930cfe9afc96d0896f7ec1a620c400405d63783bb95a9c/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087", size = 1798121, upload-time = "2026-06-07T21:07:05.987Z" }, + { url = "https://files.pythonhosted.org/packages/70/0a/e0075ce9ca0279ee1d4f0c0b85f54fea02ebc83c3007651a72bece658fec/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3", size = 1767580, upload-time = "2026-06-07T21:07:07.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/a0c0a8f327a9c52095cdd8e312391b00d3ed64ab6c72bb5c33d8ec251cf7/aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4", size = 452771, upload-time = "2026-06-07T21:07:09.669Z" }, + { url = "https://files.pythonhosted.org/packages/df/d9/ea367c75f16ac9c6cdc8febb25e8318fa21a2b1bc8d6514d4b2d890bface/aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271", size = 479873, upload-time = "2026-06-07T21:07:11.538Z" }, + { url = "https://files.pythonhosted.org/packages/03/64/8d96784a7851156db8a4c6c3f6f91042fdf39fb15a4cc38c8b3c14833c45/aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847", size = 448073, upload-time = "2026-06-07T21:07:13.637Z" }, + { url = "https://files.pythonhosted.org/packages/bc/97/bd137012dd97e1649162b099135a80e1fd59aaa807b2430fc448d1029aff/aiohttp-3.14.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178", size = 506882, upload-time = "2026-06-07T21:07:15.501Z" }, + { url = "https://files.pythonhosted.org/packages/ef/79/e5cc690e9d922a66887ceeaca53a8ffd5a7b0be3816142b7abc433742d89/aiohttp-3.14.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf", size = 515270, upload-time = "2026-06-07T21:07:17.53Z" }, + { url = "https://files.pythonhosted.org/packages/fe/22/a73ccbf9dbd6e26dda0b24d5fd5db7da92ee3383a79f47677ffb834c5c5b/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd", size = 485841, upload-time = "2026-06-07T21:07:19.555Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b9/57ed8eaf596321c2ad747bd480fb1700dbd7177c60dfc9e4c187f629662e/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe", size = 492088, upload-time = "2026-06-07T21:07:21.581Z" }, + { url = "https://files.pythonhosted.org/packages/78/c0/5ebe5270a7c140d7c6f79dcb018640225f14d406c149e4eec04a7d82fe71/aiohttp-3.14.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4", size = 501564, upload-time = "2026-06-07T21:07:23.388Z" }, + { url = "https://files.pythonhosted.org/packages/75/7f/8cdaa24fc7983865e0915153b96a9ac5bcdd3548d64c5a27d17cecccad2d/aiohttp-3.14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876", size = 751998, upload-time = "2026-06-07T21:07:25.046Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f4/c4227aacfacc5cb0cc2d119b65301d177912a6842cd64e120c47af76064f/aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da", size = 510918, upload-time = "2026-06-07T21:07:27.28Z" }, + { url = "https://files.pythonhosted.org/packages/ab/01/a2d5f96cd4e74424864d30bc0a7e44d0a12dacdcfa91b5b2d1bd3dca6bf3/aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6", size = 508657, upload-time = "2026-06-07T21:07:29.252Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ed/3c0fb5c500fdd8e7ebc10d1889c04384fffa1a9163eac1356088ca9da1b1/aiohttp-3.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96", size = 1757907, upload-time = "2026-06-07T21:07:31.03Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ab/d4c924d9bd5be3050c226612413ce68cb54c70d2c31b661bfc8d9a5b6a70/aiohttp-3.14.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f", size = 1737565, upload-time = "2026-06-07T21:07:33.031Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/37326821ff779084020cdc33224d20b19f42f4183a500ff92022a739eda7/aiohttp-3.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296", size = 1799018, upload-time = "2026-06-07T21:07:35.003Z" }, + { url = "https://files.pythonhosted.org/packages/b3/4f/6e947ba73e4ce09070761c05ed3a8ceb7c21f5e46798671d8b2aac0e4626/aiohttp-3.14.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa", size = 1894416, upload-time = "2026-06-07T21:07:36.956Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6e/dbf1d0625dc711fb2851f4f3c3055c39ed58bae92082d8c627dbe6013736/aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451", size = 1783881, upload-time = "2026-06-07T21:07:39.063Z" }, + { url = "https://files.pythonhosted.org/packages/44/c2/5e25098a67268ed369483ae7d1a58bd0a13d03aab860d2a0e4a6eb25b046/aiohttp-3.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c", size = 1587572, upload-time = "2026-06-07T21:07:41.058Z" }, + { url = "https://files.pythonhosted.org/packages/2a/bd/cf9cee17e140f942a3de73e658a543aa8fbf35a5fc67a9d2538d52d77f0b/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca", size = 1722137, upload-time = "2026-06-07T21:07:43.014Z" }, + { url = "https://files.pythonhosted.org/packages/89/6d/5684f8c59045c96f81a18cefbc1fbbd79d25b88f1c622f2a5c5c08fcb632/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09", size = 1755953, upload-time = "2026-06-07T21:07:45.933Z" }, + { url = "https://files.pythonhosted.org/packages/a8/40/35caf3170f8359760740a7d9aa0fff2e344bef98e1d1186f5a0f6dec17e6/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397", size = 1766479, upload-time = "2026-06-07T21:07:48.047Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a1/b0c61e7a137f0d81de49a82023a6df73c3c16d6fefb0f8e4a93d21639002/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080", size = 1580077, upload-time = "2026-06-07T21:07:50.069Z" }, + { url = "https://files.pythonhosted.org/packages/0b/41/194ea4623693009fcefebef7aef63c141754f153e9cd0d39d3b9e36c175c/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345", size = 1791688, upload-time = "2026-06-07T21:07:52.106Z" }, + { url = "https://files.pythonhosted.org/packages/ba/45/4de841f005cfe1fd63e2a2fe011262c515e2a62aa6994b15947e7d717ac9/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588", size = 1761094, upload-time = "2026-06-07T21:07:54.113Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ae/dbce10533d3896d544d5053939ed75b7dc31a1b0973d959b1b5ae21028d6/aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780", size = 452662, upload-time = "2026-06-07T21:07:56.06Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/0bf1a19362c32f06229da5e7ddfcec91f93474d6307f7a2d3135e9c674dc/aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a", size = 479748, upload-time = "2026-06-07T21:07:58.319Z" }, + { url = "https://files.pythonhosted.org/packages/22/0a/62e7232dc9484fbec112ceb32efb6a624cc7994ec6e2b019286f17c4e8f2/aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8", size = 447723, upload-time = "2026-06-07T21:08:00.154Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a1/5fafa04e1ca91ddb47608699d60649c1c6db3cf41c99e78fc4056f9513db/aiohttp-3.14.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15", size = 508531, upload-time = "2026-06-07T21:08:02.093Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2e/bfa02f699d87ffc86d5959270b28f1cb410add3ccaced8ed2e0b8a5238fc/aiohttp-3.14.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8", size = 514718, upload-time = "2026-06-07T21:08:04.476Z" }, + { url = "https://files.pythonhosted.org/packages/85/a5/9594ad6289eebbc97d167c44213d557807f90e59115caad24de21ad2c3b1/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3", size = 487918, upload-time = "2026-06-07T21:08:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/b4/61/16a32c36c3c49edec122a3dc811f2057df2f94d3b14aa107c8017d981618/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba", size = 494014, upload-time = "2026-06-07T21:08:08.263Z" }, + { url = "https://files.pythonhosted.org/packages/9b/89/3ebcf96ed99c05bec9c434aaac6963fd3cbab4a786ae739908a144d9ce44/aiohttp-3.14.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397", size = 502398, upload-time = "2026-06-07T21:08:10.244Z" }, + { url = "https://files.pythonhosted.org/packages/fd/3d/b74870a0c2d40c355928cd5b96c7a11fa821b8a40fc41365e64479b151fb/aiohttp-3.14.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448", size = 758018, upload-time = "2026-06-07T21:08:12.447Z" }, + { url = "https://files.pythonhosted.org/packages/d3/66/f42f5c984d99e49c6cff5f26f590750f2e2f7ef1fcfb99966ab5be1b632e/aiohttp-3.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004", size = 512462, upload-time = "2026-06-07T21:08:14.624Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a7/248e1aebe0c7810b0271e021a0f2a5eb6e78a051885b3c9df49f42a5802d/aiohttp-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983", size = 512824, upload-time = "2026-06-07T21:08:16.572Z" }, + { url = "https://files.pythonhosted.org/packages/26/97/2aa0e5ba0727dc3bd5aaebb7ccbc510f7dfb7fb961ec87497cd496635ab1/aiohttp-3.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe", size = 1749898, upload-time = "2026-06-07T21:08:18.635Z" }, + { url = "https://files.pythonhosted.org/packages/00/8d/e97f6c96c891d457c8479d92a514ba194d0412f981d72c70341ee18488ed/aiohttp-3.14.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333", size = 1710114, upload-time = "2026-06-07T21:08:20.892Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e6/aa8d7e863048c8fceb5cd6ce74017311cec3ead07847387e12265fb4444e/aiohttp-3.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0", size = 1802541, upload-time = "2026-06-07T21:08:23.044Z" }, + { url = "https://files.pythonhosted.org/packages/83/a8/72193137de57fda4ebfae4563182d082c8856e3b6e9871d0b46f028fb369/aiohttp-3.14.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a", size = 1875776, upload-time = "2026-06-07T21:08:25.288Z" }, + { url = "https://files.pythonhosted.org/packages/a0/18/938441025db6769a3464596b2410af3afde0b21eb2f204c6f766f68af4bd/aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602", size = 1760329, upload-time = "2026-06-07T21:08:27.363Z" }, + { url = "https://files.pythonhosted.org/packages/60/29/bf2496b4065e76e09fe48015aaffe5ce161d8f089b06ac6982070f653076/aiohttp-3.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca", size = 1587293, upload-time = "2026-06-07T21:08:29.805Z" }, + { url = "https://files.pythonhosted.org/packages/49/a2/2136674d52123b1354bd05dd5753c318db47dc0c927cc70b27bab3755456/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35", size = 1714756, upload-time = "2026-06-07T21:08:32.094Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/e5fd2e6f915503081c0f9b1e8540947037929c70c191da2e4d54b31a21a1/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844", size = 1721052, upload-time = "2026-06-07T21:08:34.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/5a/2833e324a2263e104e31e2e91bc5bbee81bc499afd32203faee048a883f0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496", size = 1766888, upload-time = "2026-06-07T21:08:36.95Z" }, + { url = "https://files.pythonhosted.org/packages/57/fa/dea6511870913162f3b2e8c42a7614eb203a4540b8c2da43e0bfb0548f3c/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5", size = 1581679, upload-time = "2026-06-07T21:08:39.292Z" }, + { url = "https://files.pythonhosted.org/packages/14/bd/3cf0d55e71784b33534e9710a67d382d900598b4787fbce6cc7317f8c42a/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95", size = 1782021, upload-time = "2026-06-07T21:08:41.407Z" }, + { url = "https://files.pythonhosted.org/packages/c1/af/14bb5843eccbe234f4dfb78ab73e549d99727247e62ae5d62cbd22eaf5b0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444", size = 1742574, upload-time = "2026-06-07T21:08:43.795Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1e/fbeb7af9210a67ac0f9c9bec0f8f4568497924e33137a3d5b48e1cf85f3f/aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0", size = 457773, upload-time = "2026-06-07T21:08:46.168Z" }, + { url = "https://files.pythonhosted.org/packages/f0/2b/13e8d741a9ec5db7d900c060554cf8352ab85e44e2a4469ebb9d377bda17/aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719", size = 485001, upload-time = "2026-06-07T21:08:48.401Z" }, + { url = "https://files.pythonhosted.org/packages/df/30/491acfa2c4d6c3ff59c49a14fc1b50be3241e25bbb0c84c09e2da4d11395/aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec", size = 453809, upload-time = "2026-06-07T21:08:50.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/e3/19dbe1a1f4cc6230eb9e314de7fe68053b0992f9302b27d12141a0b5db53/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2", size = 793320, upload-time = "2026-06-07T21:08:52.775Z" }, + { url = "https://files.pythonhosted.org/packages/7f/20/1b7182219ba1b108430d6e4dc53d25ae02dcfcf5a045b33af4e8c5167527/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340", size = 529077, upload-time = "2026-06-07T21:08:55Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c8/14ce60ec31a2e5f5274bb17d383a6f7a3aabca31ac04eee05585bbadab16/aiohttp-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d", size = 532476, upload-time = "2026-06-07T21:08:57.176Z" }, + { url = "https://files.pythonhosted.org/packages/7e/02/9ac85e081e53da2e061b02fa7758fe0a12d17b8ce2d1f5e6c7cb76730328/aiohttp-3.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94", size = 1922347, upload-time = "2026-06-07T21:08:59.563Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3e/d3ba07a0ab38b5389e10bec4362d21e10a4f667cba2d79ba30837b3a5059/aiohttp-3.14.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc", size = 1786465, upload-time = "2026-06-07T21:09:01.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/cb/e2ee978a00cfb2df829704a69528b18154eba5939f45bc1efa8f33aee4c5/aiohttp-3.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251", size = 1909423, upload-time = "2026-06-07T21:09:04.357Z" }, + { url = "https://files.pythonhosted.org/packages/73/5d/1430334858b1022b58ae50399a918f0bd6fe8fa7fa183598d657ff61e040/aiohttp-3.14.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1", size = 2001906, upload-time = "2026-06-07T21:09:06.722Z" }, + { url = "https://files.pythonhosted.org/packages/66/4e/560c7472d3d198a23aa5c8b19a5115bf6a9b77b7d3e4bb363da320430ad2/aiohttp-3.14.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3", size = 1877095, upload-time = "2026-06-07T21:09:09.011Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f1/4745806578d447db4a784a8591e2dae3afdfc2bcb96f8f81271b13df6543/aiohttp-3.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c", size = 1676222, upload-time = "2026-06-07T21:09:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/48255813cca749a229ef0ab476004ec623728ad79a9c0840616f6c076325/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203", size = 1842922, upload-time = "2026-06-07T21:09:14.118Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c0/bbd054e2bee909f529523a5af3891052606af5143c09f5f183ec3b234676/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1", size = 1825035, upload-time = "2026-06-07T21:09:16.447Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ae/90395d4376deceb74e09ec26b6adf7d2015a6f8802d6d84446af860fef04/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665", size = 1849512, upload-time = "2026-06-07T21:09:18.742Z" }, + { url = "https://files.pythonhosted.org/packages/93/bd/fb25f3049957553d4ce0ba6ae480aa2f592a6985497fca590837d16c1be0/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1", size = 1668571, upload-time = "2026-06-07T21:09:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/3f/22/7f73303d64dd567ff3addca90b556690ed1233a47b8f55d242fb90af3681/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d", size = 1881159, upload-time = "2026-06-07T21:09:23.813Z" }, + { url = "https://files.pythonhosted.org/packages/44/be/0474c5a8b5640e1e4aa1923430a91f4151be82e511373fe764189b89aef5/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c", size = 1841409, upload-time = "2026-06-07T21:09:26.207Z" }, + { url = "https://files.pythonhosted.org/packages/7b/3c/bb4a7cba26956cb3da4553cc2056cf67be5b5ff6e6d8fa4fbdff73bfb7ae/aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365", size = 494166, upload-time = "2026-06-07T21:09:28.505Z" }, + { url = "https://files.pythonhosted.org/packages/8a/84/ec80c2c1f66a952555a9f86df6b33af65108a6febfa0471b69013a12f807/aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9", size = 530255, upload-time = "2026-06-07T21:09:30.843Z" }, + { url = "https://files.pythonhosted.org/packages/2a/71/6e22be134a4061ada85a92951b842f2657f17d926b727f3f94c56ae963d6/aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6", size = 469640, upload-time = "2026-06-07T21:09:33.028Z" }, +] + +[[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 = "aiosqlite" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8a/64761f4005f17809769d23e518d915db74e6310474e733e3593cfc854ef1/aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650", size = 14821, upload-time = "2025-12-23T19:25:43.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/b7/e3bf5133d697a08128598c8d0abc5e16377b51465a33756de24fa7dee953/aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb", size = 17405, upload-time = "2025-12-23T19:25:42.139Z" }, +] + +[[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 = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "ast-serialize" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/9d/09e27731bd5864a9ce04e3244074e674bb8936bf62b45e0357248717adac/ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6", size = 61157, upload-time = "2026-05-17T17:48:29.429Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/9a/13dde51ba9e15f8b97957ab7cb0120d0e381524d651c6bd630b9c359227f/ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a", size = 1183520, upload-time = "2026-05-17T17:47:30.831Z" }, + { url = "https://files.pythonhosted.org/packages/37/de/5a7f0a9fe68944f536632a5af84676739c7d2582be42deb082634bf3a754/ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b", size = 1175779, upload-time = "2026-05-17T17:47:32.551Z" }, + { url = "https://files.pythonhosted.org/packages/9c/81/0bb853e76e4f6e9a1855d569003c59e19ffac45f7079d91505d1bb212f92/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1", size = 1233750, upload-time = "2026-05-17T17:47:34.731Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d3/4cf705beeccc08754d0bbda99aefff26110e209b9a07ac8a6b60eec48531/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6", size = 1235942, upload-time = "2026-05-17T17:47:36.287Z" }, + { url = "https://files.pythonhosted.org/packages/26/c8/ee097e437ea27dd2b8b227865c875492b585650a5802a22d82b304c8201b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2", size = 1442517, upload-time = "2026-05-17T17:47:38.17Z" }, + { url = "https://files.pythonhosted.org/packages/ff/bd/68063442838f1ba68ec72b5436430bc75b3bb17a1a3c3063f09b0c05ae2b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903", size = 1254081, upload-time = "2026-05-17T17:47:39.826Z" }, + { url = "https://files.pythonhosted.org/packages/50/e2/1e520793bc6a4e4524a6ab022391e827825eaa0c3811828bfdc6852eca26/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261", size = 1259910, upload-time = "2026-05-17T17:47:41.369Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e1/49b60f467979979cfe6913b43948ff25bca971ad0591d181812f163a988e/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027", size = 1250678, upload-time = "2026-05-17T17:47:43.702Z" }, + { url = "https://files.pythonhosted.org/packages/74/ba/66ab9555de6275677566f6574e5ef6c29cb185ea866f643bc06f8280a8ee/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937", size = 1301603, upload-time = "2026-05-17T17:47:46.256Z" }, + { url = "https://files.pythonhosted.org/packages/66/42/6aca9b9abc710014b2be9059689e5dd1679339e78f567ffb4d255a9e2050/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c", size = 1410332, upload-time = "2026-05-17T17:47:47.899Z" }, + { url = "https://files.pythonhosted.org/packages/47/68/2f76594432a22581ecf878b5e75a9b8601c24b2241cf0bbeb1e21fcf370c/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b", size = 1509979, upload-time = "2026-05-17T17:47:50.942Z" }, + { url = "https://files.pythonhosted.org/packages/40/ac/a93c9b58292653f6c595752f677a08e608f903b710594909e9231a389b3b/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab", size = 1505002, upload-time = "2026-05-17T17:47:54.093Z" }, + { url = "https://files.pythonhosted.org/packages/14/2e/b278f68c497ee2f1d1576cbbef8db5281cd4a5f2db040537592ac9c8862e/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3", size = 1456231, upload-time = "2026-05-17T17:47:56.311Z" }, + { url = "https://files.pythonhosted.org/packages/0b/43/419be1c566a4c504cd8fd60ce2f84e790f295495c0f327cfaeadf3d51012/ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38", size = 1058668, upload-time = "2026-05-17T17:47:58.305Z" }, + { url = "https://files.pythonhosted.org/packages/03/6f/c9d4d549295ed05111aeb8853232d1afd9d0a179fddb01eeffbb3a4a6842/ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c", size = 1101075, upload-time = "2026-05-17T17:48:00.35Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8e/d00c5ab30c58222e07d62956fca86c59d91b9ad32997e633c38b526623a3/ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb", size = 1075347, upload-time = "2026-05-17T17:48:01.753Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9e/dc2530acb3a60dc6e46d65abf27d1d9f86721694757906a148d90a6860de/ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101", size = 1191380, upload-time = "2026-05-17T17:48:03.738Z" }, + { url = "https://files.pythonhosted.org/packages/26/0a/bd3d18a582f273d6c843d16bb9e22e9e16365ff7991e92f18f798e9f1224/ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a", size = 1183879, upload-time = "2026-05-17T17:48:05.463Z" }, + { url = "https://files.pythonhosted.org/packages/40/ae/1f919100f8620887af58fcc381c61a1f218cdf89c6e155f87b213e61010a/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211", size = 1244529, upload-time = "2026-05-17T17:48:07.008Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ca/6376559dcce707cdbc1d0d9a13c8d3baaaa501e949ce0ebdc4230cd881aa/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf", size = 1240560, upload-time = "2026-05-17T17:48:08.46Z" }, + { url = "https://files.pythonhosted.org/packages/35/b2/a620e206b5aeb7efbf2710336df57d457cffbb3991076bbcc1147ef9abd4/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9", size = 1451172, upload-time = "2026-05-17T17:48:09.922Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e0/4ad5c04c24a40481b2935ce9a0ccdb6023dc8b667167d06ae530cc3512f2/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee", size = 1265072, upload-time = "2026-05-17T17:48:11.469Z" }, + { url = "https://files.pythonhosted.org/packages/b2/71/4d1d479aa56d0101c40e17720c3d6ac2af7269ea0487a80b18e7bfd1a5b7/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809", size = 1270488, upload-time = "2026-05-17T17:48:13.575Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4f/0de1bbe06f6edef9fde4ed12ca8e7b3ec7e6e2bd4e672c5af487f7957665/ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43", size = 1260702, upload-time = "2026-05-17T17:48:15.141Z" }, + { url = "https://files.pythonhosted.org/packages/75/61/e00872439cfdddcc3c1b6cdaa6e5d904ba8e26a18807c67c4e14409d0ca8/ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934", size = 1311182, upload-time = "2026-05-17T17:48:16.779Z" }, + { url = "https://files.pythonhosted.org/packages/76/8e/699a5b955f7926956c95e9e1d74132acad73c2fe7a426f94da89123c20aa/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759", size = 1421410, upload-time = "2026-05-17T17:48:18.527Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ae/d5b7626874478997adc7a29ab28accf21e596fb590c944290401dfd0b29e/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887", size = 1516587, upload-time = "2026-05-17T17:48:20.133Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ce/b59e02a82d9c4244d64cde502e0b00e83e38816abe19155ceb5437402c7f/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27", size = 1515171, upload-time = "2026-05-17T17:48:21.921Z" }, + { url = "https://files.pythonhosted.org/packages/8b/38/d8d90042747d05aa08d4efcf1c99035a5f670a6bf4c214d31644392afbca/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d", size = 1464668, upload-time = "2026-05-17T17:48:23.544Z" }, + { url = "https://files.pythonhosted.org/packages/dd/51/5b840c4df7334104cecffa28f23904fe81ca89ca223d2450e288de39fd3c/ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a", size = 1068311, upload-time = "2026-05-17T17:48:25.027Z" }, + { url = "https://files.pythonhosted.org/packages/41/11/ca5672c7d491825bc4cd6702dea106a6b60d928707712ec257c7833ae476/ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590", size = 1108931, upload-time = "2026-05-17T17:48:26.591Z" }, + { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "auto-reimbursement-system" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "flask" }, + { name = "llama-index" }, + { name = "llama-index-llms-openai-like" }, + { name = "pdfplumber" }, + { name = "playwright" }, + { name = "pywin32" }, +] + +[package.dev-dependencies] +dev = [ + { name = "deptry" }, + { name = "mypy" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "flask", specifier = ">=3.0" }, + { name = "llama-index", specifier = ">=0.12.0" }, + { name = "llama-index-llms-openai-like", specifier = "==0.7.2" }, + { name = "pdfplumber", specifier = ">=0.10" }, + { name = "playwright", specifier = ">=1.40" }, + { name = "pywin32", specifier = ">=306" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "deptry", specifier = ">=0.22" }, + { name = "mypy", specifier = ">=1.14" }, + { name = "pre-commit", specifier = ">=4.0" }, + { name = "pytest", specifier = ">=8.0" }, + { name = "pytest-cov", specifier = ">=5.0" }, + { name = "ruff", specifier = ">=0.9" }, +] + +[[package]] +name = "banks" +version = "2.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "filetype" }, + { name = "griffe" }, + { name = "jinja2" }, + { name = "platformdirs" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/51/08fb68d23f4b0f6256fe85dc86e9576941550f890b079352fba719e07b39/banks-2.4.2.tar.gz", hash = "sha256:cda6013bd377ea7b701933578bfb9370fc21ad70bc13cedfc3f5cb2c034ca3dc", size = 188633, upload-time = "2026-04-27T12:15:22.021Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/b6/8dc5477681b782e2f99de703e7a99828883364b9e03a60d3e2c47053d56a/banks-2.4.2-py3-none-any.whl", hash = "sha256:5fe407cc48c101f3e13d1cf732b83b8246003337612f13c0705d2e81f6faffb7", size = 35050, upload-time = "2026-04-27T12:15:20.785Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[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 = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, +] + +[[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 = "coverage" +version = "7.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132, upload-time = "2026-05-26T20:41:36.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022, upload-time = "2026-05-26T20:39:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379, upload-time = "2026-05-26T20:39:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888, upload-time = "2026-05-26T20:39:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624, upload-time = "2026-05-26T20:39:09.037Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739, upload-time = "2026-05-26T20:39:10.889Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998, upload-time = "2026-05-26T20:39:12.722Z" }, + { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296, upload-time = "2026-05-26T20:39:14.259Z" }, + { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658, upload-time = "2026-05-26T20:39:15.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803, upload-time = "2026-05-26T20:39:17.537Z" }, + { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873, upload-time = "2026-05-26T20:39:19.414Z" }, + { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372, upload-time = "2026-05-26T20:39:21.169Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245, upload-time = "2026-05-26T20:39:23.097Z" }, + { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567, upload-time = "2026-05-26T20:39:24.868Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372, upload-time = "2026-05-26T20:39:26.603Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989, upload-time = "2026-05-26T20:39:28.242Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044, upload-time = "2026-05-26T20:39:29.902Z" }, + { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412, upload-time = "2026-05-26T20:39:31.561Z" }, + { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412, upload-time = "2026-05-26T20:39:33.337Z" }, + { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008, upload-time = "2026-05-26T20:39:35.009Z" }, + { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241, upload-time = "2026-05-26T20:39:36.71Z" }, + { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373, upload-time = "2026-05-26T20:39:38.412Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635, upload-time = "2026-05-26T20:39:40.268Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373, upload-time = "2026-05-26T20:39:42.145Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341, upload-time = "2026-05-26T20:39:43.907Z" }, + { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497, upload-time = "2026-05-26T20:39:46.166Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159, upload-time = "2026-05-26T20:39:48.03Z" }, + { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934, upload-time = "2026-05-26T20:39:49.872Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584, upload-time = "2026-05-26T20:39:51.68Z" }, + { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394, upload-time = "2026-05-26T20:39:53.472Z" }, + { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015, upload-time = "2026-05-26T20:39:55.374Z" }, + { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733, upload-time = "2026-05-26T20:39:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086, upload-time = "2026-05-26T20:39:59.019Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381, upload-time = "2026-05-26T20:40:00.822Z" }, + { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458, upload-time = "2026-05-26T20:40:02.506Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884, upload-time = "2026-05-26T20:40:04.421Z" }, + { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022, upload-time = "2026-05-26T20:40:06.298Z" }, + { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631, upload-time = "2026-05-26T20:40:08.226Z" }, + { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443, upload-time = "2026-05-26T20:40:10.137Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069, upload-time = "2026-05-26T20:40:11.999Z" }, + { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780, upload-time = "2026-05-26T20:40:13.858Z" }, + { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970, upload-time = "2026-05-26T20:40:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157, upload-time = "2026-05-26T20:40:18.382Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259, upload-time = "2026-05-26T20:40:20.381Z" }, + { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320, upload-time = "2026-05-26T20:40:22.316Z" }, + { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577, upload-time = "2026-05-26T20:40:24.894Z" }, + { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091, upload-time = "2026-05-26T20:40:27.249Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421, upload-time = "2026-05-26T20:40:30.084Z" }, + { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466, upload-time = "2026-05-26T20:40:32.542Z" }, + { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973, upload-time = "2026-05-26T20:40:34.473Z" }, + { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318, upload-time = "2026-05-26T20:40:38.154Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633, upload-time = "2026-05-26T20:40:40.164Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488, upload-time = "2026-05-26T20:40:42.631Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329, upload-time = "2026-05-26T20:40:45.208Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291, upload-time = "2026-05-26T20:40:47.502Z" }, + { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564, upload-time = "2026-05-26T20:40:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107, upload-time = "2026-05-26T20:40:51.677Z" }, + { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764, upload-time = "2026-05-26T20:40:54.267Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837, upload-time = "2026-05-26T20:40:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650, upload-time = "2026-05-26T20:40:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218, upload-time = "2026-05-26T20:41:00.321Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822, upload-time = "2026-05-26T20:41:02.281Z" }, + { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084, upload-time = "2026-05-26T20:41:04.593Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454, upload-time = "2026-05-26T20:41:06.641Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578, upload-time = "2026-05-26T20:41:08.556Z" }, + { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981, upload-time = "2026-05-26T20:41:10.824Z" }, + { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112, upload-time = "2026-05-26T20:41:12.966Z" }, + { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558, upload-time = "2026-05-26T20:41:15Z" }, + { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447, upload-time = "2026-05-26T20:41:17.369Z" }, + { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048, upload-time = "2026-05-26T20:41:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781, upload-time = "2026-05-26T20:41:21.559Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896, upload-time = "2026-05-26T20:41:23.428Z" }, + { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214, upload-time = "2026-05-26T20:41:25.419Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624, upload-time = "2026-05-26T20:41:27.795Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728, upload-time = "2026-05-26T20:41:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752, upload-time = "2026-05-26T20:41:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815, upload-time = "2026-05-26T20:41:34.078Z" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, +] + +[[package]] +name = "dataclasses-json" +version = "0.6.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marshmallow" }, + { name = "typing-inspect" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227, upload-time = "2024-06-09T16:20:19.103Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" }, +] + +[[package]] +name = "deprecated" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, +] + +[[package]] +name = "deptry" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "packaging" }, + { name = "requirements-parser" }, + { name = "tomli", marker = "python_full_version < '3.15'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/b2/50ccc99362ae7757342978b7ecb3b98e47fade721fd617d74db1948ec3a1/deptry-0.25.1.tar.gz", hash = "sha256:45c8cd982c85cd4faae573ddff6920de7eec735336db6973f26a765ae7950f7d", size = 509748, upload-time = "2026-03-18T23:22:18.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/1d/b538dc635e873b25360d761cfe1fa0ccd7d6c69b698047e552f33401e60d/deptry-0.25.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a4dd1148db24a1ddacfa8b840836c6019c2f864fcb7579dd089fd217606338c8", size = 1850319, upload-time = "2026-03-18T23:22:15.65Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a9/511477a8f0ae4f6021d68a80bdca77e7ffb0722008dc24ee5d9ef49f5c88/deptry-0.25.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c67c666d916ef12013c0772e40d78be0f21577a495d8d99ec5fcb18c332d393d", size = 1759259, upload-time = "2026-03-18T23:22:30.853Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4b/c9f0bdda410912a6df79a789cb118fa29acae02a397794ead3c84adcda5c/deptry-0.25.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58d39279828dbf4efc1abb40bf50a71b21499c36759bed5a8d8a3c0e3149b091", size = 1872012, upload-time = "2026-03-18T23:22:19.145Z" }, + { url = "https://files.pythonhosted.org/packages/72/9c/6f6f9125bac74b5d5d2af89536cbdb3fa159b6466aa097b74e7e85e8e030/deptry-0.25.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14bfcc28b4326ed8c6abb30691b19077d4ef8613cfba6c37ef5b1f471775bf6f", size = 1926575, upload-time = "2026-03-18T23:22:11.269Z" }, + { url = "https://files.pythonhosted.org/packages/52/48/2a5e705a7f898295966ade67bd1223e2af96da433e25b39f6b9483ba2c7b/deptry-0.25.1-cp310-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:555f5f9a487899ec9bf301eecba1745e14d212c4b354f4d3a5fd691e907366d3", size = 2050816, upload-time = "2026-03-18T23:22:27.439Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c6/50f189a894e1f3bf21266299112c8a06cb731838976e1b9a9cadd0b4a86e/deptry-0.25.1-cp310-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:18d21b3545ab2bfec53f3f45c6f5f201d55f713323327f8d12674505469ae6b7", size = 2145416, upload-time = "2026-03-18T23:22:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/7a/6a/3f82f7a06217778282bc4456af1b4ffb3bc4b2c8e7891d00e8323f9ad0b8/deptry-0.25.1-cp310-abi3-win_amd64.whl", hash = "sha256:b59a560cb7dffb21832a98bb80d33d614cfb5630ea36ce21833eabf4eae3df99", size = 1718489, upload-time = "2026-03-18T23:22:28.589Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7f/cd6b3ac8cf95f2f1c5c7a74ff6452e9098af89a9b56607381f677880641e/deptry-0.25.1-cp310-abi3-win_arm64.whl", hash = "sha256:6efffd8116fb9d2c45a251382ce4ce1c38dbb17179f581ec9231ed5390f7fc12", size = 1647020, upload-time = "2026-03-18T23:22:23.311Z" }, +] + +[[package]] +name = "dirtyjson" +version = "1.0.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/04/d24f6e645ad82ba0ef092fa17d9ef7a21953781663648a01c9371d9e8e98/dirtyjson-1.0.8.tar.gz", hash = "sha256:90ca4a18f3ff30ce849d100dcf4a003953c79d3a2348ef056f1d9c22231a25fd", size = 30782, upload-time = "2022-11-28T23:32:33.319Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/69/1bcf70f81de1b4a9f21b3a62ec0c83bdff991c88d6cc2267d02408457e88/dirtyjson-1.0.8-py3-none-any.whl", hash = "sha256:125e27248435a58acace26d5c2c4c11a1c0de0a9c5124c5a94ba78e517d74f53", size = 25197, upload-time = "2022-11-28T23:32:31.219Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/b2/d6fc3f2347f43dada79e5ff118493e8109c98400a0e29a1d5264a3aa479b/distlib-0.4.1.tar.gz", hash = "sha256:c3804d0d2d4b5fcd44036eb860cb6660485fcdf5c2aba53dc324d805837ea65b", size = 610526, upload-time = "2026-06-02T11:17:40.691Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/18/3497c4fa83a76dcb154923fd2075522e8dd6995ecee4093c00ae18160046/distlib-0.4.1-py2.py3-none-any.whl", hash = "sha256:9c2c552c68cbadc619f2d0ed3a69e27c351a3f4c9baa9ffb7df9e9cdc3d19a97", size = 469216, upload-time = "2026-06-02T11:17:38.779Z" }, +] + +[[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 = "filelock" +version = "3.29.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/f9/f38573ed5844586db374d085911740a501ccfa373b455fc9413f09f85237/filelock-3.29.1.tar.gz", hash = "sha256:d97e6b1b9757569626c58caa07dc4beb1613f4a2938b1e8cc81afca398906c9e", size = 59335, upload-time = "2026-06-03T15:19:04.053Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/a0/614c5fe402fd88951df45f4dda2fa3b4e17a99ecd92340771929169b3b95/filelock-3.29.1-py3-none-any.whl", hash = "sha256:85199dfd706869641b72b2e8955d5416a4b2b7dc4b0e8e6d97b4cc1299a6983b", size = 40750, upload-time = "2026-06-03T15:19:02.959Z" }, +] + +[[package]] +name = "filetype" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, +] + +[[package]] +name = "flask" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[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 = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" }, +] + +[[package]] +name = "greenlet" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356, upload-time = "2026-05-20T15:05:03.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220, upload-time = "2026-05-20T13:07:28.463Z" }, + { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585, upload-time = "2026-05-20T14:00:06.141Z" }, + { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215, upload-time = "2026-05-20T14:05:42.675Z" }, + { url = "https://files.pythonhosted.org/packages/7c/6c/de5b1b388cd2d9fbdfeab324863daba37d54e6e233ddbefd70b385a8c591/greenlet-3.5.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:89101bfd5011e069be974903cb3a4e4523845e4ece2d62dcd8d358933c0ef249", size = 620094, upload-time = "2026-05-20T14:09:09.18Z" }, + { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358, upload-time = "2026-05-20T13:14:26.37Z" }, + { url = "https://files.pythonhosted.org/packages/4a/43/1204baffab8a6476464795a7ccf394a3248d4f22c9f87173a15b36b6d971/greenlet-3.5.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:e6cd99ea59dd5d89f0c956606571d79bfe6f68c9eb7f4a4083a41a7f1587edee", size = 422782, upload-time = "2026-05-20T14:01:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475, upload-time = "2026-05-20T14:02:25.29Z" }, + { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625, upload-time = "2026-05-20T13:14:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791, upload-time = "2026-05-20T13:10:39.018Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580, upload-time = "2026-05-20T13:08:45.056Z" }, + { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060, upload-time = "2026-05-20T13:08:51.899Z" }, + { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370, upload-time = "2026-05-20T14:00:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987, upload-time = "2026-05-20T14:05:44.237Z" }, + { url = "https://files.pythonhosted.org/packages/19/ba/c24110c55dffa55aa6e1d98b45310da33801aeba7686ff0190fe5d46fd32/greenlet-3.5.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d40a890035c0058cadbdc4af7569800fd28a0e527a0fdbb7b5f9418f176846ce", size = 622911, upload-time = "2026-05-20T14:09:10.598Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911, upload-time = "2026-05-20T13:14:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/ec/7b/d20db2e8a5ad6c038702f3179b136f93f0a3d1a21a0c0777f3e470cdf4b2/greenlet-3.5.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:67821bb03e4e98664490edb787ff6af501194c29bbee0f5c1dfdcf1dc3d9d436", size = 425228, upload-time = "2026-05-20T14:01:40.837Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689, upload-time = "2026-05-20T14:02:27.225Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602, upload-time = "2026-05-20T13:14:36.344Z" }, + { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683, upload-time = "2026-05-20T13:11:50.57Z" }, + { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499, upload-time = "2026-05-20T13:12:42.028Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cb/c62454606daf5640369c94d8a9dd540599b1bfc090e2d2180cb77f4038d2/greenlet-3.5.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8ab31c9de8651a2facdd5c5bb0011f2380dd1a7af78ce2adf4b56095294fc07", size = 285579, upload-time = "2026-05-20T13:08:56.396Z" }, + { url = "https://files.pythonhosted.org/packages/ec/71/c4270398c2eba968a6071af1dfbdcaeee6ec1c24bc8b435b8cc452700da6/greenlet-3.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e300185139abc337ade480c327183adf42a875ac7181bfe66d7d4efea31fbea", size = 651106, upload-time = "2026-05-20T14:00:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ab/71e34b78a44ec271fb5f550c17bc46d301ddc5953890d935f270b0dcdb5a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7ffdb990dcaa0234cf9845aead5df2e3c3a8b6507d409274dd87e0d5ab05ffc2", size = 663478, upload-time = "2026-05-20T14:05:45.88Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/2d80842910da44f78c286532d084b8a5c3717c844ae80ceb3858738ae89a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c09df69dc1712d131332054a858a3e5cca400967fa3a672e2324fbb0971448c", size = 667767, upload-time = "2026-05-20T14:09:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/96/4efd6fa5c62c85426a0c19077a586258ebc3a2a146ff2493e4312a697a22/greenlet-3.5.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f82b3597e9d83b63408affed0b48fd0f54935edac4302237b9a837be0dae33c", size = 660800, upload-time = "2026-05-20T13:14:29.129Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d3/dad2eecedfbb1ed7050a20dcfae40c1442b74bc7423608be2c7e03ee7133/greenlet-3.5.1-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:a4764e0bfc6a4d114c865b32520805c16a990ef5f286a514413b05d5ecd6a23d", size = 470786, upload-time = "2026-05-20T14:01:42.064Z" }, + { url = "https://files.pythonhosted.org/packages/7a/e0/6c71401a25cac7000261304e866a2f2cc04dc74810d40e2f118aa4799495/greenlet-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c0141e37414c10164e702b8fb1473304221ad98f71600850c6ef7ff4880feba0", size = 1617518, upload-time = "2026-05-20T14:02:28.662Z" }, + { url = "https://files.pythonhosted.org/packages/41/26/c5c06643e8c0af9e7bf18e16cb51d0ab7625155f0392e1c9015d66d556cd/greenlet-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:50ae25a67bea74ea41fb14b960bc532df73eb713417b2d61892dced82fe8d3bc", size = 1681593, upload-time = "2026-05-20T13:14:39.417Z" }, + { url = "https://files.pythonhosted.org/packages/8a/bd/e11a108317485075e68af9d23039619b86b28130c3b50d227d42edece64b/greenlet-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:8a17c42330e261299766b75ac1ea32caa437a9453c8f65d16a13140db378ecd3", size = 239800, upload-time = "2026-05-20T13:09:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/47/f8/8e8e8417b7bf28639a5a56356ef934d0375e1d0c70a57e04d7701e870ffe/greenlet-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:7b5f5fae05b8ac6d176a61b60c394a8cbdc2b5b91b81793066e68745cf165e54", size = 236862, upload-time = "2026-05-20T13:09:10.498Z" }, + { url = "https://files.pythonhosted.org/packages/90/12/41bf27fde4d3605d3773ae57751eda182b8be2f5398011c041173b1d9534/greenlet-3.5.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:ea8da1e900d758d078810d4255d8c6aa572181896a31ec79d779eb79c3adc9ad", size = 293637, upload-time = "2026-05-20T13:12:35.529Z" }, + { url = "https://files.pythonhosted.org/packages/44/44/ba14b23e9757707050c2f397d305bbcae62e5d7cad122f8b6baec5ae4a1f/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a19570c52a21420dcbc94e661994bc325c0b5b11304540fed514586da5dc8f2e", size = 650840, upload-time = "2026-05-20T14:00:11.079Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/5ddc2b686a6844f91abecef43411842426da2e1573f60b49ecf2547f4ae1/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d955c89b75eeca4723d7cc14135f393cd47c32e2a6cb4a8e4c6e760a26b0986", size = 656416, upload-time = "2026-05-20T14:05:47.118Z" }, + { url = "https://files.pythonhosted.org/packages/8c/46/5987dcd1a2570ba84f3b187536b2ca3ae97613387e57f5cfa99df068fe5e/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea37d5a157eb9493820d3792ac4ece28619a394391d2b9f2f78057d396ff0f0f", size = 656607, upload-time = "2026-05-20T14:09:13.949Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f0/d17510297c35a2992712f0bf84de3779749999f7d3d63aa1f09db7c62dbe/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2daaaebd1a5aa88c49045b6baf9310b3263796bd88db713edf37cf53e7bb4e", size = 654397, upload-time = "2026-05-20T13:14:30.696Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c1/6da0a9ddcc29d7e51ef14883fa3dc1e53b3f4ffba00582106c7bf55da1d8/greenlet-3.5.1-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:8d8a23250ea3ec7b36de8fa4b541e9e2db3ee82915cc060ab0631609ad8b28de", size = 488287, upload-time = "2026-05-20T14:01:43.143Z" }, + { url = "https://files.pythonhosted.org/packages/37/eb/147387705bb89092645b012586e7273cb5ed3c90ef7eaf3a69173eaf0209/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbd69cc349e43bf3a8ae1c85548ff0718efc887615c2db16c3833d7b0b072d", size = 1614469, upload-time = "2026-05-20T14:02:30.192Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4e/37ee0da7732b7aa9896f17e15579a9df34b9fcb9dd494f0adfa749af6623/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4378720dd888136c27215a0214d32a4d37c3852765d45bc37aad0623423cfd78", size = 1675115, upload-time = "2026-05-20T13:14:40.972Z" }, + { url = "https://files.pythonhosted.org/packages/57/f3/97dfcf4a6eb5077f8a672234216fb5923eb89f2cab7081cb10b2cf75b605/greenlet-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:45718441607f9325d948db98cbc691276059316d0358c188c246da4e1d4d23d2", size = 245246, upload-time = "2026-05-20T13:12:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/5d/73/d7f72e34b582f694f4a9b248162db7b09cc458a259ba8f0c0bfa1a34ea7d/greenlet-3.5.1-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:2baee5ca02031757ffe8cc3d69f0cc0aec7065ce362622da74f32d3bcab1c541", size = 285575, upload-time = "2026-05-20T13:12:07.043Z" }, + { url = "https://files.pythonhosted.org/packages/df/59/fa9c6e87dc8ad27a95dabe2f29f372b733d05a8a67470f6c901ed9975655/greenlet-3.5.1-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b1ec3274918a81d3ea778b9e75b56b72b33f300edb6cf7f3a7fe1dae56683de", size = 656428, upload-time = "2026-05-20T14:00:12.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f9/e753408871eaa61dfe35e619cfc67512b036fde99893685d50eea9e07146/greenlet-3.5.1-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:111e2390ffffc47d5840b01711dd7fac07d4c09283d0283e7f3264b14e284c64", size = 667064, upload-time = "2026-05-20T14:05:48.662Z" }, + { url = "https://files.pythonhosted.org/packages/dc/74/807a047255bf1e09303627c46dc043dca596b6958a354d904f32ab382005/greenlet-3.5.1-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:10a9a1c0bfbc93d41156ffcb90c75fbc05544054faf15dcc1fdf9765f8b607f0", size = 672962, upload-time = "2026-05-20T14:09:15.532Z" }, + { url = "https://files.pythonhosted.org/packages/96/27/5565b5b40389f1c7753003a07e21892fda8660926787036d5bc0308b8113/greenlet-3.5.1-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e630136e905fe5ff43e86945ae41220b6d1470956a39220e708110ac48d01ea5", size = 665697, upload-time = "2026-05-20T13:14:32.943Z" }, + { url = "https://files.pythonhosted.org/packages/76/32/19d4e13225193c29b13e308015223f7d75fd3d8623d49dd19040d2ce8ec1/greenlet-3.5.1-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:ef08c1567c78074b22d1a200183d52d04a14df447bf70bcbb6a3507a48e776fc", size = 476047, upload-time = "2026-05-20T14:01:44.39Z" }, + { url = "https://files.pythonhosted.org/packages/cf/82/e7de4178c0c2d1c9a5a3be3cc0b33e46a85b3ee4a77c071bf7ad8600e079/greenlet-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:975eac34b44a7077ca4d421348455b94f0f518246a7f14bc6d2fdcfe5b584368", size = 1621256, upload-time = "2026-05-20T14:02:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/00/10/f2dddcf7dacac17dfc68691809589adad06135eb28930429cf58a6467a2f/greenlet-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9ab3c3a0b2ae6198e67c898dad5215a49f9ae0d0081b3c3ec59f333e39eeca26", size = 1685956, upload-time = "2026-05-20T13:14:42.55Z" }, + { url = "https://files.pythonhosted.org/packages/22/17/4a232b32133230ada52f70e9d7f5b65b0caef8772f01849bd8d149e7e4ca/greenlet-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:cbfc69be86e10dcfef5b1e6269d1d6926552aa89ee39e1de3353360c1b6989ab", size = 239802, upload-time = "2026-05-20T13:13:15.481Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ae/4e623a7e6d4d2a5f4cb8e4c82de4169fc637942caae68d6e676b8a128ac5/greenlet-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:92fd6d44ac5e5a887c8a5dc4a8ba0ba908527c31c12f78c6bc7dcfe8aab279f6", size = 236853, upload-time = "2026-05-20T13:15:37.301Z" }, + { url = "https://files.pythonhosted.org/packages/7a/57/816d9cff29119da3505b3d6a5e14a8af89006ac36f47f891ff293ee05af1/greenlet-3.5.1-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:a6fdf2433a5441ef9a95464f7c3e674775da1c8c1177fff311cee1acad4626ed", size = 293877, upload-time = "2026-05-20T13:10:19.078Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/59b0a7c7d140ff1a75626680b9a9899b79a9176cab298b394968fb023295/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7546556f0d649f99f6a361098a55f761181bb2ea12ff150bb16d26092ad88244", size = 655333, upload-time = "2026-05-20T14:00:14.758Z" }, + { url = "https://files.pythonhosted.org/packages/72/1b/5efe127597625042218939d01855109f352779050768b670b52edcc16a6c/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5ee3ea898009fa898f85f9982255d35278c477bebe185beca249cab42d4526c", size = 659443, upload-time = "2026-05-20T14:05:50.159Z" }, + { url = "https://files.pythonhosted.org/packages/c9/9d/1dcdf7b95ab3cf8c7b6d7277c18a5e167312f2b362ddfcc5d5e6d8d84b43/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a57b0d05a0448eed231d59c0ceb287dde984551e54cbc51ac2d4865712838e9c", size = 659998, upload-time = "2026-05-20T14:09:16.912Z" }, + { url = "https://files.pythonhosted.org/packages/6c/6d/c404246ea4d22d097a7426d0efb5b781bd7eb67715f09e79001bd552ab18/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5c81f74d204d3edd136ebfd50dce53acbb776995d721a0fe801626cfc93b8cd", size = 658356, upload-time = "2026-05-20T13:14:35.091Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/c4959664fc231d587d66d8e81f2095e98056ba1954beafdcbe635e251052/greenlet-3.5.1-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:b0703c2cef53e01baec47f7a3868009913ad71ec678bbecb42a6f40895e4ce62", size = 494470, upload-time = "2026-05-20T14:01:45.611Z" }, + { url = "https://files.pythonhosted.org/packages/51/02/f8ee37fb6d2219329f350af241c27fcf12df57e723d11f6fc6d3bacdadaa/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:2c18ef16bf6d4dd410e4dd52996888ea1497be26892fe5bbc73580aba4287b8e", size = 1619216, upload-time = "2026-05-20T14:02:33.403Z" }, + { url = "https://files.pythonhosted.org/packages/93/c5/3dc9475ace2c7a3680da12372cddd7f1ac874eb410a1ac48d3e9dab83782/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:17d86354f0ae6b61bf9be5148d0dd34e06c3cb7c602c671f79f29ac3b150e659", size = 1678427, upload-time = "2026-05-20T13:14:43.71Z" }, + { url = "https://files.pythonhosted.org/packages/df/4e/750c15c317a41ffb36f0bf40b933e3d744a7dede61889f74443ea69690cf/greenlet-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e7516cf6ae6b8a582c2770a0caed47b8a48373ed732c33d69a72913ae6ac923e", size = 245225, upload-time = "2026-05-20T13:13:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/4f/fd/d3baea2eeb7b617efd47e87ca06e2ec2c6118d303aa9e918e0ce16eadc10/greenlet-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:5028648bf2253ec4745add746129d3904121fa7fe871a76bed23c5720573ce0a", size = 239590, upload-time = "2026-05-20T13:13:37.382Z" }, +] + +[[package]] +name = "griffe" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffecli" }, + { name = "griffelib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/49/eb6d2935e27883af92c930ed40cc4c69bcd32c402be43b8ca4ab20510f67/griffe-2.0.2.tar.gz", hash = "sha256:c5d56326d159f274492e9bf93a9895cec101155d944caa66d0fc4e0c13751b92", size = 293757, upload-time = "2026-03-27T11:34:52.205Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/c0/2bb018eecf9a83c68db9cd9fffd9dab25f102ad30ed869451046e46d1187/griffe-2.0.2-py3-none-any.whl", hash = "sha256:2b31816460aee1996af26050a1fc6927a2e5936486856707f55508e4c9b5960b", size = 5141, upload-time = "2026-03-27T11:34:47.721Z" }, +] + +[[package]] +name = "griffecli" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "griffelib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/e0/6a7d661d71bb043656a109b91d84a42b5342752542074ec83b16a6eb97f0/griffecli-2.0.2.tar.gz", hash = "sha256:40a1ad4181fc39685d025e119ae2c5b669acdc1f19b705fb9bf971f4e6f6dffb", size = 56281, upload-time = "2026-03-27T11:34:50.087Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/e8/90d93356c88ac34c20cb5edffca68138df55ca9bbd1a06eccfbcec8fdbe5/griffecli-2.0.2-py3-none-any.whl", hash = "sha256:0d44d39e59afa81e288a3e1c3bf352cc4fa537483326ac06b8bb6a51fd8303a0", size = 9500, upload-time = "2026-03-27T11:34:48.81Z" }, +] + +[[package]] +name = "griffelib" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, +] + +[[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 = "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 = "identify" +version = "2.6.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[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 = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[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.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/53/4f6bddbcde3c71e56d0aa1337ec95950f3d27dd4153e25aadf0feac71751/jiter-0.15.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d", size = 308793, upload-time = "2026-05-19T10:07:35.25Z" }, + { url = "https://files.pythonhosted.org/packages/01/84/c01099b59a285a1ebba64ae93f62bfa036675340fd1b0045ae65890a0442/jiter-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0", size = 309570, upload-time = "2026-05-19T10:07:36.919Z" }, + { url = "https://files.pythonhosted.org/packages/58/64/8fb7f9d45bb98190355454cd04dad8d8f27223d6bd52f83af07f637168a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138", size = 336783, upload-time = "2026-05-19T10:07:38.694Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b6/f5739011d009b3a30f6a53c5240979030ba29ae46a8c67e3a15759f7c37d/jiter-0.15.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61", size = 363555, upload-time = "2026-05-19T10:07:40.832Z" }, + { url = "https://files.pythonhosted.org/packages/e5/12/98a9d9f766665e8a3b6252454e17cb0c464606a28cf2fa09399b003345fa/jiter-0.15.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687", size = 452255, upload-time = "2026-05-19T10:07:42.62Z" }, + { url = "https://files.pythonhosted.org/packages/e8/d5/60f972840f79c5e7544fce567c56f1e4e50468f996baba3e78d823dd62a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879", size = 373559, upload-time = "2026-05-19T10:07:44.201Z" }, + { url = "https://files.pythonhosted.org/packages/ee/cf/d46ef1234ba335aabc2f013210db8e0821a22f5e644a2e9449df199ecc23/jiter-0.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d", size = 346055, upload-time = "2026-05-19T10:07:46.005Z" }, + { url = "https://files.pythonhosted.org/packages/f0/63/4d2749d8d54d230bad9b3a6b0d00cc28c6ff6b2fdffc26a8ccf76cc5a974/jiter-0.15.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb", size = 351406, upload-time = "2026-05-19T10:07:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b9/9965b990035d8773328e0a8c8b457a87bf2b19f6c4126d9d99296be5d16a/jiter-0.15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871", size = 389357, upload-time = "2026-05-19T10:07:49.665Z" }, + { url = "https://files.pythonhosted.org/packages/2d/55/9ddf903deda1413e87fed792f416b7123daee5b8efbad6a202a7421c36a5/jiter-0.15.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77", size = 517263, upload-time = "2026-05-19T10:07:51.537Z" }, + { url = "https://files.pythonhosted.org/packages/e8/76/a0c40ad064d3a20a4fde231e35d56e9a01ce82164278180e82d5daf85469/jiter-0.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d", size = 548646, upload-time = "2026-05-19T10:07:53.196Z" }, + { url = "https://files.pythonhosted.org/packages/23/4f/eca9b954942916ba2f453891b8593ab444cd872396fe66a3936616f236f3/jiter-0.15.0-cp312-cp312-win32.whl", hash = "sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d", size = 206427, upload-time = "2026-05-19T10:07:55.307Z" }, + { url = "https://files.pythonhosted.org/packages/95/bf/8ead82a87495149542748e828d153fd232a512a22c83b02c4815c1a9c7d8/jiter-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7", size = 197300, upload-time = "2026-05-19T10:07:56.651Z" }, + { url = "https://files.pythonhosted.org/packages/f4/e4/9b8a78fb2d894471bc344e37f1949bdd784bd914d031dba0ba3a40c71dd7/jiter-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b", size = 192702, upload-time = "2026-05-19T10:07:58.307Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" }, + { url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" }, + { url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" }, + { url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" }, + { url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" }, + { url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" }, + { url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" }, + { url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" }, + { url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" }, + { url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/079f350ebf7859d081de30aa890f9e3be68516f754f3ba32366ffff4dcee/jiter-0.15.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:ac0d9ddea4350974be7a221fc25895f251a8fee748c889bdced2141c0fec1a49", size = 308884, upload-time = "2026-05-19T10:08:31.667Z" }, + { url = "https://files.pythonhosted.org/packages/04/4e/a2c30a7f69b48c03b20935d647479106fe932f6e63f75faf53937197e05d/jiter-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01a8222cf05ab1128e239421156c207949808acaaea2bdfd33130ae666786e86", size = 310028, upload-time = "2026-05-19T10:08:33.304Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/2e7cdfd3cf8ca967be38c48f5cf474d79f089efaf559a40f15984a77ae69/jiter-0.15.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:182226cbc930c9fab81bc2e41a4da672f89539906dadb05e75670ac07b94f71f", size = 337485, upload-time = "2026-05-19T10:08:35.259Z" }, + { url = "https://files.pythonhosted.org/packages/9b/11/15a1aa28b120b8ee5b4f1fb894c125046225f09847738bd64233d3b84883/jiter-0.15.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71683c38c825452999b5717fcae07ea708e8c93003e808be4319c1b02e3d176e", size = 364223, upload-time = "2026-05-19T10:08:36.694Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/f442e8af5f3d0dcf47b39e83a0efd9ee45ea946aa6d04625dc3181eae3b6/jiter-0.15.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f2218e6a9e5c18bc10fe6d41ac189c442c88eacf11bad9f28ef95a9bef00e6", size = 456387, upload-time = "2026-05-19T10:08:38.143Z" }, + { url = "https://files.pythonhosted.org/packages/da/f4/37f2d2c9f64f49af7da652ed7532bb5a2372e588e6927c3fdd76f911db65/jiter-0.15.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5157de9f76eb4bc5ea74a1219366a25f945ad305641d74e04f59c54087091aa9", size = 374461, upload-time = "2026-05-19T10:08:39.869Z" }, + { url = "https://files.pythonhosted.org/packages/60/28/edcfbbbf0cb15436f36664a8908a0df47ab9006298d4cd937dc08ea932d6/jiter-0.15.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c5db5527c221249a876160663ab891ace358c17f7b9c93ec1478b7f0550e5c", size = 345924, upload-time = "2026-05-19T10:08:41.668Z" }, + { url = "https://files.pythonhosted.org/packages/47/13/89fba6398dab7f202b7278c4b4aac122399d2c0183971c4a57a3b7088df5/jiter-0.15.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:3e4540b8e74e4268811ac05db226a6a128ff572e7e0ce3f1163b693cadb184cd", size = 352283, upload-time = "2026-05-19T10:08:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/1b/da/0f6af8cef2c565a1ab44d970f268c43ccaa72707386ea6388e6fe2b6cd26/jiter-0.15.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62ebd14e47e9aed9df4472afcb2663668ce4d74891cd54f86bf6e44029d6dc89", size = 389985, upload-time = "2026-05-19T10:08:44.915Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ec/b9cb7d6d29e24ee14910266157d2a279d7a8f60ee0df7fa840882976ba64/jiter-0.15.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0be6f5ad41a809f303f416d17cec92a7a725902fb9b4f3de3d19362ac0ef8554", size = 517695, upload-time = "2026-05-19T10:08:46.486Z" }, + { url = "https://files.pythonhosted.org/packages/64/5e/6d1bda880723aae0ad86b4b763f044362448efe31e3e819635d41cb03451/jiter-0.15.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:813dfbb17d65328bf86e5f0905dd277ba2265d3ca20556e86c0c7035b7182e5a", size = 548868, upload-time = "2026-05-19T10:08:48.026Z" }, + { url = "https://files.pythonhosted.org/packages/0c/72/7de501cf38dcacaf35098796f3a50e0f2e338baba18a58946c618544b809/jiter-0.15.0-cp314-cp314-win32.whl", hash = "sha256:50e51156192722a9c58db112837d3f8ef96fb3c5ecc14e95f409134b08b158ec", size = 206380, upload-time = "2026-05-19T10:08:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a9/e19addf4b0c1bdce52c6da12351e6bc42c340c45e7c09e2158e46d293ccc/jiter-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:30ce1a5d16b5641dc935d50ef775af6a0871e3d14ab05d6fc54dff371b78e558", size = 197687, upload-time = "2026-05-19T10:08:51.088Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c9/776b1db01db25fc6c1d58d1979a37b0a9fe787e5f5b1d062d2eaacb77923/jiter-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:510c8b3c17a0ed9ac69850c0438dada3c9b82d9c4d589fcb62002a5a9cf3a866", size = 192571, upload-time = "2026-05-19T10:08:52.451Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f6/45bb4670bacf300fd2c7abadbfb3af376e5f1b6ae75fd9bc069891d15870/jiter-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7553333dd0930c104a5a0db8df72bf7219fe663d731383b576bb6ed6351c984d", size = 317151, upload-time = "2026-05-19T10:08:53.867Z" }, + { url = "https://files.pythonhosted.org/packages/d7/68/ed635ad5acd7b73e454283083bbb7c8205ad10e88b0d9d7d793b09fe8226/jiter-0.15.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2143ab06181d2b029eedcb6af3cebe95f11bbac62441781860f98ee9330a6a6", size = 341243, upload-time = "2026-05-19T10:08:55.383Z" }, + { url = "https://files.pythonhosted.org/packages/5d/db/3ff4176b817b8ea33879e71e13d8bc2b0d481a7ed3fe9e080f333d415c16/jiter-0.15.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eac374c5c975709b69c10f09afd199df74150172156ad10c8d4fd785b7da995", size = 363629, upload-time = "2026-05-19T10:08:56.928Z" }, + { url = "https://files.pythonhosted.org/packages/ab/24/5f8270e0ba9c883582f96f722f8a0b58015c7ce1f8c6d4571cf394e99b6b/jiter-0.15.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3b3b775e33d3bfaec9899edc526ae97b0da0bf9d071a46124ba419149a414f8", size = 456198, upload-time = "2026-05-19T10:08:58.618Z" }, + { url = "https://files.pythonhosted.org/packages/45/5b/76fc02b0b5c54c3d18c60653156e2f76fde1816f9b4722db68d6ee2c897e/jiter-0.15.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3071db3346334beae1360b46da4606da57bf3528c167b3c38533afaf9f2c5", size = 373710, upload-time = "2026-05-19T10:09:00.151Z" }, + { url = "https://files.pythonhosted.org/packages/c4/52/4310821b0ea9277994d3e1f49fc6a4b34e4800caebacb2c0af81da59a454/jiter-0.15.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6694a173ecabc12eb60efbc0b474464ead1951ff65cd8b1e72100715c64512b", size = 349901, upload-time = "2026-05-19T10:09:01.621Z" }, + { url = "https://files.pythonhosted.org/packages/93/fe/67648c35b3594fba8854ac64cc8a826d8bcd18324bbdb53d77697c60b6ef/jiter-0.15.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a254e10b593624d230c365b6d616b22ca0ad65e63a16e6631c2b3466022e6ba8", size = 352438, upload-time = "2026-05-19T10:09:03.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/0a1879d07ad6b3e025a2750027363452ced93c2d16d1c9d4b153ffd51c91/jiter-0.15.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d8d2955167274e15d79a7a020afdd9b39c990eb80b2d89fca695d92dcfdd38ec", size = 388152, upload-time = "2026-05-19T10:09:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/c1/78/46c6f6b56ba85c90021f4afd72ed42f691f8f84daacb5fe27277070e3858/jiter-0.15.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:acf4ee4d1fc55917239fe72972fb292dd773055d05eb040d36f4326e02cc2c0e", size = 517707, upload-time = "2026-05-19T10:09:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/ca/cb/720662d4c88fcad606e826fef5424365527ba43ce4868a479aed8f8c507e/jiter-0.15.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:e7196e56f1cd69af1dbb07dff02dcfb260a50b45a82d409d92a06fedb32473b5", size = 548241, upload-time = "2026-05-19T10:09:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/60/e3/935b8034fd143f21125c87d51404a9e0e1449186a494405721ff5d1d695e/jiter-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:7f6163c0f10b055245f814dcc59f4818da60dfe72f3e72ab89fc24b6bd5e9c52", size = 207950, upload-time = "2026-05-19T10:09:09.616Z" }, + { url = "https://files.pythonhosted.org/packages/93/59/984fd9ece895953dad3e0880a650e766f5a2da2c5514f0eafdaaabbeb5f9/jiter-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:980c256edb05b78a111b99c4de3b1d32e31634b867fd1fc2cf726e7b7bba9854", size = 200055, upload-time = "2026-05-19T10:09:11.367Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/cf8d779feb133a27a2e3bc833bccb9e13aa332cdf820497ebf72c10ce8c3/jiter-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:66b1880df2d01e206e8339769d1c7c1753bcb653efd6289e203f6f24ebada0c0", size = 191244, upload-time = "2026-05-19T10:09:12.74Z" }, + { url = "https://files.pythonhosted.org/packages/73/38/505941b2b092fd5bbbd60a52a880db1173f1690ae6751bed3af1c9ddcb4e/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0", size = 303769, upload-time = "2026-05-19T10:09:42.203Z" }, + { url = "https://files.pythonhosted.org/packages/e7/95/a06692b29e77473f286e1ec1f426d3ca44d7b5843be8ad21d7a5f3fcdcc0/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45", size = 305128, upload-time = "2026-05-19T10:09:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/85/7270d7ad41d6061a25b950c6bf91d638bd9aacb113200a8c8d57a055fd67/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c", size = 340459, upload-time = "2026-05-19T10:09:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" }, +] + +[[package]] +name = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, +] + +[[package]] +name = "librt" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" }, + { url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" }, + { url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" }, + { url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" }, + { url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" }, + { url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" }, + { url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" }, + { url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" }, + { url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" }, + { url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" }, + { url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" }, + { url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/29/eb/dbce197da4e227779e56b5735f2decc3eb36e55a1cdbf1bd65d6639d76c1/librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd", size = 143345, upload-time = "2026-05-10T18:16:30.674Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/254bebd0c11c8ba684018efb8006ff22e466abce445215cca6c778e7d9de/librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4", size = 143131, upload-time = "2026-05-10T18:16:32.037Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3f/f77d6122d21ac7bf6ae8a7dfced1bd2a7ac545d3273ebdcaf8042f6d619f/librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8", size = 477024, upload-time = "2026-05-10T18:16:33.493Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0a/2c996dadebaa7d9bbbd43ef2d4f3e66b6da545f838a41694ef6172cebec8/librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b", size = 474221, upload-time = "2026-05-10T18:16:34.864Z" }, + { url = "https://files.pythonhosted.org/packages/0a/7e/f5d92af8486b8272c23b3e686b46ff72d89c8169585eb61eef01a2ac7147/librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175", size = 505174, upload-time = "2026-05-10T18:16:36.705Z" }, + { url = "https://files.pythonhosted.org/packages/af/1a/cb0734fe86398eb33193ab753b7326255c74cac5eb09e76b9b16536e7adb/librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03", size = 497216, upload-time = "2026-05-10T18:16:38.418Z" }, + { url = "https://files.pythonhosted.org/packages/18/06/094820f91558b66e29943c0ec41c9914f460f48dd51fc503c3101e10842d/librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c", size = 513921, upload-time = "2026-05-10T18:16:39.848Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c2/00de9018871a282f530cacb457d5ec0428f6ac7e6fedde9aff7468d9fb04/librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3", size = 520850, upload-time = "2026-05-10T18:16:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/51/9d/64631832348fd1834fb3a61b996434edddaaf25a31d03b0a76273159d2cf/librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96", size = 504237, upload-time = "2026-05-10T18:16:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ec/ae5525eb16edc827a044e7bb8777a455ff95d4bca9379e7e6bddd7383647/librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe", size = 546261, upload-time = "2026-05-10T18:16:44.408Z" }, + { url = "https://files.pythonhosted.org/packages/5a/09/adce371f27ca039411da9659f7430fcc2ba6cd0c7b3e4467a0f091be7fa9/librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f", size = 96965, upload-time = "2026-05-10T18:16:46.039Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ee/8ac720d98548f173c7ce2e632a7ca94673f74cacd5c8162a84af5b35958a/librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7", size = 115151, upload-time = "2026-05-10T18:16:47.133Z" }, + { url = "https://files.pythonhosted.org/packages/94/20/c900cf14efeb09b6bef2b2dff20779f73464b97fd58d1c6bccc379588ae3/librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1", size = 98850, upload-time = "2026-05-10T18:16:48.597Z" }, + { url = "https://files.pythonhosted.org/packages/0c/71/944bfe4b64e12abffcd3c15e1cce07f72f3d55655083786285f4dedeb532/librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72", size = 151138, upload-time = "2026-05-10T18:16:49.839Z" }, + { url = "https://files.pythonhosted.org/packages/b6/10/99e64a5c86989357fda078c8143c533389585f6473b7439172dd8f3b3b2d/librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa", size = 151976, upload-time = "2026-05-10T18:16:51.062Z" }, + { url = "https://files.pythonhosted.org/packages/21/31/5072ad880946d83e5ea4147d6d018c78eefce85b77819b19bdd0ee229435/librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548", size = 557927, upload-time = "2026-05-10T18:16:52.632Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8d/70b5fb7cfbab60edbe7381614ab985da58e144fbf465c86d44c95f43cdca/librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2", size = 539698, upload-time = "2026-05-10T18:16:53.934Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a3/ba3495a0b3edbd24a4cae0d1d3c64f39a9fc45d06e812101289b50c1a619/librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f", size = 577162, upload-time = "2026-05-10T18:16:55.589Z" }, + { url = "https://files.pythonhosted.org/packages/f7/db/36e25fb81f99937ff1b96612a1dc9fd66f039cb9cc3aee12c01fac31aab9/librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51", size = 566494, upload-time = "2026-05-10T18:16:56.975Z" }, + { url = "https://files.pythonhosted.org/packages/33/0d/3f622b47f0b013eeb9cf4cc07ae9bfe378d832a4eec998b2b209fe84244d/librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2", size = 596858, upload-time = "2026-05-10T18:16:58.374Z" }, + { url = "https://files.pythonhosted.org/packages/a9/02/71b90bc93039c46a2000651f6ad60122b114c8f54c4ad306e0e96f5b75ad/librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085", size = 590318, upload-time = "2026-05-10T18:16:59.676Z" }, + { url = "https://files.pythonhosted.org/packages/04/04/418cb3f75621e2b761fb1ab0f017f4d70a1a72a6e7c74ee4f7e8d198c2f3/librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3", size = 575115, upload-time = "2026-05-10T18:17:01.007Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2c/5a2183ac58dd911f26b5d7e7d7d8f1d87fcecdddd99d6c12169a258ff62c/librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd", size = 617918, upload-time = "2026-05-10T18:17:02.682Z" }, + { url = "https://files.pythonhosted.org/packages/15/1f/dc6771a52592a4451be6effa200cbfc9cec61e4393d3033d81a9d307961d/librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8", size = 103562, upload-time = "2026-05-10T18:17:03.99Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/7d1415567027286a75ba1093ec4aca11f073e0f559c530cf3e0a757ad55c/librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c", size = 124327, upload-time = "2026-05-10T18:17:05.465Z" }, + { url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" }, +] + +[[package]] +name = "llama-index" +version = "0.14.22" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llama-index-core" }, + { name = "llama-index-embeddings-openai" }, + { name = "llama-index-llms-openai" }, + { name = "nltk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/89/3b6f3318ea2249158daab3ff22777ef5ffa87a63c011659a6cfc55e54c35/llama_index-0.14.22.tar.gz", hash = "sha256:c2c9b31f50d2815abdc191085db4acaf96b7c01851ac66b2e4cc82be8cde589e", size = 8565, upload-time = "2026-05-14T20:22:21.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/fd/f0837c4ce049d8ece7525bbf64564e93e3f16333856c2a0b47fecb58f317/llama_index-0.14.22-py3-none-any.whl", hash = "sha256:14b4bdd799112062e38288eab6aa16643f29d7532505ab174b0b6d5b0817fe94", size = 7115, upload-time = "2026-05-14T20:22:19.611Z" }, +] + +[[package]] +name = "llama-index-core" +version = "0.14.22" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "aiosqlite" }, + { name = "banks" }, + { name = "dataclasses-json" }, + { name = "deprecated" }, + { name = "dirtyjson" }, + { name = "filetype" }, + { name = "fsspec" }, + { name = "httpx" }, + { name = "llama-index-workflows" }, + { name = "nest-asyncio" }, + { name = "networkx" }, + { name = "nltk" }, + { name = "numpy" }, + { name = "pillow" }, + { name = "platformdirs" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "setuptools" }, + { name = "sqlalchemy", extra = ["asyncio"] }, + { name = "tenacity" }, + { name = "tiktoken" }, + { name = "tinytag" }, + { name = "tqdm" }, + { name = "typing-extensions" }, + { name = "typing-inspect" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/7f/94a4b940ef0d069840df0fd6d361a2aa832a2dd73b4cecdf86e8f8c353c8/llama_index_core-0.14.22.tar.gz", hash = "sha256:1384410f89bdbd32349aab444ef4f5c828c338787bc65bd1ffd8e86dfb44ac41", size = 11584786, upload-time = "2026-05-14T20:21:37.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/15/e1a26d8d56aa55fa07587a3e9c7e85294d2df5af6c2229193019bc549ef6/llama_index_core-0.14.22-py3-none-any.whl", hash = "sha256:9cfffde46fd5b7937101e1c0c9bb5c21bd7ff8c8a56937810b87ba3542f31225", size = 11920774, upload-time = "2026-05-14T20:21:40.409Z" }, +] + +[[package]] +name = "llama-index-embeddings-openai" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llama-index-core" }, + { name = "openai" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/52/eb56a4887501651fb17400f7f571c1878109ff698efbe0bbac9165a5603d/llama_index_embeddings_openai-0.6.0.tar.gz", hash = "sha256:eb3e6606be81cb89125073e23c97c0a6119dabb4827adbd14697c2029ad73f29", size = 7629, upload-time = "2026-03-12T20:21:27.234Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/d1/4bb0b80f4057903110060f617ef519197194b3ff5dd6153d850c8f5676fa/llama_index_embeddings_openai-0.6.0-py3-none-any.whl", hash = "sha256:039bb1007ad4267e25ddb89a206dfdab862bfb87d58da4271a3919e4f9df4d61", size = 7666, upload-time = "2026-03-12T20:21:28.079Z" }, +] + +[[package]] +name = "llama-index-instrumentation" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/d0/671b23ccff255c9bce132a84ffd5a6f4541ceefdeab9c1786b08c9722f2e/llama_index_instrumentation-0.5.0.tar.gz", hash = "sha256:eeb724648b25d149de882a5ac9e21c5acb1ce780da214bda2b075341af29ad8e", size = 43831, upload-time = "2026-03-12T20:17:06.742Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/45/6dcaccef44e541ffa138e4b45e33e0d40ab2a7d845338483954fcf77bc75/llama_index_instrumentation-0.5.0-py3-none-any.whl", hash = "sha256:aaab83cddd9dd434278891012d8995f47a3bc7ed1736a371db90965348c56a21", size = 16444, upload-time = "2026-03-12T20:17:05.957Z" }, +] + +[[package]] +name = "llama-index-llms-openai" +version = "0.7.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llama-index-core" }, + { name = "openai" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/85/f07466d0f5c3f1e1f295a60f2d2e9d32163635ba39ae2fda22ce2fbac1c9/llama_index_llms_openai-0.7.9.tar.gz", hash = "sha256:f54a24b717134c86e724007057a06a84394f019d1f01e918b624894e208a86df", size = 27564, upload-time = "2026-05-29T15:32:37.141Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/c2/cfa341cff00154b58abed3fc559fb43c2531d5dd70eefb75f230704d4c3c/llama_index_llms_openai-0.7.9-py3-none-any.whl", hash = "sha256:0bc8f59faddf8dbc9f90c5576127ab2fa6d368be5078885dc7e611af129b5a79", size = 28648, upload-time = "2026-05-29T15:32:35.997Z" }, +] + +[[package]] +name = "llama-index-llms-openai-like" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llama-index-core" }, + { name = "llama-index-llms-openai" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/a3/16410b28d131aa113ada79f856b78cb68a8e92a1e27255ea9c36c27a5dec/llama_index_llms_openai_like-0.7.2.tar.gz", hash = "sha256:ed9ff73f975dce470f98ac61c982151ba78eedfa3fb9b03894bc1d1312b213ff", size = 5389, upload-time = "2026-04-23T23:05:32.525Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/0c/fdddaee5391d915d3d568d2d8dbdb7c95647e65bb94d4ddb31d47cef5daf/llama_index_llms_openai_like-0.7.2-py3-none-any.whl", hash = "sha256:1f45a7b1cec8fb3f5997684327ffe6c19f93e789c2fff35dc5522465850faf0b", size = 6602, upload-time = "2026-04-23T23:05:31.708Z" }, +] + +[[package]] +name = "llama-index-workflows" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llama-index-instrumentation" }, + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/ec/05f3db99a2e6e252e3939e7751cad2fb1322dc6d32f4cf5c795cf7ddcad3/llama_index_workflows-2.20.0.tar.gz", hash = "sha256:df2760fea9e100c97a4e919d255461e344413acac4382d17d8217337806e4772", size = 97410, upload-time = "2026-04-24T14:54:41.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/5f/385231406d777cb4b608fd8ebe3577dbd90962770717181e6b91b44fb1b8/llama_index_workflows-2.20.0-py3-none-any.whl", hash = "sha256:36f6b6ace77f837d9907078aea7e830251afe96a58daecff5ed090c88c55095d", size = 121238, upload-time = "2026-04-24T14:54:40.455Z" }, +] + +[[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 = "marshmallow" +version = "3.26.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/79/de6c16cc902f4fc372236926b0ce2ab7845268dcc30fb2fbb7f71b418631/marshmallow-3.26.2.tar.gz", hash = "sha256:bbe2adb5a03e6e3571b573f42527c6fe926e17467833660bebd11593ab8dfd57", size = 222095, upload-time = "2025-12-22T06:53:53.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/2f/5108cb3ee4ba6501748c4908b908e55f42a5b66245b4cfe0c99326e1ef6e/marshmallow-3.26.2-py3-none-any.whl", hash = "sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73", size = 50964, upload-time = "2025-12-22T06:53:51.801Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "mypy" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ast-serialize" }, + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" }, + { url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" }, + { url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" }, + { url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" }, + { url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" }, + { url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" }, + { url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ca/b279a672e874aedd5498ae25f722dacc8aa86bbffb939b3f97cbb1cf6686/mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2", size = 14848422, upload-time = "2026-05-11T18:35:45.984Z" }, + { url = "https://files.pythonhosted.org/packages/27/e6/3efe56c631d959b9b4454e208b0ac4b7f4f58b404c89f8bec7b49efdfc21/mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f", size = 13677374, upload-time = "2026-05-11T18:36:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/84/7f/8107ea87a44fd1f1b59882442f033c9c3488c127201b1d1d15f1cbd6022e/mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4", size = 14055743, upload-time = "2026-05-11T18:35:18.361Z" }, + { url = "https://files.pythonhosted.org/packages/51/4d/b6d34db183133b83761b9199a82d31557cdbb70a380d8c3b3438e11882a3/mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef", size = 15020937, upload-time = "2026-05-11T18:34:59.618Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d7/f08360c691d758acb02f45022c34d98b92892f4ea756644e1000d4b9f3d8/mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135", size = 15253371, upload-time = "2026-05-11T18:36:41.081Z" }, + { url = "https://files.pythonhosted.org/packages/67/1b/09460a13719530a19bce27bd3bc8449e83569dd2ba7faf51c9c3c30c0b61/mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21", size = 11326429, upload-time = "2026-05-11T18:34:13.526Z" }, + { url = "https://files.pythonhosted.org/packages/40/62/75dbf0f82f7b6680340efc614af29dd0b3c17b8a4f1cd09b8bd2fd6bc814/mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57", size = 10218799, upload-time = "2026-05-11T18:32:23.491Z" }, + { url = "https://files.pythonhosted.org/packages/b2/66/caca04ed7d972fb6eb6dd1ccd6df1de5c38fae8c5b3dc1c4e8e0d85ee6b9/mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e", size = 15923458, upload-time = "2026-05-11T18:35:28.64Z" }, + { url = "https://files.pythonhosted.org/packages/ed/52/2d90cbe49d014b13ed7ff337930c30bad35893fe38a1e4641e756bb62191/mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780", size = 14757697, upload-time = "2026-05-11T18:36:14.208Z" }, + { url = "https://files.pythonhosted.org/packages/ac/37/d98f4a14e081b238992d0ed96b6d39c7cc0148c9699eb71eaa68629665ea/mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd", size = 15405638, upload-time = "2026-05-11T18:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c2/15c46613b24a84fad2aea1248bf9619b99c2767ae9071fe224c179a0b7d4/mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08", size = 16215852, upload-time = "2026-05-11T18:32:50.296Z" }, + { url = "https://files.pythonhosted.org/packages/5c/90/9c16a57f482c76d25f6379762b56bbf65c711d8158cf271fb2802cfb0640/mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081", size = 16452695, upload-time = "2026-05-11T18:33:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/0f/4c/215a4eeb63cacc5f17f516691ea7285d11e249802b942476bff15922a314/mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7", size = 12866622, upload-time = "2026-05-11T18:34:39.945Z" }, + { url = "https://files.pythonhosted.org/packages/4b/50/1043e1db5f455ffe4c9ab22747cd8ca2bc492b1e4f4e21b130a44ee2b217/mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6", size = 10610798, upload-time = "2026-05-11T18:36:31.444Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, +] + +[[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 = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "nltk" +version = "3.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "joblib" }, + { name = "regex" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/a1/b3b4adf15585a5bc4c357adde150c01ebeeb642173ded4d871e89468767c/nltk-3.9.4.tar.gz", hash = "sha256:ed03bc098a40481310320808b2db712d95d13ca65b27372f8a403949c8b523d0", size = 2946864, upload-time = "2026-03-24T06:13:40.641Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/91/04e965f8e717ba0ab4bdca5c112deeab11c9e750d94c4d4602f050295d39/nltk-3.9.4-py3-none-any.whl", hash = "sha256:f2fa301c3a12718ce4a0e9305c5675299da5ad9e26068218b69d692fda84828f", size = 1552087, upload-time = "2026-03-24T06:13:38.47Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873, upload-time = "2025-11-16T22:49:49.84Z" }, + { url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838, upload-time = "2025-11-16T22:49:52.863Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378, upload-time = "2025-11-16T22:49:55.055Z" }, + { url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559, upload-time = "2025-11-16T22:49:57.371Z" }, + { url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702, upload-time = "2025-11-16T22:49:59.632Z" }, + { url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086, upload-time = "2025-11-16T22:50:02.127Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985, upload-time = "2025-11-16T22:50:04.536Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976, upload-time = "2025-11-16T22:50:07.557Z" }, + { url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274, upload-time = "2025-11-16T22:50:10.746Z" }, + { url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922, upload-time = "2025-11-16T22:50:12.811Z" }, + { url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667, upload-time = "2025-11-16T22:50:16.16Z" }, + { url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" }, + { url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" }, + { url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" }, + { url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" }, + { url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" }, + { url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" }, + { url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" }, + { url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" }, + { url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" }, + { url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" }, + { url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" }, + { url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" }, + { url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" }, + { url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" }, + { url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" }, + { url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" }, + { url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" }, + { url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" }, + { url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" }, + { url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" }, + { url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" }, + { url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" }, + { url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" }, + { url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" }, + { url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" }, + { url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" }, + { url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" }, + { url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" }, +] + +[[package]] +name = "openai" +version = "2.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/a6/5815fe2e2aca74b36c650d1bd43b69827cee568073d0d2d9b6fc5aaac80c/openai-2.41.0.tar.gz", hash = "sha256:db5c362acd6604b84f076abbefa66826ea4b46ecba2954ed866e6a149a1352c0", size = 783525, upload-time = "2026-06-03T22:39:40.719Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/51/d82bb424e8aa372190c5233253a2ceb399a778747d18b42cff487411e663/openai-2.41.0-py3-none-any.whl", hash = "sha256:20cc7952e8501c7e5773dd2ef7be437bae9cb549044902e1041a83a54516e375", size = 1353378, upload-time = "2026-06-03T22:39:38.964Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "pdfminer-six" +version = "20251230" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "charset-normalizer" }, + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/9a/d79d8fa6d47a0338846bb558b39b9963b8eb2dfedec61867c138c1b17eeb/pdfminer_six-20251230.tar.gz", hash = "sha256:e8f68a14c57e00c2d7276d26519ea64be1b48f91db1cdc776faa80528ca06c1e", size = 8511285, upload-time = "2025-12-30T15:49:13.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/d7/b288ea32deb752a09aab73c75e1e7572ab2a2b56c3124a5d1eb24c62ceb3/pdfminer_six-20251230-py3-none-any.whl", hash = "sha256:9ff2e3466a7dfc6de6fd779478850b6b7c2d9e9405aa2a5869376a822771f485", size = 6591909, upload-time = "2025-12-30T15:49:10.76Z" }, +] + +[[package]] +name = "pdfplumber" +version = "0.11.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pdfminer-six" }, + { name = "pillow" }, + { name = "pypdfium2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/37/9ca3519e92a8434eb93be570b131476cc0a4e840bb39c62ddb7813a39d53/pdfplumber-0.11.9.tar.gz", hash = "sha256:481224b678b2bbdbf376e2c39bf914144eef7c3d301b4a28eebf0f7f6109d6dc", size = 102768, upload-time = "2026-01-05T08:10:29.072Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/c8/cdbc975f5b634e249cfa6597e37c50f3078412474f21c015e508bfbfe3c3/pdfplumber-0.11.9-py3-none-any.whl", hash = "sha256:33ec5580959ba524e9100138746e090879504c42955df1b8a997604dd326c443", size = 60045, upload-time = "2026-01-05T08:10:27.512Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, +] + +[[package]] +name = "playwright" +version = "1.60.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet" }, + { name = "pyee" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/f0/832bd9677194908da118064eef20082f2791e3d18215cc6d9391ee2c5a67/playwright-1.60.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:6a8cd0fec171fb3089e95e898c8bc8a6f35dea0b78b399e12fcc19427e91b1d7", size = 43474635, upload-time = "2026-05-18T12:00:31.969Z" }, + { url = "https://files.pythonhosted.org/packages/59/7b/e1d32ae8a3ed937ec2be3721c5f728b13d731a0b7c6442e0b3bec5094ac0/playwright-1.60.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:39b5420ba6145045b69ced4c5c47d4d9fe5bddfc8ff816c518913afcb25ec7a5", size = 42261327, upload-time = "2026-05-18T12:00:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/d7/bc/23de499ded6411c188a20c5a0dea6f0cd4ed5d2b3cc6042a5dbd3ed609aa/playwright-1.60.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:2581d0e6a3392c71f91b27460c7fd093356818dc430f48153896c8aeeaef7705", size = 43474636, upload-time = "2026-05-18T12:00:39.294Z" }, + { url = "https://files.pythonhosted.org/packages/22/7b/1d679f4fced4ea94efadd17103856d8c565384f68382a1681264e46f5925/playwright-1.60.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:1c2bfae7884fb3fb05b853290eab8f343d524e5016f2f1def702acbbdf14c93e", size = 47467220, upload-time = "2026-05-18T12:00:43.179Z" }, + { url = "https://files.pythonhosted.org/packages/84/c2/1528d267d4442bd2c6b8eaeab819dd52c2030bf80e89293f0ba1f687473b/playwright-1.60.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43e66564125ee31b07a58cefb21e256d62d67d8d1713e6858df7a3019d8ed353", size = 47154856, upload-time = "2026-05-18T12:00:46.715Z" }, + { url = "https://files.pythonhosted.org/packages/bb/4e/b008b6440a7a1624378041da94829956d4b8f7ab9ef5aad22d0dc3f2e26d/playwright-1.60.0-py3-none-win32.whl", hash = "sha256:ec94e416ea320711e0ad4bf185dcbf41833672961e90773e1885255d7db7b7e7", size = 37902157, upload-time = "2026-05-18T12:00:50.374Z" }, + { url = "https://files.pythonhosted.org/packages/55/f0/0541524133104f9cc20bf900870ff4a736b76a23483f3a55295ddfa58409/playwright-1.60.0-py3-none-win_amd64.whl", hash = "sha256:9566821ce6030a1f9e7146a24e19355ab0d98805fd0f9be50bb3d8fef1750c02", size = 37902159, upload-time = "2026-05-18T12:00:53.728Z" }, + { url = "https://files.pythonhosted.org/packages/80/c8/210f282d278e4709cdd71b12a31af45a30a22ab3207b387e29b37e478713/playwright-1.60.0-py3-none-win_arm64.whl", hash = "sha256:6e4f6700a4c2250efff8e690a81d66e3855754fb587b6b87cf5c784014f91537", size = 34037981, upload-time = "2026-05-18T12:00:57.584Z" }, +] + +[[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 = "pre-commit" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +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/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, +] + +[[package]] +name = "pyee" +version = "13.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/04/e7c1fe4dc78a6fdbfd6c337b1c3732ff543b8a397683ab38378447baa331/pyee-13.0.1.tar.gz", hash = "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", size = 31655, upload-time = "2026-02-14T21:12:28.044Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/b4d4827c93ef43c01f599ef31453ccc1c132b353284fc6c87d535c233129/pyee-13.0.1-py3-none-any.whl", hash = "sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228", size = 15659, upload-time = "2026-02-14T21:12:26.263Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pypdfium2" +version = "5.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/98/6b44bf82ddb3c7a3e0249203772aad8981b4491d6227f182685f310faeff/pypdfium2-5.9.0.tar.gz", hash = "sha256:db1274bd27844db6fda17ef1dbcd0026c47d357437058d838e98060c0da9e92e", size = 272455, upload-time = "2026-06-01T15:43:38.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/d9/59630cb40e5f37e7712e6ea65e9cac633f4195e8b737bb3a46054aa63340/pypdfium2-5.9.0-py3-none-android_23_arm64_v8a.whl", hash = "sha256:91914837c4a4285b3e0724a84eca8079363db7475acbcab405933d1807785664", size = 3407817, upload-time = "2026-06-01T15:42:58.426Z" }, + { url = "https://files.pythonhosted.org/packages/0f/3d/e205708835a3730d5242652b6577ac06ad4721e6fcef77cc7c9d3541c686/pypdfium2-5.9.0-py3-none-android_23_armeabi_v7a.whl", hash = "sha256:90610d352f050b065b703f3a46602a852fce7dd8787300c8c7a472485b644d8f", size = 2862706, upload-time = "2026-06-01T15:43:00.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/47/e843fb895a891438b3f8c6d834fdc9c19183cd60980fc9325429d5c01505/pypdfium2-5.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6c4fbe3a7190b329c526358fb2855d797f7b74b5ecfc61d19657ef20bcebc108", size = 3489945, upload-time = "2026-06-01T15:43:02.542Z" }, + { url = "https://files.pythonhosted.org/packages/35/bd/f5e6afd556f97fcaa2bec4cb04669664c166028fc2a059bd65447c852b43/pypdfium2-5.9.0-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:e93f0cf440169a3e445e6fbd06c803877e7418f3e13254287875cb67f208bb5a", size = 3674186, upload-time = "2026-06-01T15:43:04.496Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4d/5286812216a292d51dfba8e7bff276da198f126508f8c2afa3630bf701dc/pypdfium2-5.9.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d902e03dff5efd51d93cd23d3e55bde53802fa6207bcd0e455239518859a069", size = 3669571, upload-time = "2026-06-01T15:43:06.571Z" }, + { url = "https://files.pythonhosted.org/packages/ac/c8/822db2c89baa13e6cee321d587fcd42df463a1fc2f7520b3f6814768bc71/pypdfium2-5.9.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cf38d7ad3575947b82384869f2ab69ba345eb21d83118d25db3e83f967b0421", size = 3400412, upload-time = "2026-06-01T15:43:08.35Z" }, + { url = "https://files.pythonhosted.org/packages/1a/dd/7d09d8cdc28383df13f739a97ac4f1215a704a97a29506dee2bf89d8a350/pypdfium2-5.9.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:77f7479a28b43aa658735e3ce79cfd1fccd5d42db035c21bb4c26e8bd7e280e5", size = 3803326, upload-time = "2026-06-01T15:43:10.054Z" }, + { url = "https://files.pythonhosted.org/packages/99/58/3f4e04ffe1ae62b437de07a96da672091cef62b619d0dc78207c1af442e6/pypdfium2-5.9.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07e6ba170d577eabf60dbba701d051c64318dd029d38ca5907d83ae1a66fe779", size = 4216890, upload-time = "2026-06-01T15:43:11.701Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f6/2dde4656750c4a6da99e1f070ca09d2b5a9d68186b42e711a1a3e5b1cb32/pypdfium2-5.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ce3a3dd23ec0adaa079d8be54565ba2aa2f6060e76a4989cd42dabc163d74ee", size = 3728830, upload-time = "2026-06-01T15:43:13.329Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ca/f2ff8b9200c7dfc5aee85126edc856eb93c7056085da2454a75ef1e4dbc4/pypdfium2-5.9.0-py3-none-manylinux_2_27_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae177938f5cf95a275db25a4f8553e2ebd954ecda2f9bc84848ba4b027ce438f", size = 4063322, upload-time = "2026-06-01T15:43:15.158Z" }, + { url = "https://files.pythonhosted.org/packages/64/88/0b587de03c873c28adc59f6ac959de4032d3f3bc946094523b14a192d9c3/pypdfium2-5.9.0-py3-none-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ffe49edde2ac86f28ca7e58f565255a442f38a7508fff31b79a55f508f25a31e", size = 4039738, upload-time = "2026-06-01T15:43:16.975Z" }, + { url = "https://files.pythonhosted.org/packages/83/4c/fa627f00a954e66465e929077cf43bd012595091fff82758d989486e7bdc/pypdfium2-5.9.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b7b760bc2957ecf73c274af6ed8b168a2dcb328ac0a0f7ed6123cd92f6e7c9c9", size = 4997259, upload-time = "2026-06-01T15:43:18.915Z" }, + { url = "https://files.pythonhosted.org/packages/32/f0/1736d80c5d12d931f74ca6b4213b006ee016ec33c6325fad870234cc240c/pypdfium2-5.9.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7cdc8e5d2f8d82add1e4f70a4fbe5f3b33c17f301ebde38c669fd7f78a7d032c", size = 4537061, upload-time = "2026-06-01T15:43:20.879Z" }, + { url = "https://files.pythonhosted.org/packages/01/00/aa8890dfd385b2e7365034231987029cff15cc7eb4f06e8380da5608738a/pypdfium2-5.9.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:38a058dbd4929acaf0ab9171179eb86c24d8c6655a6836006796105a9f200890", size = 5232786, upload-time = "2026-06-01T15:43:23.73Z" }, + { url = "https://files.pythonhosted.org/packages/65/12/8f45ea698781a0bed96ac4fbde440060790863273943461f0f160a993d52/pypdfium2-5.9.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:1894511a0e862e7ec5679f3a6dc43ac72c4ef92c7ca438357203913e8634a643", size = 5170121, upload-time = "2026-06-01T15:43:25.858Z" }, + { url = "https://files.pythonhosted.org/packages/25/bd/9bb6ba375796e1de1d6c1af8d8303dd1781190346871c81a94d4e09eddfd/pypdfium2-5.9.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:040f5513b808db705d4878f57e2bf0b9dc6e6a0ad8d765c36cf62febf3933b28", size = 4663540, upload-time = "2026-06-01T15:43:27.677Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/fd103bac197f22038bf70be1f7507ced7519f1214ea0dae137f37803ab8a/pypdfium2-5.9.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:f4991ae39bcea757552579bba4aebfaedb71c96dd35c2292f957b8ac9132f1ff", size = 5090619, upload-time = "2026-06-01T15:43:29.522Z" }, + { url = "https://files.pythonhosted.org/packages/22/89/9531fa1e6e004fe522cdca0cd945cd6a9d7338e7125e6b0734d632d31fa6/pypdfium2-5.9.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:25ff1a5abd08ff9e87f62e5dac114ea95647c257fbbdbe029be8db71a6d7650b", size = 5050806, upload-time = "2026-06-01T15:43:31.322Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d0/e53c68555ff128b2470e4a468762b320d9c6ae2c914decea3487d923982f/pypdfium2-5.9.0-py3-none-win32.whl", hash = "sha256:b0057dc8c2033584dc3e61afb5f23a135dab52b081695b435e27f9b7b074c605", size = 3670966, upload-time = "2026-06-01T15:43:32.991Z" }, + { url = "https://files.pythonhosted.org/packages/da/0c/22e5fc035ad1594b44f265bc0a59ae34d377bc2ea74a92793e7a674bf96d/pypdfium2-5.9.0-py3-none-win_amd64.whl", hash = "sha256:06508c33b9772cf3878e48364c6e14c70cefc18a3abd6983ac9f338da9305275", size = 3800959, upload-time = "2026-06-01T15:43:34.536Z" }, + { url = "https://files.pythonhosted.org/packages/11/e3/cf1711add7add22a17f7c7633cd795edc92f17ab7bdf1930493ae0f56680/pypdfium2-5.9.0-py3-none-win_arm64.whl", hash = "sha256:565ddfc98795fd2f6054b544ee9791d7b9032f9cf77a57891b6e501fafd0ef3f", size = 3585718, upload-time = "2026-06-01T15:43:36.521Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +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/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/12/38c1a0b1e64806780c9563e3fc9f6e472251839662587cfbe9bfaf2ae10a/python_discovery-1.4.0.tar.gz", hash = "sha256:eb8bc7daad3c226c147e45bb4e970a1feb1bf4048ee178e6db59e197b8010ce3", size = 68455, upload-time = "2026-05-28T01:15:37.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/8d/3d316429f65029532bb1e28ff77b797d86b5ac3915bb44ca4e19aa283d43/python_discovery-1.4.0-py3-none-any.whl", hash = "sha256:26ed78d703e234879a66244c7d4114563fb13ec5cd30a2d1357e5fb4850782da", size = 33217, upload-time = "2026-05-28T01:15:36.573Z" }, +] + +[[package]] +name = "pywin32" +version = "312" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/ff/32aa7d2ed0ab12b323aaa64f9b75e6ad4f8fd09f9ccfc28c79414d46838d/pywin32-312-cp312-cp312-win32.whl", hash = "sha256:dab4f65ac9c4e48400a2a0530c46c3c579cd5905ecd11b80692373915269208b", size = 6371877, upload-time = "2026-06-04T07:49:28.836Z" }, + { url = "https://files.pythonhosted.org/packages/03/d9/77040d3b43df3f3be32ea289433d660d2727f5ba327bc73be835127d9d60/pywin32-312-cp312-cp312-win_amd64.whl", hash = "sha256:b457f6d628a47e8a7346ce22acb7e1a46a4a78b52e1d17e1af56871bd19a93bc", size = 6914841, upload-time = "2026-06-04T07:49:31.85Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cc/7b1ec671775756020a0ee7f4feeaf3c568f0ab86bd3900088cf986937a92/pywin32-312-cp312-cp312-win_arm64.whl", hash = "sha256:6017c58e12f6809fbb0555b75df144c2922a9ffd18e4b9b5afa863b6c1a9d950", size = 6727901, upload-time = "2026-06-04T07:49:34.244Z" }, + { url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184, upload-time = "2026-06-04T07:49:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298, upload-time = "2026-06-04T07:49:38.876Z" }, + { url = "https://files.pythonhosted.org/packages/84/37/c1697194092b76de9ed47ca124323f02c57ffc8a45c06f88a3d5acaf01eb/pywin32-312-cp313-cp313-win_arm64.whl", hash = "sha256:59aba5d5940842075343a5ddc6b11f1cdf0d1567fe745290359dfbcc7c2eb831", size = 6727640, upload-time = "2026-06-04T07:49:41.083Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2b/1f3cded5822fd49c02f40544cbb5f58c7cfd6b1694869fd476cb6170ee97/pywin32-312-cp314-cp314-win32.whl", hash = "sha256:a77a90fbb6881238d2ca9c6fd797b25817f3768fe78d214a90137ff055a75f5b", size = 6468928, upload-time = "2026-06-04T07:49:43.188Z" }, + { url = "https://files.pythonhosted.org/packages/21/82/3bf86d2e2808902013132e1ce905a7da0da53790f3836c64bf44d55e24f3/pywin32-312-cp314-cp314-win_amd64.whl", hash = "sha256:a4dd3a848290ef724347b19f301045831d8e802fa4464f491b98b1e0a081432e", size = 7024157, upload-time = "2026-06-04T07:49:45.34Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0e/73f6d6800b4f27655abd9e9f6aaeaefcddb2b946e4674efa2bab184a7f7b/pywin32-312-cp314-cp314-win_arm64.whl", hash = "sha256:9fce94568364e0155e6dfb781ac5d95903be8baf28670632beab1b523f300daa", size = 6839598, upload-time = "2026-06-04T07:49:47.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/61/caa39686032d2ebdd04ff0ab5cbe163126c0066d98e00c9018646e42393b/pywin32-312-cp315-cp315-win32.whl", hash = "sha256:5c1fbe4a937a73ae9297384a3da38518cbc694c68ad8a809b2e19acd350f03ed", size = 6471159, upload-time = "2026-06-04T07:49:50.035Z" }, + { url = "https://files.pythonhosted.org/packages/0f/cd/7e1de64a4a6f69c04214169657ccab0d93a670ea50e35eb8f489d7378249/pywin32-312-cp315-cp315-win_amd64.whl", hash = "sha256:c2f03a0f73f804a13c2735b99392b0cd426bb4f2c4d0178e5ac966a0f21618d5", size = 7025293, upload-time = "2026-06-04T07:49:54.857Z" }, + { url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + +[[package]] +name = "regex" +version = "2026.5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, + { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, + { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, + { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, + { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, + { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, + { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, + { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, + { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, + { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, + { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, + { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, + { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, + { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, + { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, + { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, + { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, + { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, + { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, + { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, + { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, + { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, + { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, + { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, + { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, + { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, + { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, + { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, + { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, + { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "requirements-parser" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/96/fb6dbfebb524d5601d359a47c78fe7ba1eef90fc4096404aa60c9a906fbb/requirements_parser-0.13.0.tar.gz", hash = "sha256:0843119ca2cb2331de4eb31b10d70462e39ace698fd660a915c247d2301a4418", size = 22630, upload-time = "2025-05-21T13:42:05.464Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/60/50fbb6ffb35f733654466f1a90d162bcbea358adc3b0871339254fbc37b2/requirements_parser-0.13.0-py3-none-any.whl", hash = "sha256:2b3173faecf19ec5501971b7222d38f04cb45bb9d87d0ad629ca71e2e62ded14", size = 14782, upload-time = "2025-05-21T13:42:04.007Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/5f7ec371001337d8fa61701c186ff8b613ecac1651848c5950f4c4d5f2e9/ruff-0.15.16.tar.gz", hash = "sha256:d05e78d38c78caf020b03789e25106c93017db5a0cb6e2819885018c61343b78", size = 4714267, upload-time = "2026-06-04T16:33:09.974Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/42/53ef1c3953f157956db9bf7861e3bc50b9b887ce93300aa48cdba8336fe6/ruff-0.15.16-py3-none-linux_armv6l.whl", hash = "sha256:6ac3c0b3969cc6cf6b158c4e2f8f682acb58e7d700d8a44b65ecdc72d66ab0b2", size = 10709025, upload-time = "2026-06-04T16:32:51.935Z" }, + { url = "https://files.pythonhosted.org/packages/93/9a/a79159346f19134a956607754e57d8d128f7a4c00f4ad2f7514d224c172c/ruff-0.15.16-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:197c207ed75ffba54a0dec23db4aa939a27a3053073e085e0042433cbdc58e4a", size = 11063550, upload-time = "2026-06-04T16:32:42.24Z" }, + { url = "https://files.pythonhosted.org/packages/bc/72/3ce2ac000a5299ec238e01f51397b3b653c93b077d9b1bfe8715bb895f20/ruff-0.15.16-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3a39fec45ab316cc23e7558f23fea4a70403ddb5648ea9a4a3854a16973d0071", size = 10421345, upload-time = "2026-06-04T16:32:37.251Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c2/cc7fad3ec9169373f5b6a18f1917b91080feec40c3f9658334a1d28e2f03/ruff-0.15.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba93191d79003116b95128c9d306e045200fdbd0bccb782b110f3cd1d4abc5cf", size = 10757217, upload-time = "2026-06-04T16:32:54.722Z" }, + { url = "https://files.pythonhosted.org/packages/69/d2/3474009eaa0a65b31fa7152a2fad5e2f050c640ceb1e6b02ee6922e94c82/ruff-0.15.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6ee4b90520630120ef032aa5cc10db483852dff950e78b1d717e2993a61ac8d", size = 10507035, upload-time = "2026-06-04T16:33:05.343Z" }, + { url = "https://files.pythonhosted.org/packages/ca/81/b7ae6ccbd11f0c8dc3d5d67fc4be9b57ff57ca86ba56152021378e1277f2/ruff-0.15.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e4215bc938bc3c8215c1472c1aa437e310fee20cd427335fec9d7e609563628", size = 11255291, upload-time = "2026-06-04T16:32:49.49Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e1/46e526f1a7cc90857ce6ddf25fbb77eb6568651ac38d71b033af07076dd5/ruff-0.15.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c8d26be963b090f10e29abc8b3e74a2a321f6fa34e02424e30b5af89350ecbb", size = 12124922, upload-time = "2026-06-04T16:33:07.821Z" }, + { url = "https://files.pythonhosted.org/packages/1a/da/5c791b088b596b24d0deb967fa28ae02ad751a140c0b9ea81c5ab915d6c0/ruff-0.15.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f198cf4123602a2280ed46c307bcbafe41758d6fee5b456b6b6058ca1514b3b4", size = 11332186, upload-time = "2026-06-04T16:33:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/72/11/5da87abe20047c8962361473923ebb2f62b595250126aadfad8c20649c1e/ruff-0.15.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb27515fa6240fb586ae82b901a59e67d24acff86f2190b433dc542fe0435aeb", size = 11373541, upload-time = "2026-06-04T16:32:47.007Z" }, + { url = "https://files.pythonhosted.org/packages/fe/2a/8554754c23a854ae3fd6b507e36ad61ddb121e298c6d5d617dec94ed0f14/ruff-0.15.16-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a267c46ba1593fc26b8eecbea050b39d40c0b6bb7781ee11c90a02cd10032951", size = 11353014, upload-time = "2026-06-04T16:32:34.795Z" }, + { url = "https://files.pythonhosted.org/packages/62/25/62ea41529ec89f742ea3fed9cb1059c72877ec7cf9b9e99ac9cf3294d1d9/ruff-0.15.16-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:528c68f39a91498a8d50e91ff5985df3d105782bab49cc378e73ac26bff083e8", size = 10737467, upload-time = "2026-06-04T16:32:26.348Z" }, + { url = "https://files.pythonhosted.org/packages/90/17/334d3ad9de4d40f9dd58fdd09e35ce64553bb501e2f19a839e2fb6be14fc/ruff-0.15.16-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7ed55c58950df60589a9a7a5d2f8fa5f54ebd287163be805adfe6ee95a9de123", size = 10521910, upload-time = "2026-06-04T16:32:32.54Z" }, + { url = "https://files.pythonhosted.org/packages/4d/bd/3ac7c6ae77a885c1004b3dda2446ea401768d24f851c14b4ad4b24f6639c/ruff-0.15.16-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d482feaf51512b50f9790ceb417a56a61dd1e9d9bf967662b9ed27c01b34f53a", size = 10979190, upload-time = "2026-06-04T16:32:57.492Z" }, + { url = "https://files.pythonhosted.org/packages/33/d7/609546e6a413c3f216fbf2a50c928f97c80939154f6a0503114094a86191/ruff-0.15.16-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e15bc8c94513dae2a40cc9ef07c94fdd4ecc9e29dabebeebe170f952322c9e3", size = 11477014, upload-time = "2026-06-04T16:32:44.687Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/f2cd247ad32633a5c36e97141a2c21b11c6279f7957bc2ff360b1e08fddd/ruff-0.15.16-py3-none-win32.whl", hash = "sha256:580378f7bd4aa25f72e74aa54948a9622f142b1e509521dd10902e886681cc1e", size = 10735541, upload-time = "2026-06-04T16:32:30.145Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9e/02e845ef151b1dee585e55c4739f8e1734ae1d9f1221dff65761c162208b/ruff-0.15.16-py3-none-win_amd64.whl", hash = "sha256:408256017284eddf98fff77b29aa4fb30f586042d535b2d9befc6512f400aaec", size = 11843403, upload-time = "2026-06-04T16:32:39.76Z" }, + { url = "https://files.pythonhosted.org/packages/15/19/016553f86f207450aebebc2b2b5088d086b901cc8186c02ac4284db3bd88/ruff-0.15.16-py3-none-win_arm64.whl", hash = "sha256:8cd61783afb39638a7133ef0d2dfb1e91277593962f81b5a8423eb0b888a6121", size = 11134555, upload-time = "2026-06-04T16:33:00.136Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, +] + +[[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 = "sqlalchemy" +version = "2.0.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/da/6fbf010c8ebb347679d0d100b22fe9ba5e13fd04046c5df7280d2f0bf706/sqlalchemy-2.0.50.tar.gz", hash = "sha256:af5607d11ef90fd6a5c0549fe0045dce1663d427426bcfb506dcb5346a85a3b9", size = 9907424, upload-time = "2026-05-24T19:20:04.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/b0/a9d19b43f38f878b1278bca5b00b909f7540d41494396dd2561f9ad0956d/sqlalchemy-2.0.50-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae23d8b9d344d30d0a92f06d45825024a5790f1c1dd4cf452636a50d3e58cb", size = 2159807, upload-time = "2026-05-24T19:27:53.086Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/191dd58a248fd2cfd4780fa82c375c505e4ad98c8b522fa69ec492130d77/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47b71b933e7b4ebad407c8fdfd70d2c4f08b78b3238bb30eebdd6eb32ca51b89", size = 3343358, upload-time = "2026-05-24T20:09:29.279Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2b/514fce8a7df81cf5bad7ff7865de7ac0c5776a38cc043475c4703eb7fe8b/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:110fdac56ace278949f00de805edacbd6141e382d992f9ba28238b3a0827a600", size = 3357994, upload-time = "2026-05-24T20:17:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/35/a6/a0e283f5494f92b0d77e319ff77e437b1ffe4a051ba67c81d53234825475/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5e4ac70e9e757f6b3e87c0491ff034442ecd8dfd36d041a50564c322dafc0e", size = 3289399, upload-time = "2026-05-24T20:09:32.239Z" }, + { url = "https://files.pythonhosted.org/packages/b7/96/1b07325ba71752d6a028b77d07bed1483ad545f794e8b1dc89b3ba3b3c68/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:724f3dcbe53dd0151e3cb5e7ec4ba4c620bede579caacd16275dc35ce06e8615", size = 3321216, upload-time = "2026-05-24T20:17:15.581Z" }, + { url = "https://files.pythonhosted.org/packages/ed/8e/bad6ed253e8a99edfc99af02f7173ec48a1d3ed1b9b35a1b8bc1700900cc/sqlalchemy-2.0.50-cp312-cp312-win32.whl", hash = "sha256:1208050441471d003b7c8cb4054fb084f185cf35ac3f0ea270803865bca9939a", size = 2119194, upload-time = "2026-05-24T19:50:04.943Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2d/314a6690dda4b9cfc571eab1a63cf6fe6e1470aa3759ccda6aa016ee0f5a/sqlalchemy-2.0.50-cp312-cp312-win_amd64.whl", hash = "sha256:9d1af51558029a156a70986b7df88f042b3d158d7c8d8fb5072912d4b32d89c7", size = 2146186, upload-time = "2026-05-24T19:50:06.74Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c4/c42356b527296e9862f67990efce31ef78b4cf69cd3f80873a528a060320/sqlalchemy-2.0.50-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:06a9210bdc5f4298cff0781087e2ff45683922252dacc452846373a58761f093", size = 2156697, upload-time = "2026-05-24T19:27:54.764Z" }, + { url = "https://files.pythonhosted.org/packages/60/a1/b1a70e3c4365ac7fe9e347f3710f19b562c866fb96d45e3c891588789a7b/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b53784972ade4f8174b9aa661f31a06f8a936d2cfdd602913ff3c6dd40ae873", size = 3284260, upload-time = "2026-05-24T20:09:34.195Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4a/f3ac3caa19f263d57b0a47f8c91bbf56583dc2d3fc63acfbf644abb24fe0/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31648fa14460537e768a7303b078e4344d208e0d23e06867c1f376a227ed82db", size = 3302280, upload-time = "2026-05-24T20:17:17.825Z" }, + { url = "https://files.pythonhosted.org/packages/66/55/ccada3e3d62254587819749a0bc69f41173eb48a6e385d10e66d32a9c88e/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03f4323c980ad0e918cc9e5369b015f759f4e534db5bbaf4dc36832c10d05064", size = 3231580, upload-time = "2026-05-24T20:09:36.406Z" }, + { url = "https://files.pythonhosted.org/packages/05/f6/6809349130a2de0e109e7f00fd7d431da9565b9b2868b32ee684754f672b/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b9dcc43afef8ac157cd92fce96985d6b8b0cfbd3df4d666f66b4d55a75d202f", size = 3269375, upload-time = "2026-05-24T20:17:20.34Z" }, + { url = "https://files.pythonhosted.org/packages/48/84/278a811ef4e07be9c89dc5cdd7be833268509a66a68c4897cf585e67428f/sqlalchemy-2.0.50-cp313-cp313-win32.whl", hash = "sha256:60922d6599065ddca2c6f376b9aa2f41a6b85a271725e0909490bbc50b1998a5", size = 2117229, upload-time = "2026-05-24T19:50:08.215Z" }, + { url = "https://files.pythonhosted.org/packages/f6/1c/067cc6187ed32d2ec222fe6d2643acc1659a6d0659f8a7cbc5ad3ae83280/sqlalchemy-2.0.50-cp313-cp313-win_amd64.whl", hash = "sha256:287086e67275a212c4582d166a6fb03a65ccc5551d80866270ce0dd9f34eccd3", size = 2143126, upload-time = "2026-05-24T19:50:09.691Z" }, + { url = "https://files.pythonhosted.org/packages/df/32/10ac51b4be7cdecd7e93d069251c86dfbf70b7adbd7c67b48ccea6c49e1c/sqlalchemy-2.0.50-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c966932507a4d7d0a37314927dbfcd89720e3f37d2a1e3352e7ae7939fa8e8a0", size = 2158519, upload-time = "2026-05-24T19:27:56.472Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/e703d2f7681d7d66c4c891af3f07c7ccf4c76ad7f18351de035b5eda007a/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:faffef4bcc20a1892e65e155293d99d60855bbbc79250ab712819cfd56a8e6bb", size = 3282063, upload-time = "2026-05-24T20:09:38.57Z" }, + { url = "https://files.pythonhosted.org/packages/31/26/ef168b184a25701f9995e8fb7e503fafd7a99c1c77cda1bc1a26ea2ed486/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c206aec519a2e7bd08abbfb33436e325fd22c632d9c21a9047e376ce241646e", size = 3287069, upload-time = "2026-05-24T20:17:21.942Z" }, + { url = "https://files.pythonhosted.org/packages/c2/15/765acc2bc693bccc43ca4a95d5b69750da8aaf6db1b5c616536e087f8920/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bef4ac756363227ef6402a75fee025a4bc690f92328e825868939b3b3a446a6d", size = 3230453, upload-time = "2026-05-24T20:09:40.398Z" }, + { url = "https://files.pythonhosted.org/packages/63/61/08e03c3adbf5db0087a0b6816746fec8f3032fb2f7fc899a9bb9b2a48ce4/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96fbee6b19c19cd1556c8bf9419447cf2ec149ffcab7ab64348c23e54ef8547f", size = 3252413, upload-time = "2026-05-24T20:17:24.067Z" }, + { url = "https://files.pythonhosted.org/packages/03/0c/370a1f2db38436c615e10134c8a37de3688e74084792380695f3f5083860/sqlalchemy-2.0.50-cp314-cp314-win32.whl", hash = "sha256:8f00e3eb43ba30eb1b238ee03a8a62309486d1321eda3328bb611e0340033ad8", size = 2120063, upload-time = "2026-05-24T19:50:11.08Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a0/fe92bb9817863bc13ba093bda931979a26cc2ca69f8e8f26d07add3d7c6f/sqlalchemy-2.0.50-cp314-cp314-win_amd64.whl", hash = "sha256:15708c613cd5005b7dffe1f66ee6a63ee8f5e46799f71c70ebad74178c676a39", size = 2145830, upload-time = "2026-05-24T19:50:12.452Z" }, + { url = "https://files.pythonhosted.org/packages/cc/ff/e5640a98a0b2f491eb8fde10fb6c773621a2e44340de231fafcc9370f4a9/sqlalchemy-2.0.50-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3699dac4be410e97049a1658e9480da9cde956594aa0f3aebc60b88f21c5ba70", size = 2178435, upload-time = "2026-05-24T19:42:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/b7/85/337116e186f1236375b5fb70c21cfac98e8e8ab0d3a47be838dc47a59e08/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96233858e3df43932ac11589e22520da6e8aeb624b03fedfeebb0e8ea213086", size = 3566059, upload-time = "2026-05-24T20:01:20.848Z" }, + { url = "https://files.pythonhosted.org/packages/96/34/bb0e190e161c3c2c24314a65add57218be14a4a9486886b7f5047c1ff7c8/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4e70c46fad30c3bcc6a4708bc0130a3173e11a5b25f0ea4a9d8911b450f1f52", size = 3535366, upload-time = "2026-05-24T20:03:56.768Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/a7f759f97e4fd499c5d4e4488c760d5a7fbecf3028b465a04274fcd52384/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1918a3cf564d16d95bca7301005f41ab2ad50b07cd3b9da50d3ed986db148d6a", size = 3474879, upload-time = "2026-05-24T20:01:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d9/2907ea38eb60687d297bf9c39e5ee58053c87b57fe8a9cae97090cecbf10/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b00098cdbdbd38c7be3d568b0c9c3122b8c0ec62b911b57cd5e6e0254d60a76d", size = 3486117, upload-time = "2026-05-24T20:03:59.052Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e3/5aa06f167559f8c0bdae487e297d23ba548150ab016a3418265d617a4985/sqlalchemy-2.0.50-cp314-cp314t-win32.whl", hash = "sha256:1fbd55a969d7ac44a98e3dec75016074f809fa08f871585ace58dde110d1bf3e", size = 2150823, upload-time = "2026-05-24T20:08:58.644Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/112fb8f977582d7489d036e409e3723948bcf5320b3ac465f3c481bbe8f9/sqlalchemy-2.0.50-cp314-cp314t-win_amd64.whl", hash = "sha256:c5c3cdb753a9004183e1ccb634b41611654c989e61bc68617ce878e46d6f1e51", size = 2185794, upload-time = "2026-05-24T20:09:00.319Z" }, + { url = "https://files.pythonhosted.org/packages/d0/10/f7220e9b784d295d241c86ed99aeb537f92afcd469a64861f2717e9bb077/sqlalchemy-2.0.50-py3-none-any.whl", hash = "sha256:92064363517a3ff8212b5a93b8c62876579d8dfd1ca5b561335f30152d884fa9", size = 1943861, upload-time = "2026-05-24T19:59:01.119Z" }, +] + +[package.optional-dependencies] +asyncio = [ + { name = "greenlet" }, +] + +[[package]] +name = "tenacity" +version = "9.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, +] + +[[package]] +name = "tiktoken" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/e5/5f3cb2159769d0f4324c0e9e87f9de3c4b1cd45848a96b2eb3566ad5ca77/tiktoken-0.13.0.tar.gz", hash = "sha256:c9435714c3a84c2319499de9a300c0e604449dd0799ff246458b3bb6a7f433c1", size = 38986, upload-time = "2026-05-15T04:51:27.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/8e/144bde4e01df66b34bb865557c7cd754ed08b036217ebd79c9db5e9048a9/tiktoken-0.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:32ac870a806cfb260a02d0cb70426aef02e038297f8ad50df5040bb5af360791", size = 1034888, upload-time = "2026-05-15T04:50:31.579Z" }, + { url = "https://files.pythonhosted.org/packages/36/18/d4ac9d20956cdebca04841316660ed584c2fecdc2b81722a28bc7ad3b1e4/tiktoken-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d9980f11429ed2d737c463bb1fb78cf330caa026adf002f714aced7849a687b", size = 982970, upload-time = "2026-05-15T04:50:32.961Z" }, + { url = "https://files.pythonhosted.org/packages/74/ed/6bb8d05b9f731f749fee5c6f5ca63e981143c826a5985877330507bd13b7/tiktoken-0.13.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3f277ebea5edd7b8bf03c6f9431e1d67d517530115572b2dc1d465326e8f88c7", size = 1115741, upload-time = "2026-05-15T04:50:34.475Z" }, + { url = "https://files.pythonhosted.org/packages/34/de/2ca96b07a82d972b74fe4b46de055b79c904e45c7eab699354a0bfa697dc/tiktoken-0.13.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a116178fa7e1b4065bff05214360373a65cac22f965be7b3f73d00a0dbfe7649", size = 1136523, upload-time = "2026-05-15T04:50:35.782Z" }, + { url = "https://files.pythonhosted.org/packages/ee/dc/9dafec002c2d4424378563cf4cf5c7fb93631d2a55013c8b87554ee4012c/tiktoken-0.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c397ddda233208345b01bd30f2fca79ff730e55731d0108a603f9bc57f6af3b", size = 1181954, upload-time = "2026-05-15T04:50:36.99Z" }, + { url = "https://files.pythonhosted.org/packages/a1/d0/1f8578c45b2f24759b46f0b50d31878c63c73e6bf0f2227e10ec5c5408dc/tiktoken-0.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:95097e4f89b06403976e498abf61a0ee73a7497e73fb599cb211d8197a054d91", size = 1240069, upload-time = "2026-05-15T04:50:38.221Z" }, + { url = "https://files.pythonhosted.org/packages/aa/90/28d7f154888610aa9237e541986beb62b479df29d193a5a0617dbb1514d0/tiktoken-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8f2d16e7a7c783ad81f36e457d046d1f1c8af70b22aec8a13238efe531977c41", size = 874748, upload-time = "2026-05-15T04:50:39.587Z" }, + { url = "https://files.pythonhosted.org/packages/9c/83/b096c859c2a47c11731bf2f5885f4028b809dfe2396582883eed9cae372f/tiktoken-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5df5d1507bd245f1ccad4a074698240021239e455eb0bb4ced4e3d7181872154", size = 1034228, upload-time = "2026-05-15T04:50:40.988Z" }, + { url = "https://files.pythonhosted.org/packages/53/61/c68e123b6d753e3fc2751e9b18e732c9d8bf1e1926762e736eee935d931c/tiktoken-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fe806a50664e83a6ffd56cbd1e4f5dcc6cd32a3e7538f70dc38b1a271384545", size = 982978, upload-time = "2026-05-15T04:50:42.195Z" }, + { url = "https://files.pythonhosted.org/packages/ef/8b/96cc178cc584e65d363134500f297790b06cd48cdeb1e8fcf7bbe60f4715/tiktoken-0.13.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:125bc05005e747f993a83dc67934249932d6e4209854452cd4c0b1d53fba3ba2", size = 1116355, upload-time = "2026-05-15T04:50:43.564Z" }, + { url = "https://files.pythonhosted.org/packages/86/f5/bab735d2c72ea55404b295d02d092644eb5f7cc6205e34d35eb9abfb9ab2/tiktoken-0.13.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5e6358911cab4adee6712da27d65573496a4f68cf8a2b5fca6a4ad10fc5748cf", size = 1135772, upload-time = "2026-05-15T04:50:44.782Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b9/6de04ebdf904edfaad87788011b3735087a0c9ea671b9027e1e4e965e8c8/tiktoken-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:975cbd78d085d75d26b59660e262736dcaed1e35f8f142cd6291025c01d25486", size = 1182415, upload-time = "2026-05-15T04:50:46.422Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9c/470a05f3b1caf038f44880e334d47ab674e0c80d514c66b375d14d5afa10/tiktoken-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ab9bc99fa020a4c283424590ecd7f3afd70c1c281cb3fa3192a6c3af9f9615", size = 1239879, upload-time = "2026-05-15T04:50:48.052Z" }, + { url = "https://files.pythonhosted.org/packages/42/a6/c1936d16055436cb32e6c6128d68629622e00f4768562f55653752d34768/tiktoken-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:6b1615f0ff71953d19729ceb18865429c185b0a23c5353f1bbca34a394bf60f7", size = 874829, upload-time = "2026-05-15T04:50:49.202Z" }, + { url = "https://files.pythonhosted.org/packages/d6/07/acb5992c3772b5a36284f742cfb7a5895aa4471d1848ac31464ad50d7fdf/tiktoken-0.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6eb4a5bfbc6426938026b1a334e898ac53541360d62d8c689870160cc80abd67", size = 1033600, upload-time = "2026-05-15T04:50:50.4Z" }, + { url = "https://files.pythonhosted.org/packages/14/e9/742e9aec30f59b9f161f7ff7cd072e02ea836c9e1c0854a8076dfcd40d5c/tiktoken-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:43cee3e5400573b2046fbf092cc7a5bc30164f9e4c95ce20714da929df48737a", size = 982516, upload-time = "2026-05-15T04:50:52.03Z" }, + { url = "https://files.pythonhosted.org/packages/72/74/ca1541b053e7648254d2e4b42a253e1bb4359f2c91a0a8d49228c794e1a0/tiktoken-0.13.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7de52e3f566d19b3b11bd37eea552c6c305ad74081f736882bd44d148ed4c48d", size = 1115518, upload-time = "2026-05-15T04:50:53.543Z" }, + { url = "https://files.pythonhosted.org/packages/46/e3/93825eaf5a4a504795b787e5d5dea07fbeb3dabf97aa7b450be8bde59c89/tiktoken-0.13.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:51384448aa508e4df84c0f7c1dc3211c7f7b8096325660ee5fc82f3e11b381ce", size = 1136867, upload-time = "2026-05-15T04:50:55.191Z" }, + { url = "https://files.pythonhosted.org/packages/8c/46/002b68de6827091d5ae90b048f326e8aad8d953520950e5ce1508879414f/tiktoken-0.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e28157350f7ebf35008dd8e9e0fdb621f976e4230c881099c85e8cf07eaa50e2", size = 1181826, upload-time = "2026-05-15T04:50:56.296Z" }, + { url = "https://files.pythonhosted.org/packages/db/c6/d393e3185a276505182f7abd93fe714f3c444a2be9180798fa052347504e/tiktoken-0.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:165cf1820ea4a354985c2490a5205d4cc74661c934aca79dd0368232fff94e0f", size = 1239489, upload-time = "2026-05-15T04:50:57.918Z" }, + { url = "https://files.pythonhosted.org/packages/b7/4d/bc07d1f1635d4897a202acc0ae11c2886eaa7325c359ba4741b47bf8e225/tiktoken-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6c43a675ca14f6f2749ba7f12075d37456015a24b859f2517b9beb4ef30807ec", size = 873820, upload-time = "2026-05-15T04:50:59.528Z" }, + { url = "https://files.pythonhosted.org/packages/8c/93/0dd6adca026a616c3a92974566b43381eea4b475ce1f36c062b8271a9ac5/tiktoken-0.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaaaef47c2406277181d2086484c317bf7fc433e2d5d03ff94f56b0dcec87471", size = 1034977, upload-time = "2026-05-15T04:51:00.957Z" }, + { url = "https://files.pythonhosted.org/packages/d9/77/5ec6e6bc5b30bed6d93f7f2162d8f6b32437b3ba27cb527cfe004f6109c9/tiktoken-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ca8b310bd93b3772cb1b7922d915446864860f562bdfe4825c63a0aed3fb28cd", size = 983635, upload-time = "2026-05-15T04:51:02.629Z" }, + { url = "https://files.pythonhosted.org/packages/94/b0/c8ae9aff00d625c50659b4513e707a0462c4bf5d4d6cc1b802103225c02e/tiktoken-0.13.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:32e0c12305105002c047b3bb1070b0dd9a73b0cb3b2856a8972b810e7a4f5881", size = 1116036, upload-time = "2026-05-15T04:51:04.082Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ac/6a5dddd1d0a6018ecb389bd0353e6b4a515eb4d2286611bd0ace1937b9e1/tiktoken-0.13.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:5ba5fd62507a932d1241346179e3b39bc7bf7408f03c272652d93b3bedf5db24", size = 1135544, upload-time = "2026-05-15T04:51:05.229Z" }, + { url = "https://files.pythonhosted.org/packages/f4/b8/585032b4384b2f7dcdaddcb52865c83a701a420d09e3c2b4a2be1c450c57/tiktoken-0.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d108bc2d470fc53c8ecd24f2c0fd2b5f98c33e87cdb6aa2e9b8c5dced703d273", size = 1182217, upload-time = "2026-05-15T04:51:06.517Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b6/993ff1ded3958215fd341a847b8e5ffeb5de473f435296870d314fc91ac4/tiktoken-0.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cb99cb5127449f58d0a2d5f5ccfb390d8dbdfd919c221246caaee29d8725ed51", size = 1239404, upload-time = "2026-05-15T04:51:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3d/fef7e06e3b33e7538db0ced734cf9fe23b6832d2ac4990c119c377aec55e/tiktoken-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:115c4f26ffa11caac8b54eea35c2ad38c612c20a48d35dd15d70a02ac6f51f58", size = 918686, upload-time = "2026-05-15T04:51:08.925Z" }, + { url = "https://files.pythonhosted.org/packages/c1/82/a7fc44582bc32ab00de988a2299bf77c077f59068b233109e34b7d6ca7e6/tiktoken-0.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:472527e9132952f2fbf77cd290658bacf003d4d5a3fabc18e5fbd407cbae4d9b", size = 1034454, upload-time = "2026-05-15T04:51:10.035Z" }, + { url = "https://files.pythonhosted.org/packages/37/d0/24d8a890c14f432a05cea669c17bebeaa99f96a7c79523b590f564246411/tiktoken-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e2f67d27c9626cdd25fe33d9313c5cdb3d8d82da646b68d6eb8e7e9c20e6448", size = 982976, upload-time = "2026-05-15T04:51:11.23Z" }, + { url = "https://files.pythonhosted.org/packages/49/b7/2ab43f62788a9266187a9bfc1d3af99ad83e5eaa25fbef168a69cd5ad14f/tiktoken-0.13.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2b920b35805cd64585a37c3dc7ce65fba4d2d36016be01e1d7942482ca29093a", size = 1115526, upload-time = "2026-05-15T04:51:12.608Z" }, + { url = "https://files.pythonhosted.org/packages/64/39/1494321ed323ce7a14d88e3cd6cb9058625977df1c6961ddc492bd10a9f3/tiktoken-0.13.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:493af3aa28a4aaf2e3d2600a2ee717252c9bf5ab38fff94eb5a02db5ab77e5ad", size = 1136466, upload-time = "2026-05-15T04:51:13.926Z" }, + { url = "https://files.pythonhosted.org/packages/96/d9/dfd086aa2d918c563a140720e0ce296cada1634efd2783d5cf51e05f984e/tiktoken-0.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6644c9c2b5cf3916f5a3641d7d12fdb3f006a7b3d9ff6acdaec44e29ab1ff91e", size = 1181863, upload-time = "2026-05-15T04:51:15.025Z" }, + { url = "https://files.pythonhosted.org/packages/2f/68/a18b4f307086954fdae32714cb4f85562e34f9d34ab206e61f1816aa6018/tiktoken-0.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5cb65b60b9408563676d874a3a4ee573370066f0dc4e29d84e82e989c6517424", size = 1239218, upload-time = "2026-05-15T04:51:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/16/5b/f2aa703a4fc5d2dff73460a7d46cc2f3f44aa0f3dd8eeb20d2a0ecf68862/tiktoken-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:85b78cc3a2c3d48723ca751fa981f1fedccd54194ca0471b957364353a898b07", size = 918110, upload-time = "2026-05-15T04:51:17.237Z" }, +] + +[[package]] +name = "tinytag" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/59/8a8cb2331e2602b53e4dc06960f57d1387a2b18e7efd24e5f9cb60ea4925/tinytag-2.2.1.tar.gz", hash = "sha256:e6d06610ebe7cd66fd07be2d3b9495914ab32654a5e47657bb8cd44c2484523c", size = 38214, upload-time = "2026-03-15T18:48:01.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/34/d50e338631baaf65ec5396e70085e5de0b52b24b28db1ffbc1c6e82190dc/tinytag-2.2.1-py3-none-any.whl", hash = "sha256:ed8b1e6d25367937e3321e054f4974f9abfde1a3e0a538824c87da377130c2b6", size = 32927, upload-time = "2026-03-15T18:47:59.613Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tqdm" +version = "4.68.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/b3/36c8ecf72e8925200671613332db156d84b99b3aee742a41c1938ebb0808/tqdm-4.68.1.tar.gz", hash = "sha256:fc163d96b287bd031e1aa24421ce4411b25559bd0a1be4fe649bdaa4d2c02bf5", size = 171236, upload-time = "2026-06-05T17:23:15.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/aa/218a0eb34de1f753c83e4d0d1c8e7c4cef27f20dcb8342e024f63a80dc86/tqdm-4.68.1-py3-none-any.whl", hash = "sha256:fea4a90e4023f764914569f7802a297277c5ab1a66be5144143e142e1a4031d8", size = 78354, upload-time = "2026-06-05T17:23:13.654Z" }, +] + +[[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-inspect" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825, upload-time = "2023-05-24T20:25:47.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827, upload-time = "2023-05-24T20:25:45.287Z" }, +] + +[[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 = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0d/4e93c8e6d1001a75763f87d8f5ecda8ebc7f4aa2153dddfaf4ae8892821a/virtualenv-21.4.2.tar.gz", hash = "sha256:38e6ee0a555615c0ea9da2ac7e9998fe8dc3b911dd33ad8eaad2020957653b0c", size = 7613326, upload-time = "2026-05-31T17:01:22.827Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/c4/557dc082be035381b85fdb2b74e21d3d21b57750b74f2b47a32f3a639ff9/virtualenv-21.4.2-py3-none-any.whl", hash = "sha256:854210ca524a1a4d0d744734f4acbc721c3ffe163b85bbf5d56d14d5ae2f0fae", size = 7594079, upload-time = "2026-05-31T17:01:20.735Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, +] + +[[package]] +name = "wrapt" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/9f/06263fcd8ad6c405f05a3905fd7a84dd3176eb5ad46e44bccc0cd16348bb/wrapt-2.2.1.tar.gz", hash = "sha256:6744f504375775d7609c82c8d3d94af1c9a6f05586984536905908ba905277b9", size = 127620, upload-time = "2026-05-22T14:49:43.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/0c/bfae7b9401583b6d05938cd16dedc43857d96da2f8a3d50d78cc515bf6ff/wrapt-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ffad790d9d11d8ecf9f17c4bb671a5b4089e4d8b575c46c5129597f41f836b0", size = 81021, upload-time = "2026-05-22T14:48:00.313Z" }, + { url = "https://files.pythonhosted.org/packages/26/58/80f6a6599f933f4caecc1cb3ee88a04faf81e8b9bddbd6109c688dd63e0f/wrapt-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:628f5220c7a904d5fc78f7075c8d7871433eb6d035c94728a22fdf85f193d2a8", size = 81692, upload-time = "2026-05-22T14:48:01.49Z" }, + { url = "https://files.pythonhosted.org/packages/17/93/fb357cc7847c58a8ae790be718903afa81a28d23e642c843dc4129e8a0b2/wrapt-2.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:61acce4257a9883669703c525447c5b4c392edf0f987ae77ec32668440158f0e", size = 169364, upload-time = "2026-05-22T14:48:02.791Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0b/76b601ee309a8bd556af0eecb184394c20b3c49aa9c8e085aa1ffacc2568/wrapt-2.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727ab4244622cd6ad2390f322642090c877d2e83a608d2653a7643ae5368d926", size = 171079, upload-time = "2026-05-22T14:48:04.22Z" }, + { url = "https://files.pythonhosted.org/packages/cd/87/ee3f32d5658e3e26d3e0e457922b47a36dd3bfbdfee7f97bb3e802344a66/wrapt-2.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03df9ebed4c73ab93fa8c07e3d41d818dfca1852b15731a3de59457b27814624", size = 160205, upload-time = "2026-05-22T14:48:05.553Z" }, + { url = "https://files.pythonhosted.org/packages/b1/d0/ae2fd64277a67f5d7bffcf2d05eea1e476263fb2a072baf0b0129ab85984/wrapt-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0d9ff006f420b2ec8296aa56ade43ea7da3e997e85769f0aafc5e0661aacb710", size = 168922, upload-time = "2026-05-22T14:48:07.132Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f3/2d541a060c5bbafb9400bca4917e4d78bfd1f239f404782c86831a8f6b29/wrapt-2.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:844c858fc3bb7eacc0ba8efa904935d16aac6a4470948ad1e7e55c9f5a2a665f", size = 158388, upload-time = "2026-05-22T14:48:08.629Z" }, + { url = "https://files.pythonhosted.org/packages/1d/68/8d92c8800c57e93cb116ae9e9d6cbafc34fade5ee9f9107b6f203fb4dc35/wrapt-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87bacdaf225117a342a20d9c03438d701c02112f6e3f351ce9b7f32354f14797", size = 167682, upload-time = "2026-05-22T14:48:10.042Z" }, + { url = "https://files.pythonhosted.org/packages/30/72/83ea3790ea352439442349388e29ff07b76e0686265f9088bbb505d1608d/wrapt-2.2.1-cp312-cp312-win32.whl", hash = "sha256:2f8c90c8afde51969487be4e1343ae049b268854877d415c2510baf833775052", size = 77857, upload-time = "2026-05-22T14:48:11.782Z" }, + { url = "https://files.pythonhosted.org/packages/ef/cb/99450668dd3502d62a54a1c8aa56e44f34cb8c1261b381cfe2e7926c3b75/wrapt-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ce32763ac31ce94fe9aada947e479b1975012bff166da409b4b9e4e376cf7e5", size = 80825, upload-time = "2026-05-22T14:48:13.046Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3a/87512881be64e743f9ee4c66f4cbe8e884974bef2a5989af71f999653ac7/wrapt-2.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d1b4d0e0c2119587a31f5c029abd547e0c81d93b89d394566fe1588659eb579", size = 79087, upload-time = "2026-05-22T14:48:14.323Z" }, + { url = "https://files.pythonhosted.org/packages/88/d1/a1b08f8f4fac8cbb156fa51cf64ee2c7f7f74f9875ba3cf70b3c58368694/wrapt-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d2beb1c7cab10603aecdc42f8edd6ff013f9a32e4543474e38e6b77ce9975aeb", size = 80831, upload-time = "2026-05-22T14:48:15.598Z" }, + { url = "https://files.pythonhosted.org/packages/54/ce/57890814991446a845e09b3445ce8b694f27eb0577004f2c2a36a9772ed4/wrapt-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0cb7e4dd71f4c32e5e84843cd3c4cd65dda034314004bbe1d7f99af2426ab80", size = 81375, upload-time = "2026-05-22T14:48:17.071Z" }, + { url = "https://files.pythonhosted.org/packages/38/65/08d7a6c76ac4493bdb668205ee9c1de1bd5daca61717c3e9aa49b4c01499/wrapt-2.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95821352042722cd9f1108874579a47989d0a7e12a37d87d2fc4af20fd99ab8a", size = 167417, upload-time = "2026-05-22T14:48:18.303Z" }, + { url = "https://files.pythonhosted.org/packages/62/ce/f1ccbee7a1bfe5cdc6b3da6bab4b45713d628b9294da32a39f563d648140/wrapt-2.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abd621552ede77c4c69be7fac44ba911225b0c812b6ba604e5964cf98085b474", size = 166948, upload-time = "2026-05-22T14:48:19.768Z" }, + { url = "https://files.pythonhosted.org/packages/86/2a/f85d48d1cd4869aee6704028d257d740a47c1c467b457ce396b4b5b55d07/wrapt-2.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e3677c7146ce694874941ba82b57092cc4875445aadf29d72807351023105143", size = 158148, upload-time = "2026-05-22T14:48:21.96Z" }, + { url = "https://files.pythonhosted.org/packages/fe/5c/93939ad11d4a12358ab1aab219a2ef5efa5612e0db6b9fc65af8af1a891b/wrapt-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9a5934eaea872e17936b5f45501eba5ab0bce9a74122e172b663d7c28c459c4a", size = 165905, upload-time = "2026-05-22T14:48:23.373Z" }, + { url = "https://files.pythonhosted.org/packages/e0/22/b8c2aa89862ff58605934d7abf4b70e6a5a1c33df96656f49035ccdf1c8a/wrapt-2.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f5b9daf6b629fce418e0cc3dd0436eac045188fa35deadb7a7f3941d5b8203f9", size = 156712, upload-time = "2026-05-22T14:48:24.767Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/bf00a7b02239c12bb02ddcc3c0b971bfcc36e578c5a44f1ccfef5b458545/wrapt-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f53ac9f3ef573326d009ed809beff4efcac6451931c2b8132586da4b9e53ff31", size = 166560, upload-time = "2026-05-22T14:48:26.83Z" }, + { url = "https://files.pythonhosted.org/packages/fe/93/6390ca9c5b787683cef588d04f57c8d41b9a2323b5597a65f18638c90ef2/wrapt-2.2.1-cp313-cp313-win32.whl", hash = "sha256:1ffa9cfd4bdb581539951b14ae661ff20ed0c3599b3e911a131ee0ec5ac11337", size = 77817, upload-time = "2026-05-22T14:48:28.221Z" }, + { url = "https://files.pythonhosted.org/packages/97/73/ce10f0e71c0cfaa1a65faadb8efd4852028b3bb9ba28932b8889df769d38/wrapt-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:368eac1e20fd0bb03dd3cc42bf9887154c3861b60989389ccb5fac032617d215", size = 80736, upload-time = "2026-05-22T14:48:30.139Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4c/89f4a6818fafbbd840330e4fa3873073e1bfc166133a64cac7f8fde7a5e3/wrapt-2.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:c754dafdf5aaf0b401b644a90a30046929a0dd1a536e0ff0ec959a59155d9c7f", size = 79099, upload-time = "2026-05-22T14:48:31.405Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f2/9a8741c46f8c208ac0a45b25ba170bcb4fb72a2781d5fb97dbd7b6be73cb/wrapt-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ed928d0fda15fc0adc8d13305c8b3c0f2fba5b0669950c9e6d019d9162a3b3e8", size = 82802, upload-time = "2026-05-22T14:48:33.307Z" }, + { url = "https://files.pythonhosted.org/packages/9c/0d/e9c855716a3705eef1416456bdf062b60620726fdc59428ff670fc3c60dc/wrapt-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fafb4e739e43544d12cb4abd1605fd4683b6ca6a9ad682b7fd8f4d21973eafa8", size = 83329, upload-time = "2026-05-22T14:48:34.593Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d6/a88f1c13112b7831adac75cea65d8310e0d696d570c8961844c90a57b865/wrapt-2.2.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:74d6a0c31472fe5d814917266b9f46495d7c61ed890af08b468acea92fb89a8d", size = 202937, upload-time = "2026-05-22T14:48:35.859Z" }, + { url = "https://files.pythonhosted.org/packages/42/65/e29d54aef06a4d898a5b8a25589a0b3769bde454f922fad8f6f89fbfb650/wrapt-2.2.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab5be648d5a0b86b7438864f8df3c705a65cef35a2fd3e5561e3e203167e0f27", size = 209997, upload-time = "2026-05-22T14:48:38.153Z" }, + { url = "https://files.pythonhosted.org/packages/2a/91/e4454263516cf0e12640912fbca9a83654e424f0a6ddb79f5cd7ce14bf33/wrapt-2.2.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d8f204c8e3a8bf9ece17e0a83d137fd807440977f8a5e762d59306795011440", size = 194856, upload-time = "2026-05-22T14:48:39.69Z" }, + { url = "https://files.pythonhosted.org/packages/de/d0/fe0ee202286afdf4a7f77dd29f195703145764d572aec209c5086e57d924/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d047f6498c973874ba08ac3f97c69a2c4b2211c8de6f4c205f75cb1c9522596e", size = 205654, upload-time = "2026-05-22T14:48:43.456Z" }, + { url = "https://files.pythonhosted.org/packages/23/b6/87d860dfc6460c246af70b1fd5c8b76df77571b42a493459423ded94fd7d/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:7a4fdb9326aab4a5a477a1640e5ad786a8495901009d7e7b038371edd23a9d2b", size = 192206, upload-time = "2026-05-22T14:48:44.858Z" }, + { url = "https://files.pythonhosted.org/packages/df/46/3eea8cde077d985f239a38c0257087b8064fd9ee9b1a99e282d2c86da4ef/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c8cc5094b08abeae52da9c73c8a32003623be691a5193df2f4e3eac3d557c394", size = 198428, upload-time = "2026-05-22T14:48:46.319Z" }, + { url = "https://files.pythonhosted.org/packages/18/dc/b927ee9c7fc67adc3a5658f246a0d275425eb840ba36e7b702e70f18bde8/wrapt-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:9907a4402ab6db12b7077a0ea5d7a4d028ecb22c8eee2b53527080d347cd1562", size = 79448, upload-time = "2026-05-22T14:48:47.901Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b3/fd30b473fe498c70e6b9a5f328b8d3fbaf1b8c3c481465f59724bba8eb70/wrapt-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:5590d63f5243251641cf543009b4c9314a79d0598fdb8a8e4cfc918494536c53", size = 83021, upload-time = "2026-05-22T14:48:49.201Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f3/96c39153a8737a6e9aa85adef254ac4195bea3f2d24efc60472ccc3c9e2e/wrapt-2.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:c318a64b53d97b841d7b5e637517e50a27be64bc695128422953d4b21710954e", size = 80295, upload-time = "2026-05-22T14:48:50.479Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a3/11d7f34ebbf3231bc907a3e6d5ee051b14d034c1bc7b65a97d5cc00516df/wrapt-2.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f56a647e4eaf5f0ca40330fb070f566bdf9f7b0db89a1af20d71c28dcd7a0ab", size = 80879, upload-time = "2026-05-22T14:48:51.802Z" }, + { url = "https://files.pythonhosted.org/packages/13/3c/b74cfd984cef560b900fb1a727af20352d89e1f06bf2e1114dd3f00f5f5a/wrapt-2.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:64b7deeda4b70408e382328d8bbe52a256fe9bc63ae3db86d804608367e5422c", size = 81462, upload-time = "2026-05-22T14:48:53.18Z" }, + { url = "https://files.pythonhosted.org/packages/15/a3/7c8f704b8dc07dfe0a5d01c2edbfd88317aa8e5e3fa7c743eb7a085ae767/wrapt-2.2.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9cf53ba90717db2e292401de290776c498d4bbfb0d4a559ca2895db8b9dcb5c", size = 167251, upload-time = "2026-05-22T14:48:54.562Z" }, + { url = "https://files.pythonhosted.org/packages/80/85/a34d1888d97247da6c2ff6118c3a721c73ed8cc4dd198c00208bb73b6f80/wrapt-2.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf3638274ab9d9b724c9baa0b4c04e132cd6faefb78b4dd3dd1a02a4bdaad41e", size = 166316, upload-time = "2026-05-22T14:48:56.065Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d7/72ffaeb01eebc704afe3fb99e840480f4bda45f0fa66e3381b6a39251c8f/wrapt-2.2.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aed9658797d0b45d6c49adcfc6b41f66e6f2d0c6de3ec79e16cf4b1855df240f", size = 157952, upload-time = "2026-05-22T14:48:57.924Z" }, + { url = "https://files.pythonhosted.org/packages/24/5b/36f5d6b024e4edfdd90b140742d11ebcf7836daf5c9daf326c55c24db412/wrapt-2.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1d676ee388bc42a04d56dd7deb5605244dac2e35cc2fadbb43c9fa25bbd93508", size = 166130, upload-time = "2026-05-22T14:48:59.384Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/9296d9e97bfdef5483dfcc859d57b095b257144b2bc5300ab521e06f4bc7/wrapt-2.2.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e395f7bc31851ef9b612050368cb446e9bc14cd7454b025018980349caf25ae5", size = 156604, upload-time = "2026-05-22T14:49:00.921Z" }, + { url = "https://files.pythonhosted.org/packages/53/37/16953929ed6776175720e58fc966e779926d8d71e2c7b2273230590ca71f/wrapt-2.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f1845c2a8cc1180ccccfa45785dd06f562730d19ef75be180334254012b6283", size = 166007, upload-time = "2026-05-22T14:49:02.332Z" }, + { url = "https://files.pythonhosted.org/packages/b9/73/20ee58c0612dae7c31131a7095345812ed2c7b389019e175f68cde34e5b4/wrapt-2.2.1-cp314-cp314-win32.whl", hash = "sha256:436addbc4bb4fc0a88c702577f51195d7d73683a7f3e0e5b253d8404d7847243", size = 78327, upload-time = "2026-05-22T14:49:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/22/b3/ef7c3295d02e0448a71c639a36a057f46d524d057c9486291a7a3039e65c/wrapt-2.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:50972a1d974ea07725a7f6b1cec5f8759008afd030a0024843ebe7d52de47f2b", size = 81144, upload-time = "2026-05-22T14:49:05.093Z" }, + { url = "https://files.pythonhosted.org/packages/ac/dc/7bdf336953f99f4ceb0a584bb8870e42c8f26f93ea10c87834dad62f1668/wrapt-2.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:1c9934ea5d92957e3cd0adbc0845539dccfd62710ebe16195a8c66c53954db36", size = 79569, upload-time = "2026-05-22T14:49:06.413Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6d/6dfae80150ff1919c356d1dd528f049bcdfaae29b4d284bc957e022caef4/wrapt-2.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17de18fc12cea55b8a9587314cb830573e37fb33b247a7515696350863714188", size = 82892, upload-time = "2026-05-22T14:49:07.925Z" }, + { url = "https://files.pythonhosted.org/packages/82/7b/4e34766a7d7804ffce9e71befe47e9b3225dc350c49c94493c4ab39fd3a5/wrapt-2.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9dec1aca52dddde7df94818310fa2fe79739c8f385b2014c4cb1035f5508199", size = 83333, upload-time = "2026-05-22T14:49:09.257Z" }, + { url = "https://files.pythonhosted.org/packages/9d/57/0b34db3e8de44ccfece62d7b337abd1631dd810f5adc5f3db571727836b5/wrapt-2.2.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:69f2e9244542cb34dd59c7f073445b9e54ad9f3fce8d93606c368a1b499fc413", size = 202899, upload-time = "2026-05-22T14:49:10.572Z" }, + { url = "https://files.pythonhosted.org/packages/e5/45/ac0c459f154b99d92789a6cba7ca727185b83513b986f8ec7fe2aacddcbf/wrapt-2.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d83966dc7f4f45e8b97b5933685ac2e6e67fc0e19246ea314bceb9a8970c956", size = 209986, upload-time = "2026-05-22T14:49:12.229Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e4/77e37ff33ad018fa81ade52c25fa327b80b56f81d734279a63614fcb4cbc/wrapt-2.2.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78b0aa6bfb7be8deed0ab23e7aa028cc5210c29bc2d32a04d52b50e517a7307e", size = 194893, upload-time = "2026-05-22T14:49:14.139Z" }, + { url = "https://files.pythonhosted.org/packages/dd/9d/7ea651d1ab032fc5fa222fbec91d0f8a1397f6ae04ebb93fa7219aa921d7/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:05d5cb74d1b232ec8cfa130a8f900708699ff2491d97b8f85a4cdc5996294b85", size = 205636, upload-time = "2026-05-22T14:49:15.714Z" }, + { url = "https://files.pythonhosted.org/packages/09/af/8e88031a701275b9085c54e64bc88c0b1cd55c77eadd400691c371cd76c4/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f6518b94edb9150452e9aba08027d4cc293433753ec1fbefb4629a21cbc74181", size = 192267, upload-time = "2026-05-22T14:49:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/bf/a8/e657ca876b06710194f243d81c4b0896ade646e244bdbec2d87c8c56a8bd/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ed55af48b3eb28f43228ca2306788892bcb629eb2b5c4876e2a3659872c2f17a", size = 198378, upload-time = "2026-05-22T14:49:18.785Z" }, + { url = "https://files.pythonhosted.org/packages/c8/59/822efe4ea722a3961331bfa35b7d90937790d2c20f0616de1997ccc3aebd/wrapt-2.2.1-cp314-cp314t-win32.whl", hash = "sha256:2e08688ab16525897da6589d56d0aebaf417bbe91c2d8e3b96203b1efa596e85", size = 80226, upload-time = "2026-05-22T14:49:20.264Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/2a7dc5f6abb2fca0b6e1610e120419f603650aceb4f1d3ac4cae0354e162/wrapt-2.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:fd0135d34387f5fd087d9be368ea77ea89cf2451dc1cd1c622d35021bcb3ab50", size = 83835, upload-time = "2026-05-22T14:49:21.634Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c0/782b86e28d1ceebeb74cccea12d2cd3d2ba0bd68e3dec20b1bc5873f6127/wrapt-2.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:f70db64e8266d7c45d3b735f2e08eeb434b5e03da9a479ae42b2e2e486a21a00", size = 80722, upload-time = "2026-05-22T14:49:23.59Z" }, + { url = "https://files.pythonhosted.org/packages/53/46/29ac9daf11a86c22a8c38cd9236c62928ccae83f7ceb06bd3b0467cf9d05/wrapt-2.2.1-py3-none-any.whl", hash = "sha256:3aafea2975caef8ca49400640dde02cc7426e798f24870ed01f490bc3cffd32f", size = 61000, upload-time = "2026-05-22T14:49:41.593Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957, upload-time = "2026-05-19T21:28:51.695Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164, upload-time = "2026-05-19T21:28:53.242Z" }, + { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688, upload-time = "2026-05-19T21:28:54.865Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902, upload-time = "2026-05-19T21:28:56.963Z" }, + { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931, upload-time = "2026-05-19T21:28:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030, upload-time = "2026-05-19T21:29:00.148Z" }, + { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392, upload-time = "2026-05-19T21:29:02.155Z" }, + { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612, upload-time = "2026-05-19T21:29:04.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487, upload-time = "2026-05-19T21:29:06.491Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333, upload-time = "2026-05-19T21:29:08.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025, upload-time = "2026-05-19T21:29:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507, upload-time = "2026-05-19T21:29:12.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719, upload-time = "2026-05-19T21:29:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438, upload-time = "2026-05-19T21:29:16.485Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719, upload-time = "2026-05-19T21:29:18.367Z" }, + { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901, upload-time = "2026-05-19T21:29:20.014Z" }, + { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229, upload-time = "2026-05-19T21:29:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/fcf0ce677f17e5c471c06311dd25964be38a4c586993632910d2e75278bc/yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536", size = 128978, upload-time = "2026-05-19T21:29:23.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/58/8e63299bb71ed61a834121d9d3fe6c9fcf2a6a5d09754ff4f20f2d20baf5/yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607", size = 91733, upload-time = "2026-05-19T21:29:25.375Z" }, + { url = "https://files.pythonhosted.org/packages/c1/24/16748d5dab6daec8b0ed81ccec639a1cded0f18dcc62a4f696b4fe366c37/yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1", size = 91113, upload-time = "2026-05-19T21:29:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986", size = 103899, upload-time = "2026-05-19T21:29:28.842Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/ba1974b8533909636f7733fe86cf677e3619527c3c2fa913e0ea89c48757/yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488", size = 97862, upload-time = "2026-05-19T21:29:31.086Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/123ac993b5c2ba6f554a140305620cb8f150fa543711bbc49be3ec0a65a4/yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b", size = 111060, upload-time = "2026-05-19T21:29:32.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/37/c472d3af3509688392134a88a825276770a187f1daa4de3f6dc0a327a751/yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592", size = 110613, upload-time = "2026-05-19T21:29:34.379Z" }, + { url = "https://files.pythonhosted.org/packages/df/88/09c28dad91e662ccfaa1b78f1c57badde74fc9d0b23e74aef644750ecd73/yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617", size = 107012, upload-time = "2026-05-19T21:29:36.216Z" }, + { url = "https://files.pythonhosted.org/packages/07/ab/9d4f69d571a94f4d112fa7e2e007200f5a54d319f58c82ac7b7baa61f5c6/yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92", size = 105887, upload-time = "2026-05-19T21:29:38.746Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9a/000b2b66c0d772a499fc531d21dab92dfeb73b640a12eed6ba89f49bb2d0/yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a", size = 103620, upload-time = "2026-05-19T21:29:40.368Z" }, + { url = "https://files.pythonhosted.org/packages/41/7c/7c1050f73450fbdaa3f0c72017059f00ce5e13366692f3dba25275a1083d/yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44", size = 100599, upload-time = "2026-05-19T21:29:42.66Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b1/29e5756b3926705f5f6089bd5b9f50a56eaac550da6e260bf713ead44d04/yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a", size = 110604, upload-time = "2026-05-19T21:29:44.632Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4b/8415bc96e9b150cde942fbac9a8182985e58f40ce5c54c34ed015407d3ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf", size = 105161, upload-time = "2026-05-19T21:29:46.755Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d4/cde059abfa229553b7298a2eadde2752e723d50aeedaef86ce59da2718ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056", size = 110619, upload-time = "2026-05-19T21:29:48.972Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2c/d6a6c9a61549f7b6c7e6dc6937d195bcf069582b47b7200dcd0e7b256acf/yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992", size = 107362, upload-time = "2026-05-19T21:29:51Z" }, + { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667, upload-time = "2026-05-19T21:29:52.743Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069, upload-time = "2026-05-19T21:29:54.442Z" }, + { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670, upload-time = "2026-05-19T21:29:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916, upload-time = "2026-05-19T21:29:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625, upload-time = "2026-05-19T21:30:00.412Z" }, + { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574, upload-time = "2026-05-19T21:30:02.544Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534, upload-time = "2026-05-19T21:30:04.319Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481, upload-time = "2026-05-19T21:30:05.988Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529, upload-time = "2026-05-19T21:30:07.738Z" }, + { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338, upload-time = "2026-05-19T21:30:09.713Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147, upload-time = "2026-05-19T21:30:11.365Z" }, + { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272, upload-time = "2026-05-19T21:30:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962, upload-time = "2026-05-19T21:30:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063, upload-time = "2026-05-19T21:30:16.683Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438, upload-time = "2026-05-19T21:30:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458, upload-time = "2026-05-19T21:30:21.024Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589, upload-time = "2026-05-19T21:30:23.412Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424, upload-time = "2026-05-19T21:30:25.425Z" }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690, upload-time = "2026-05-19T21:30:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248, upload-time = "2026-05-19T21:30:29.297Z" }, + { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084, upload-time = "2026-05-19T21:30:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272, upload-time = "2026-05-19T21:30:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497, upload-time = "2026-05-19T21:30:34.842Z" }, + { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002, upload-time = "2026-05-19T21:30:37.724Z" }, + { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524, upload-time = "2026-05-19T21:30:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165, upload-time = "2026-05-19T21:30:41.888Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010, upload-time = "2026-05-19T21:30:43.985Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128, upload-time = "2026-05-19T21:30:46.291Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382, upload-time = "2026-05-19T21:30:48.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964, upload-time = "2026-05-19T21:30:49.785Z" }, + { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204, upload-time = "2026-05-19T21:30:51.862Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510, upload-time = "2026-05-19T21:30:53.62Z" }, + { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584, upload-time = "2026-05-19T21:30:55.962Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410, upload-time = "2026-05-19T21:30:57.962Z" }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980, upload-time = "2026-05-19T21:30:59.735Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219, upload-time = "2026-05-19T21:31:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, +] diff --git a/报销操作指南.md b/报销操作指南.md deleted file mode 100644 index d994566..0000000 --- a/报销操作指南.md +++ /dev/null @@ -1,302 +0,0 @@ -# 阜阳师范大学财务报销系统 - 自动化脚本操作指南 - -> 适用场景:日常报销录入(基于 `reimburse.py` 脚本) -> 最后更新:2026-05-23 - ---- - -## 一、系统概览 - -``` -整体流程: - -信息门户(SSO登录) → 财务系统入口 → 单点登录页 → 网络报销 → 日常报销录入 -(tyrz.fynu.edu.cn) (点击"财务系统") (新标签页) (a:has(img)) (/expen/common/common) - -目标系统: http://210.45.32.214:8081 -用户: 王建锋 (工号: 202407021) -``` - -### 关键 URL - -| 系统 | URL | 说明 | -|------|-----|------| -| SSO 登录 | `https://tyrz.fynu.edu.cn/sso/login` | 统一认证入口 | -| 信息门户 | `https://tyrz.fynu.edu.cn/oshall` | 登录后跳转目标 | -| 报销系统 | `http://210.45.32.214:8081` | 网络报销主系统 | -| 日常报销录入 | `/expen/common/common?v=4.0` | 目标录入页面 | - ---- - -## 二、数据准备 - -### 2.1 发票数据 CSV - -脚本从 `invoice_summary.csv`(GBK 编码)读取发票数据,CSV 需包含以下列: - -| 列名 | 说明 | 示例 | -|------|------|------| -| 序号 | 发票序号 | 1, 2, 3... | -| 发票号码 | 发票编号 | 26442000005432652421 | -| 开票日期 | 发票开具日期 | 2026/5/18 | -| 项目名称 | 采购项目名称 | 电阻一批 | -| 规格型号 | 规格型号 | — | -| 价税合计 | 发票金额 | 2900.00 | -| 销售方名称 | 商户/销售方 | 佛山市泓宇芯科技有限公司 | -| 人员姓名 | 报销人 | 王建锋(默认值) | -| 刷卡日期 | 公务卡消费日期 | 2026/5/18 → 自动转为 2026-05-18 | -| 公务卡号 | 公务卡卡号 | 6282880139161682(默认值) | -| 刷卡金额 | 实际刷卡金额 | 2900.00 | -| 备注 | 备注信息 | — | -| 工号 | 人员工号 | 202407021(默认值) | - -### 2.2 附件文件 - -脚本自动扫描当前工作目录下所有 `.pdf` 文件,按文件名排序后与发票一一对应上传。确保 PDF 文件名与发票顺序一致。 - ---- - -## 三、脚本执行流程 - -### 运行方式 - -```bash -python reimburse.py --data invoice_summary.csv -``` - -支持命令行覆盖配置: - -```bash -python reimburse.py \ - --data invoice_summary.csv \ - --username 202407021 \ - --password "your_password" \ - --user-data-dir browser_profile -``` - -### 执行步骤 - -脚本按以下顺序自动执行,截图保存在 `images/` 目录: - -``` -Step 0: 登录信息门户 - ├── 访问 SSO 登录页 - ├── 填写工号 + 密码 - ├── 勾选用户协议复选框 - ├── 点击"登录"按钮 - └── 等待跳转到信息门户 (zs-uip/oshall/portal) - -Step 1: 进入报销系统 - ├── 点击"财务系统"快捷入口 - ├── 等待新标签页打开 (含 dddl 或 210.45.32.214) - ├── 切换到新标签页 - ├── 关闭旧标签页 - ├── 通过 a:has(img[src*="wlbx"]) 定位"网络报销"链接 - ├── 提取链接 URL 并导航 - ├── 等待"报销录入"文本出现 - └── 导航到 /expen/common/common?v=4.0 - -Step 2: 创建新报销单 - ├── 等待 2 秒 - ├── 点击 button:has-text("新增") - └── 等待表单加载 3 秒 - -Step 3: 填写基本信息 - ├── 填写 #EXPENEXPLAIN → "元器件采购报销" - ├── 点击 #PROJECTCODE 打开项目选择弹窗 - ├── 在 #promodal .fixed-table-body tbody tr 中点击第一行 - └── 点击 #saveAndNext 进入下一步 - -Step 4: 录入报销明细(一条总明细) - ├── 计算所有发票的刷卡金额合计 - ├── 点击 #insertDetail 打开增加明细弹窗 - ├── 点击 #economicscode2 打开经济科目选择 - ├── 在 #econmodal 中选择第 3 行经济科目 - ├── 填写单据数 = 发票张数 - ├── 填写报销总金额 = 刷卡金额合计 - └── 点击 #detailAdd 确认 - -Step 5: 录入支付方式(逐张发票) - ├── 点击 "下一步(支付方式)" - ├── 对每张发票循环: - │ ├── 点击 #insertPay - │ ├── 填写 #personid2 (工号) - │ ├── 填写 #accountname2 (姓名) - │ ├── 填写 #receiptdate2 (刷卡日期) - │ ├── 填写 #localaccount2 (固定卡号: 6282880139161682) - │ ├── 填写 #receiptmoney2 (刷卡金额) - │ ├── 填写 #money2 (实报金额 = 刷卡金额) - │ ├── 填写 #merchant2 (销售方名称) - │ ├── 填写 #smark2 (备注) - │ └── 点击 #payAdd 确认 - └── 所有发票录入完成 - -Step 6: 上传附件(逐张发票) - ├── 点击 #next3 切换到附件清单页面 - ├── 对每张发票循环: - │ ├── 点击 #insertAcc 打开附件弹窗 - │ ├── select_option #fjlx → '1' (发票类型) - │ ├── 填写 #fpsmxx (项目名称 - 发票号码) - │ ├── set_input_files #file (对应 PDF 文件) - │ └── 点击 #cjtj 确认 - └── 所有附件上传完成 - -提交阶段: - └── 点击 #submit (当前已注释,需手动取消注释) -``` - ---- - -## 四、页面元素速查 - -### 基本信息页 (Step 3) - -| 字段 | 选择器 | 操作 | -|------|--------|------| -| 报销说明 | `#EXPENEXPLAIN` | fill | -| 项目代码 | `#PROJECTCODE` | click → 弹窗选择 | -| 项目弹窗 | `#promodal .fixed-table-body tbody tr` | 点击第一行 | -| 下一步按钮 | `#saveAndNext` | click | - -### 报销明细页 (Step 4) - -| 字段 | 选择器 | 操作 | -|------|--------|------| -| 增加按钮 | `#insertDetail` | click | -| 经济事项代码 | `#economicscode2` | click → 弹窗选择 | -| 经济科目弹窗 | `#econmodal .fixed-table-body tbody tr` | 点击第 3 行 | -| 单据数 | `input[name="expenPwCommondetail.HOWBILLS"]` | fill | -| 报销总金额 | `#je_zwzcdz` | fill | -| 确定按钮 | `#detailAdd` | click | - -### 支付方式页 (Step 5) - -| 字段 | 选择器 | 操作 | -|------|--------|------| -| 增加按钮 | `#insertPay` | click | -| 人员编号 | `#personid2` | fill | -| 人员姓名 | `#accountname2` | fill | -| 刷卡日期 | `#receiptdate2` | fill | -| 公务卡号 | `#localaccount2` | fill (固定值) | -| 刷卡金额 | `#receiptmoney2` | fill | -| 实报金额 | `#money2` | fill | -| 商户 | `#merchant2` | fill | -| 备注 | `#smark2` | fill | -| 确定按钮 | `#payAdd` | click | - -### 附件清单页 (Step 6) - -| 字段 | 选择器 | 操作 | -|------|--------|------| -| 增加按钮 | `#insertAcc` | click | -| 附件类型 | `#fjlx` | select_option → '1'(发票) | -| 附件说明 | `#fpsmxx` | fill | -| 文件上传 | `#file` | set_input_files | -| 确定按钮 | `#cjtj` | click | - -### 提交 - -| 操作 | 选择器 | -|------|--------| -| 提交按钮 | `#submit` | -| 提交按钮(备用) | `#submit2` | - ---- - -## 五、数据流向 - -``` -invoice_summary.csv (GBK) - │ - ▼ load_invoice_data() - │ - ├── 读取 CSV 行 - ├── 日期格式转换 (2026/5/18 → 2026-05-18) - ├── 填充默认值 (姓名/卡号/工号) - └── 输出: list[dict] - │ - ▼ add_reimburse_items() - ├── 计算: card_amount = sum(所有发票刷卡金额) - ├── 单据数 = len(invoices) - └── 录入 1 条总明细 - │ - ▼ fill_payment() - └── 对每张发票录入 1 条支付记录 - │ - ▼ upload_attachments() - ├── 扫描 *.pdf 文件 - └── 按索引匹配发票 → PDF,逐张上传 -``` - ---- - -## 六、关键设计说明 - -### 6.1 浏览器复用 - -脚本使用 `launch_persistent_context` 持久化浏览器上下文,登录状态保存在 `browser_profile/` 目录。再次运行时复用已有会话,无需重复登录。 - -### 6.2 明细录入策略 - -脚本采用"一条总明细"策略:将所有发票合并为一条报销明细,报销总金额为所有发票刷卡金额之和,单据数为发票总张数。支付方式则逐张发票分别录入,每张发票对应一条支付记录。 - -### 6.3 经济科目选择 - -脚本在经济科目弹窗中固定选择第 3 行。如需更改科目,修改 `rows[2]` 的索引即可。 - -### 6.4 项目选择 - -脚本在项目选择弹窗中固定选择第 1 行。如需更改项目,修改 `first_row` 的选择逻辑即可。 - -### 6.5 网络报销链接动态获取 - -单点登录页的"网络报销"链接参数每次不同,脚本通过 `a:has(img[src*="wlbx"])` 精确定位链接,动态提取 `href` 属性后导航,不硬编码 URL。 - -### 6.6 提交控制 - -脚本默认注释了 `bot.submit()` 调用。完成所有录入后停留在附件清单页面,需人工确认数据无误后,取消注释 `bot.submit()` 再运行,或手动点击提交按钮。 - ---- - -## 七、日志与调试 - -### 日志输出 - -- 控制台实时输出(DEBUG 级别) -- 文件日志:`reimburse.log`(UTF-8 编码) - -### 截图保存 - -每个关键步骤自动截图到 `images/` 目录: - -| 截图文件 | 对应步骤 | -|----------|----------| -| `debug_portal_loaded.png` | 登录成功 | -| `debug_step3_project_modal.png` | 项目弹窗打开 | -| `debug_step3_project_selected.png` | 项目选择完成 | -| `debug_step3_done.png` | 基本信息完成 | -| `debug_after_add_click.png` | 点击新增后 | -| `debug_item_total.png` | 总明细录入完成 | -| `debug_step5_done.png` | 支付方式完成 | -| `debug_step6_done.png` | 附件上传完成 | -| `debug_submitted.png` | 提交完成 | -| `debug_error.png` | 异常状态 | - -### 超时设置 - -- 页面默认超时:30 秒 -- 登录门户等待:最多 30 秒 -- 单点登录页等待:最多 15 秒 - ---- - -## 八、常见问题 - -| 问题 | 原因 | 解决方案 | -|------|------|----------| -| 登录超时 | SSO 需要手动验证码/微信扫码 | 手动完成验证后脚本继续 | -| 未找到财务系统入口 | 门户页面结构变化 | 检查 `images/debug_*` 截图定位 | -| 经济科目选择失败 | 弹窗加载延迟 | 检查超时设置,增加等待时间 | -| 附件上传失败 | PDF 文件不存在或路径错误 | 确认 PDF 在当前工作目录 | -| 金额不匹配 | 明细合计 ≠ 支付合计 | 检查 CSV 数据中刷卡金额 | -| 提交被拦截 | 必填项为空 | 检查 `reimburse.log` 定位失败步骤 | \ No newline at end of file