diff --git a/.agents/skills/course-learning-coach/SKILL.md b/.agents/skills/course-learning-coach/SKILL.md new file mode 100644 index 0000000..54904b6 --- /dev/null +++ b/.agents/skills/course-learning-coach/SKILL.md @@ -0,0 +1,116 @@ +--- +name: course-learning-coach +description: Guides daedalus course-learning for courses, tutorials, official learning paths, online curricula, chapter-based docs, and lesson labs. Use when the user learns a course such as Hugging Face Course, follows an official tutorial/notebook, asks what to study next in a curriculum, or says they copied course code but do not understand the underlying mechanism. +--- + +# Course Learning Coach + +Use this skill when the user is learning a course, tutorial, official learning path, online curriculum, or chapter-based technical material through daedalus. + +## Core Rule + +Course learning turns course material into an observable learning path: learning need, syllabus route, concept mechanism, lesson lab, user observation, practice transfer, review, and archive. + +It keeps daedalus's filesystem-first state, WIP discipline, human-owned notes, first principles, and critical lens. Its coaching center is course mastery: the user should be able to explain, reproduce, modify, and transfer the course concepts. + +The course-learning path is: + +```text +learning need -> syllabus route -> concept mechanism -> smallest lesson lab +-> user observation -> practice transfer -> capstone/review -> archive +``` + +## Startup + +1. Resolve active context from `workspaces/.daedalus/current.toml`, `workspaces/current-project`, or `workspaces/current-topic`. +2. Confirm the project state uses `task.kind = "course-learning"` and `project.source_kind = "course"`. +3. If the metadata does not match course-learning, pause course-learning routing and repair the concrete files in place with user approval. +4. Read project `shared/syllabus-map.md`, `shared/course-progress.md`, `shared/concept-map.md`, active topic `.daedalus/state.md`, `.daedalus/outcome-map.md`, and `.daedalus/todo.md`. +5. Route to exactly one current course-learning stage. + +## Stage Routing + +### 01 Need Aligner + +Load: + +- `system/prompts/course/01-need-aligner.md` + +Use when the learning goal, course role, output proof, or non-goals are unclear. + +### 02 Syllabus Mapper + +Load: + +- `system/prompts/course/02-syllabus-mapper.md` + +Use when choosing chapters, mapping a course outline, or deciding what to skip/delay. + +### 03 Concept Roadmap + +Load: + +- `system/prompts/course/03-concept-roadmap.md` + +Use when chapter titles need to become mechanism questions and checkpoints. + +### 04 Lesson Lab + +Load: + +- `system/prompts/course/04-lesson-lab.md` + +Use when the user follows course code, runs notebooks, inspects datasets/tokenizers/models/loss, or asks why the code is written that way. + +### 05 Mechanism Deep Dive + +Load: + +- `system/prompts/course/05-mechanism-deep-dive.md` + +Use only when lesson output, official docs, and small experiments are not enough to explain a hidden mechanism. + +### 06 Practice Transfer + +Load: + +- `system/prompts/course/06-practice-transfer.md` + +Use when transferring a course concept to the user's real task. + +### 07 Capstone Lab + +Load: + +- `system/prompts/course/07-capstone-lab.md` + +Use when building the final course-driven mini project. + +### 08 Review Loop + +Load: + +- `system/prompts/course/08-review-loop.md` + +Use when checking whether the user can explain, reproduce, modify, or transfer concepts without the tutorial. + +### 09 Closeout Archive + +Load: + +- `system/prompts/course/09-closeout-archive.md` + +Use when closing a course topic and mining verified knowledge. + +## Coaching Rules + +- Advance at most one lesson, mechanism, transfer checkpoint, or review checkpoint per turn. +- Ask 1-3 high-value questions when the user's intent or understanding is still fuzzy. +- Write Agent-generated action plans to `guides/`, not `notes/`. +- Write `notes/` only after user answers, runs code, observes output, or explains a mechanism. +- When a course-learning answer explains a reusable mechanism, API control panel, parameter trade-off, speed/cost lever, or "how to derive the code" frame, update the active focused `guides/` artifact in the same turn unless the user asks to only discuss; stage `README.md` files should only receive index/recovery updates. +- Before writing guides or notes, classify the artifact shape. Stage `README.md` files are indexes/timelines for restoring the learning scene. A reusable plan, concept, mechanism explanation, comparison, debugging pattern, user recap, or Agent failure mode must become a dedicated guide/note file and be linked from the stage README plus shared maps when relevant. +- For lesson labs, always separate prediction, minimum code, observed output, explanation, and next transfer. +- Treat warnings, shapes, losses, artifacts, and generated files as learning evidence, not terminal noise. +- Keep historical directories after manual reorganization unless the user approves renaming them. +- After changing lifecycle state, evidence, risks, or next action, synchronize `todo.md`, `outcome-map.md`, and relevant shared maps. diff --git a/.agents/skills/repo-learning-coach/SKILL.md b/.agents/skills/repo-learning-coach/SKILL.md index 7974da6..4f68438 100644 --- a/.agents/skills/repo-learning-coach/SKILL.md +++ b/.agents/skills/repo-learning-coach/SKILL.md @@ -123,6 +123,6 @@ For long-running work: - Tie every reading step to a future output artifact. - Do not let chat become the only learning record. If the user answers, corrects, or validates an important point, update the relevant `notes/` file in the same turn. - Preserve the distinction between user understanding, Agent calibration, and verified source-code conclusions. -- Do not accept a code-reading note that only explains call chains. It must explain production constraints, failure handling, invariants, trade-offs, and migration limits. +- Do not accept a code-reading note that only explains call chains. It must explain production constraints, failure handling, invariants, trade-offs, and transfer limits. - Do not mark the task complete until demo/business transfer/knowledge archival are addressed. - Do not archive unverified summaries as knowledge. diff --git a/.claude/skills/repo-learning-coach/SKILL.md b/.claude/skills/repo-learning-coach/SKILL.md index afdbc36..dc2ab77 100644 --- a/.claude/skills/repo-learning-coach/SKILL.md +++ b/.claude/skills/repo-learning-coach/SKILL.md @@ -20,6 +20,7 @@ Non-negotiables: - Keep stable projects under `workspaces/projects`; complete context lives in `workspaces/.daedalus/current.toml`, while top-level symlinks show only action entries: `current-project` / `current-topic` for active learning and `closeout-topic` for pending reflection. - Resolve active context from `workspaces/.daedalus/current.toml` or `workspaces/current-topic` before reading project/topic state. Resolve pending closeout from `pending_closeout_topic` or `workspaces/closeout-topic`. The repo root intentionally has no `.daedalus/state.toml`. - Keep at most one active topic and at most one pending closeout topic. When no active topic exists, do not treat a selected project as the current learning entry. +- Treat `workspaces/discovery` as pre-topic exploration space for unclear motivations, competing materials, and need hypotheses; do not create active lifecycle state from discovery alone. - Prefer workspace artifacts over chat memory. - Output drives input: every reading, debugging, review, or implementation step must advance a final artifact or a blocked decision. - Ground progress in code, tests, runtime evidence, and git diff before trusting markdown maps. @@ -80,6 +81,7 @@ Closeout progression must follow `system/prompts/common/closeout-flow.md`: once Load only the prompt(s) needed for the current action: - CLI lifecycle, render, validate, review, and deterministic knowledge checks: `system/prompts/repo/repo-learning-cli-contract.md` +- Topic selection discovery before task cards or gatekeeping: `system/prompts/common/topic-discovery.md` - Human-owned notes, notes review, topic closeout retrospective, and knowledge-base gate: `system/prompts/common/human-owned-notes.md` - Resume or "where are we": `system/prompts/common/resume.md` - Checkpoint, cursor sync, pre-commit sync, post-commit orientation: `system/prompts/common/checkpoint-lifecycle.md` diff --git a/.codex/plans/course-learning-system.md b/.codex/plans/course-learning-system.md new file mode 100644 index 0000000..a7385a4 --- /dev/null +++ b/.codex/plans/course-learning-system.md @@ -0,0 +1,35 @@ +--- +name: course-learning-system +overview: 追踪 daedalus course-learning 一等学习类型建设;详细方案以 docs/plan/15-course-learning-system.md 为准。 +status: completed +todos: + - id: design-course-learning-plan + content: 编写 course-learning 一等学习类型正式计划。 + status: completed + - id: implement-course-templates + content: 新增 course project/topic templates 与 state/source kind。 + status: completed + - id: implement-course-cli + content: 新增 init course-learning、validate/render 支持。 + status: completed + - id: add-course-prompts-skill + content: 新增 system/prompts/course 与 course-learning-coach skill。 + status: completed + - id: organize-hf-course-project + content: 手动整理 hugging-face-llm-course,使其成为 course-learning project。 + status: completed +isProject: false +--- + +# Codex Tracking Card + +详细计划不在此处展开。本文件只用于 Codex `status` / `todos` 追踪。 + +## Canonical Plan + +- [Course Learning 一等学习类型实现方案](../../docs/plan/15-course-learning-system.md) + +## Tracking Rule + +- `docs/plan/`:正式详细计划,作为 daedalus 项目的长期设计资料。 +- `.codex/plans/`:Codex 执行追踪卡,只保留 `status`、`todos` 和正式计划链接。 diff --git a/.codex/plans/discovery-driven-topic-selection.md b/.codex/plans/discovery-driven-topic-selection.md new file mode 100644 index 0000000..e82c534 --- /dev/null +++ b/.codex/plans/discovery-driven-topic-selection.md @@ -0,0 +1,37 @@ +--- +name: discovery-driven-topic-selection +overview: 追踪 daedalus 选题发现能力改进和 LoRA Fine-tuning Feedback Loop Lab 启动计划;详细方案以 docs/plan 为准。 +status: completed +todos: + - id: formalize-plan-location-rule + content: 确认正式详细计划放在 docs/plan,.codex/plans 只做带 status/todos 的 Codex 追踪卡并引用正式计划。 + status: completed + - id: write-daedalus-improvement-plan + content: 编写 daedalus 澄清、挖掘、选题能力改进正式计划。 + status: completed + - id: write-lora-topic-plan + content: 编写 LoRA Fine-tuning Feedback Loop Lab 新 topic 启动正式计划。 + status: completed + - id: implement-topic-discovery + content: 按 docs/plan/13 实现 topic discovery prompt、artifact 和 clarify/gatekeeper 集成。 + status: completed + - id: start-lora-topic + content: 按 docs/plan/14 使用 daedalus lifecycle 创建新 project/topic。 + status: completed +isProject: false +--- + +# Codex Tracking Card + +详细计划不在此处展开。本文件只用于 Codex `status` / `todos` 追踪。当前 daedalus 改进和新 topic 初始化均已完成。 + +## Canonical Plans + +- [澄清、挖掘、选题能力改进方案](../../docs/plan/13-discovery-driven-topic-selection.md) +- [LoRA Fine-tuning Feedback Loop Lab 启动计划](../../docs/plan/14-lora-finetuning-feedback-loop-topic.md) + +## Tracking Rule + +- `docs/plan/`:正式详细计划,作为 daedalus 项目的长期设计资料。 +- `.codex/plans/`:Codex 执行追踪卡,只保留 `status`、`todos` 和正式计划链接。 +- `workspaces/`:实际进入 daedalus lifecycle 后的 project/topic 状态。 diff --git a/.codex/plans/tui-learning-type-adapters.md b/.codex/plans/tui-learning-type-adapters.md new file mode 100644 index 0000000..bb51620 --- /dev/null +++ b/.codex/plans/tui-learning-type-adapters.md @@ -0,0 +1,35 @@ +--- +name: tui-learning-type-adapters +overview: 追踪 daedalus TUI 对 repo-learning / course-learning 的解耦适配;详细方案以 docs/plan/16-tui-learning-type-adapters.md 为准。 +status: completed +todos: + - id: write-tui-adapter-plan + content: 编写 TUI learning type adapter 正式计划。 + status: completed + - id: implement-learning-type-adapter + content: 在 TUI app 层引入 learning-type adapter,并接入 repo/course。 + status: completed + - id: update-tui-screens + content: 让 TUI screens 展示 learning type、source reference 和类型化进度文案。 + status: completed + - id: add-course-tui-tests + content: 补充 course-learning TUI fixture 和 guide 查找测试。 + status: completed + - id: write-changelog + content: 实现完成后写 changelog 总结。 + status: completed +isProject: false +--- + +# Codex Tracking Card + +详细计划不在此处展开。本文件只用于 Codex `status` / `todos` 追踪。 + +## Canonical Plan + +- [TUI Learning Type Adapter 实现方案](../../docs/plan/16-tui-learning-type-adapters.md) + +## Tracking Rule + +- `docs/plan/`:正式详细计划,作为 daedalus 项目的长期设计资料。 +- `.codex/plans/`:Codex 执行追踪卡,只保留 `status`、`todos` 和正式计划链接。 diff --git a/.gitignore b/.gitignore index 494c9ec..7d03a38 100644 --- a/.gitignore +++ b/.gitignore @@ -43,5 +43,3 @@ coverage/ tmp/ temp/ .cache/ -.daedalus-migration-backup/ -**/.daedalus-migration-backup/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4aaf67..1dcda65 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: pass_filenames: false - id: commit-msg-format name: commit msg format - description: Check commit message follows daedalus convention. + description: Check commit message follows topic/scope convention. entry: system/bin/check-commit-msg language: system stages: [commit-msg] diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..cb63449 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-python.python", + "ms-python.vscode-python-envs" + ] +} diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 0d4afdc..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,41 +0,0 @@ -# AGENTS.md - -daedalus is a filesystem-first deep learning coach. It guides a user from a real learning or business problem to source selection, deep reading, mini-demo implementation, business transfer, and verified knowledge archival. - -## Must Follow - -- Follow `system/prompts/common/agent-operating-contract.md`; the bullets below are the high-priority local contract. -- Use Chinese for project-facing learning artifacts unless the user asks otherwise. -- Prefer filesystem artifacts over hidden chat memory. Long-running learning state must be recoverable from files. -- Keep WIP strict: `workspaces/projects` may contain many stable projects, but at most one topic can be active through `workspaces/.daedalus/current.toml`. -- Resolve active learning context from `workspaces/.daedalus/current.toml` or `workspaces/current-topic` first. Resolve pending closeout context from `pending_closeout_topic` or `workspaces/closeout-topic`. Do not look for `.daedalus/state.toml` at the repository root. -- Treat project/topic `.daedalus/state.toml` and `workspaces/.daedalus/current.toml` as lifecycle sources of truth. `current-project` / `current-topic` are active-learning entry symlinks and should only appear when an active topic exists; `closeout-topic` is the pending reflection entry. -- Ground implementation progress in current code, tests, runtime output, and git diff before trusting learning maps. -- After review, validation, or commit changes completion status, risks, evidence, or next action, synchronize the relevant learning artifacts. -- Before repo-learning commits, run a learning-map sync check; after repo-learning commits, end with post-commit orientation. -- When a discussion reveals a reusable learning method, thinking tool, artifact pattern, or Agent failure mode, promote it to the right durable layer instead of burying it only in the current topic. -- Treat topic closeout as a push-driven state machine: when a gate is satisfied, synchronize maps and move to the next gate instead of waiting for the user to re-prompt. -- Mine knowledge-base candidates from closeout plus guides, notes, demo, and validation evidence; do not rely on closeout alone. -- Keep closeout focused on core goals, demo decisions, trade-offs, architecture, transfer boundaries, and important weak foundations; make knowledge extraction greedy. -- Knowledge-base entries are natural notes in an evolving knowledge tree, not fixed templates or type buckets. -- A knowledge-base entry must be deep enough to stand alone as a technical blog post or lesson; do not archive shallow summaries. -- Knowledge-web pages must be at least as deep as their Markdown sources: use light, readable UI; include mechanism details, diagrams, comparisons, interactions, and source links; never ship a shallow decorative summary. -- When summaries or knowledge archival fill technical details, verify them with external sources when possible; prefer official docs, papers, source repos, or established best-practice references. -- Treat every study material as a constrained design case, not an authority. Preserve first principles, trade-offs, critical lens, faithful imitation choices, and not-to-copy boundaries. -- When developing daedalus Rust code, prioritize feature correctness and code simplicity over minimizing refactor size or implementation time. -- Write daedalus implementation plans as target outcomes, not "first version" compromises, unless the user explicitly asks for phased delivery. -- Git commit messages must follow `type(scope): 中文描述` or `type: 中文描述`. -- Tests should assert stable behavior, not incidental wording. - -## Project Map - -- `crates/`: deterministic Rust workspace for CLI, TUI, MCP, and code-backed capabilities. -- `system/prompts/`: composable prompt protocols. -- `system/templates/`: generated workspace templates. -- `knowledge-base/`: verified reusable knowledge. -- `workspaces/`: stable projects, backlog, current symlinks, and workspace-level indexes. - -## Repo Learning - -- Use `.claude/skills/repo-learning-coach/SKILL.md` for repo learning, review, human-owned notes, and verified knowledge archival. -- Before marking a learning task completed, verify goal, core questions, run/debug evidence or justified skip, architecture/code notes, demo or explicit no-demo reason, business transfer, user closeout retrospective, and verified knowledge archival. diff --git a/AGENTS.md b/AGENTS.md new file mode 120000 index 0000000..681311e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index a657c44..70c47f3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# CLAUDE.md +# Agent Instructions daedalus is a filesystem-first deep learning coach. It guides a user from a real learning or business problem to source selection, deep reading, mini-demo implementation, business transfer, and verified knowledge archival. @@ -10,17 +10,24 @@ daedalus is a filesystem-first deep learning coach. It guides a user from a real - Keep WIP strict: `workspaces/projects` may contain many stable projects, but at most one topic can be active through `workspaces/.daedalus/current.toml`. - Resolve active learning context from `workspaces/.daedalus/current.toml` or `workspaces/current-topic` first. Resolve pending closeout context from `pending_closeout_topic` or `workspaces/closeout-topic`. Do not look for `.daedalus/state.toml` at the repository root. - Treat project/topic `.daedalus/state.toml` and `workspaces/.daedalus/current.toml` as lifecycle sources of truth. `current-project` / `current-topic` are active-learning entry symlinks and should only appear when an active topic exists; `closeout-topic` is the pending reflection entry. +- Treat `workspaces/discovery` as pre-topic exploration space: use it to preserve unclear motivations, need hypotheses, and selection questions before creating backlog candidates or active topics. - Ground implementation progress in current code, tests, runtime output, and git diff before trusting learning maps. - After review, validation, or commit changes completion status, risks, evidence, or next action, synchronize the relevant learning artifacts. - Before repo-learning commits, run a learning-map sync check; after repo-learning commits, end with post-commit orientation. - When a discussion reveals a reusable learning method, thinking tool, artifact pattern, or Agent failure mode, promote it to the right durable layer instead of burying it only in the current topic. +- For learning guides and notes, classify the artifact before writing: README files are indexes/timelines/recovery entrypoints; reusable plans, mechanisms, comparisons, failure modes, deep explanations, run evidence, and user retrospectives get their own focused file and are linked from the README and relevant maps. - Treat topic closeout as a push-driven state machine: when a gate is satisfied, synchronize maps and move to the next gate instead of waiting for the user to re-prompt. - Mine knowledge-base candidates from closeout plus guides, notes, demo, and validation evidence; do not rely on closeout alone. - Keep closeout focused on core goals, demo decisions, trade-offs, architecture, transfer boundaries, and important weak foundations; make knowledge extraction greedy. +- Knowledge-base entries are natural notes in an evolving knowledge tree, not fixed templates or type buckets. +- A knowledge-base entry must be deep enough to stand alone as a technical blog post or lesson; do not archive shallow summaries. +- Knowledge-web pages must be at least as deep as their Markdown sources: use light, readable UI; include mechanism details, diagrams, comparisons, interactions, and source links; never ship a shallow decorative summary. +- When summaries or knowledge archival fill technical details, verify them with external sources when possible; prefer official docs, papers, source repos, or established best-practice references. - Treat every study material as a constrained design case, not an authority. Preserve first principles, trade-offs, critical lens, faithful imitation choices, and not-to-copy boundaries. - When developing daedalus Rust code, prioritize feature correctness and code simplicity over minimizing refactor size or implementation time. - Write daedalus implementation plans as target outcomes, not "first version" compromises, unless the user explicitly asks for phased delivery. -- Git commit messages must follow `type(scope): 中文描述` or `type: 中文描述`. +- Put detailed daedalus implementation plans in `docs/plan/`; use `.codex/plans/` only as Codex tracking cards with `status` and `todos` that link to the canonical `docs/plan/` plan. +- Git commit messages must follow `{topic}/{scope}: 中文描述`, for example `lora-feedback-loop/debugger-guide: 跑通 Causal LM 训练闭环` or `daedalus/cli: 修正 topic 校验规则`. - Tests should assert stable behavior, not incidental wording. ## Project Map diff --git a/README.md b/README.md index 1c057fa..190f7e0 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ flowchart LR workspaces/ current-topic 当前正在学习的 topic 入口 closeout-topic 等待回顾总结的 topic 入口 + discovery/ 尚未立项前的选题探索 backlog/ 未来可能学习的候选项 projects/ / @@ -204,6 +205,7 @@ Topic: tools-permissions ```text workspaces/current-topic workspaces/closeout-topic +workspaces/discovery workspaces/backlog knowledge-base apps/knowledge-web diff --git a/crates/daedalus-cli/src/application/init_task.rs b/crates/daedalus-cli/src/application/init_task.rs index c9a740a..a4104bc 100644 --- a/crates/daedalus-cli/src/application/init_task.rs +++ b/crates/daedalus-cli/src/application/init_task.rs @@ -23,9 +23,30 @@ pub struct InitTaskOptions { pub reason: Option, } +/// 初始化 course learning 任务所需的参数。 +#[derive(Debug, Clone)] +pub struct InitCourseLearningOptions { + /// daedalus 项目根目录。 + pub repo_root: PathBuf, + /// 用户提供的学习任务名称。 + pub name: String, + /// 初始专题 slug。 + pub topic_slug: String, + /// 初始专题标题。 + pub topic_title: String, + /// 课程主页或课程材料入口。 + pub course_url: String, + /// 是否允许在已有 active task 时继续创建任务。 + pub allow_existing_active: bool, + /// 使用绕过选项时必须提供的具体原因。 + pub reason: Option, +} + /// 初始化学习任务后的输出。 #[derive(Debug, Clone)] pub struct InitTaskOutput { + /// 学习项目类型。 + pub project_kind: String, /// 新建学习任务目录。 pub task_dir: PathBuf, /// 新建初始专题目录。 @@ -36,11 +57,85 @@ pub struct InitTaskOutput { pub topic_state_md: PathBuf, } +#[derive(Debug, Clone)] +struct InitLearningProjectOptions { + repo_root: PathBuf, + name: String, + topic_slug: String, + topic_title: String, + allow_existing_active: bool, + reason: Option, + project_kind: LearningProjectKind, +} + +#[derive(Debug, Clone)] +enum LearningProjectKind { + RepoLearning, + CourseLearning { course_url: String }, +} + +impl LearningProjectKind { + fn label(&self) -> &'static str { + match self { + Self::RepoLearning => "repo-learning", + Self::CourseLearning { .. } => "course-learning", + } + } + + fn project_template(&self) -> &'static str { + match self { + Self::RepoLearning => "repo", + Self::CourseLearning { .. } => "course", + } + } + + fn topic_template(&self) -> &'static str { + match self { + Self::RepoLearning => "repo-topic", + Self::CourseLearning { .. } => "course-topic", + } + } + + fn course_url(&self) -> &str { + match self { + Self::RepoLearning => "", + Self::CourseLearning { course_url } => course_url, + } + } +} + /// 初始化一个 repo learning project 和初始 topic。 /// /// 该 use case 会复制 project 模板、创建初始 topic,并立即分别渲染 project/topic /// `state.md`。 pub fn init_repo_learning(options: InitTaskOptions) -> Result { + init_learning_project(InitLearningProjectOptions { + repo_root: options.repo_root, + name: options.name, + topic_slug: options.topic_slug, + topic_title: options.topic_title, + allow_existing_active: options.allow_existing_active, + reason: options.reason, + project_kind: LearningProjectKind::RepoLearning, + }) +} + +/// 初始化一个 course learning project 和初始 topic。 +pub fn init_course_learning(options: InitCourseLearningOptions) -> Result { + init_learning_project(InitLearningProjectOptions { + repo_root: options.repo_root, + name: options.name, + topic_slug: options.topic_slug, + topic_title: options.topic_title, + allow_existing_active: options.allow_existing_active, + reason: options.reason, + project_kind: LearningProjectKind::CourseLearning { + course_url: options.course_url, + }, + }) +} + +fn init_learning_project(options: InitLearningProjectOptions) -> Result { workspace_fs::ensure_stable_workspace_layout(&options.repo_root)?; let active = workspace_fs::active_projects(&options.repo_root)?; @@ -71,7 +166,7 @@ pub fn init_repo_learning(options: InitTaskOptions) -> Result { .repo_root .join("system") .join("templates") - .join("repo"); + .join(options.project_kind.project_template()); template_fs::copy_template_dir( &template_dir, &task_dir, @@ -80,6 +175,7 @@ pub fn init_repo_learning(options: InitTaskOptions) -> Result { ("{{CREATED_AT}}", &created_at), ("{{TOPIC_SLUG}}", &topic_slug), ("{{TOPIC_TITLE}}", &topic_title), + ("{{COURSE_URL}}", options.project_kind.course_url()), ], )?; let topic_dir = task_dir.join("topics").join(&topic_slug); @@ -88,7 +184,7 @@ pub fn init_repo_learning(options: InitTaskOptions) -> Result { .repo_root .join("system") .join("templates") - .join("repo-topic"); + .join(options.project_kind.topic_template()); template_fs::copy_template_dir( &topic_template_dir, &topic_dir, @@ -97,6 +193,7 @@ pub fn init_repo_learning(options: InitTaskOptions) -> Result { ("{{CREATED_AT}}", &created_at), ("{{TOPIC_SLUG}}", &topic_slug), ("{{TOPIC_TITLE}}", &topic_title), + ("{{COURSE_URL}}", options.project_kind.course_url()), ], )?; sync_project_navigation(&task_dir)?; @@ -106,6 +203,7 @@ pub fn init_repo_learning(options: InitTaskOptions) -> Result { sync_rust_analyzer_linked_projects(&options.repo_root)?; Ok(InitTaskOutput { + project_kind: options.project_kind.label().to_owned(), task_dir, topic_dir, state_md, diff --git a/crates/daedalus-cli/src/application/migrate.rs b/crates/daedalus-cli/src/application/migrate.rs deleted file mode 100644 index be5f18a..0000000 --- a/crates/daedalus-cli/src/application/migrate.rs +++ /dev/null @@ -1,317 +0,0 @@ -use std::fs; -use std::path::{Path, PathBuf}; - -use toml_edit::{DocumentMut, Item, Table, value}; - -use crate::application::ide::sync_rust_analyzer_linked_projects; -use crate::application::render::render_state; -use crate::domain::{DaedalusError, Result}; -use crate::infrastructure::{clock, state_toml, template_fs, workspace_fs}; - -/// repo learning workspace 迁移参数。 -#[derive(Debug, Clone)] -pub struct MigrateRepoLearningOptions { - pub repo_root: PathBuf, - pub task_dir: PathBuf, - pub topic_slug: String, - pub topic_title: String, - pub execute: bool, -} - -/// 迁移输出。 -#[derive(Debug, Clone)] -pub struct MigrateRepoLearningOutput { - pub project_dir: PathBuf, - pub topic_dir: PathBuf, - pub backup_dir: Option, - pub execute: bool, - pub state_md: Option, - pub topic_state_md: Option, - pub planned_actions: Vec, -} - -/// 将旧 single-topic workspace 一次性迁移为 project/topic 新结构。 -pub fn migrate_repo_learning( - options: MigrateRepoLearningOptions, -) -> Result { - let project_dir = options.task_dir; - let topic_slug = sanitize_slug(&options.topic_slug); - let topic_title = if options.topic_title.trim().is_empty() { - topic_slug.clone() - } else { - options.topic_title.trim().to_owned() - }; - let topic_dir = project_dir.join("topics").join(&topic_slug); - - let mut planned_actions = vec![ - format!("create topic dir {}", topic_dir.display()), - "move notes/ guides/ demo/ into topic".to_owned(), - "move topic-level .daedalus markdown files into topic".to_owned(), - "rewrite project .daedalus/state.toml".to_owned(), - "render project and topic state.md".to_owned(), - ]; - - validate_old_workspace(&project_dir)?; - if !options.execute { - planned_actions.push("dry-run only; pass --execute to apply".to_owned()); - return Ok(MigrateRepoLearningOutput { - project_dir, - topic_dir, - backup_dir: None, - execute: false, - state_md: None, - topic_state_md: None, - planned_actions, - }); - } - - let old_state_path = state_toml::state_path(&project_dir); - let old_doc = state_toml::load_state_doc(&old_state_path)?; - let task_name = state_toml::task_name(&old_doc); - let migrated_at = clock::now_local_timestamp(); - let task_created_at = old_doc - .get("task") - .and_then(|task| task.get("created_at")) - .and_then(Item::as_str) - .unwrap_or(&migrated_at) - .to_owned(); - let backup_dir = project_dir - .join(".daedalus-migration-backup") - .join(migrated_at.replace([':', ' '], "-")); - workspace_fs::ensure_dir(&backup_dir)?; - backup_if_exists(&project_dir, &backup_dir, ".daedalus")?; - backup_if_exists(&project_dir, &backup_dir, "notes")?; - backup_if_exists(&project_dir, &backup_dir, "guides")?; - backup_if_exists(&project_dir, &backup_dir, "demo")?; - let preserved_source_files = preserve_existing_files( - &project_dir, - &[ - "source/README.md", - "source/pull_source.sh", - "source/.gitignore", - ], - )?; - - workspace_fs::ensure_dir(&topic_dir)?; - move_dir_if_exists(&project_dir, &topic_dir, "notes")?; - move_dir_if_exists(&project_dir, &topic_dir, "guides")?; - move_dir_if_exists(&project_dir, &topic_dir, "demo")?; - workspace_fs::ensure_dir(&topic_dir.join(".daedalus"))?; - for file in [ - "task-card.md", - "outcome-map.md", - "todo.md", - "artifact-index.md", - "decision-log.md", - "validation-log.md", - "long-context.md", - ] { - move_file_if_exists( - &project_dir.join(".daedalus").join(file), - &topic_dir.join(".daedalus").join(file), - )?; - } - - let topic_doc = build_topic_doc(old_doc, &topic_slug, &topic_title); - state_toml::save_state_doc(&state_toml::state_path(&topic_dir), &topic_doc)?; - - let project_template = options - .repo_root - .join("system") - .join("templates") - .join("repo"); - template_fs::copy_template_dir( - &project_template, - &project_dir, - &[ - ("{{TASK_NAME}}", &task_name), - ("{{CREATED_AT}}", &task_created_at), - ("{{TOPIC_SLUG}}", &topic_slug), - ("{{TOPIC_TITLE}}", &topic_title), - ], - )?; - restore_preserved_files(&project_dir, preserved_source_files)?; - - let mut project_doc = state_toml::load_state_doc(&state_toml::state_path(&project_dir))?; - let topic_next_action = state_toml::next_action(&topic_doc); - state_toml::set_next_action( - &mut project_doc, - &format!("继续 active topic `{topic_slug}`:{topic_next_action}"), - ); - state_toml::append_transition( - &mut project_doc, - crate::domain::transition::Transition { - stage: "project".to_owned(), - action: "migrate".to_owned(), - timestamp: migrated_at, - actor: "daedalus-cli".to_owned(), - reason: format!( - "从 single-topic workspace 迁移为 multi-topic project,初始 topic 为 `{topic_slug}`。" - ), - approval_source: None, - }, - ); - state_toml::save_state_doc(&state_toml::state_path(&project_dir), &project_doc)?; - - let state_md = render_state(&project_dir)?.path; - let topic_state_md = render_state(&topic_dir)?.path; - workspace_fs::sync_current_workspace(&options.repo_root, Some(&project_dir), Some(&topic_dir))?; - sync_rust_analyzer_linked_projects(&options.repo_root)?; - Ok(MigrateRepoLearningOutput { - project_dir, - topic_dir, - backup_dir: Some(backup_dir), - execute: true, - state_md: Some(state_md), - topic_state_md: Some(topic_state_md), - planned_actions, - }) -} - -fn validate_old_workspace(project_dir: &Path) -> Result<()> { - let doc = state_toml::load_state_doc(&state_toml::state_path(project_dir))?; - if state_toml::state_kind(&doc) == "project" || project_dir.join("topics").exists() { - return Err(DaedalusError::InvalidStateDocumentKind { - expected: "legacy-task".to_owned(), - actual: "project-or-topic-layout".to_owned(), - }); - } - Ok(()) -} - -fn build_topic_doc(mut old_doc: DocumentMut, slug: &str, title: &str) -> DocumentMut { - let current_phase = state_toml::current_phase(&old_doc).unwrap_or_else(|| { - state_toml::stages(&old_doc) - .first() - .map(|stage| stage.id.clone()) - .unwrap_or_else(|| "01-goal-aligner".to_owned()) - }); - let next_action = state_toml::next_action(&old_doc); - old_doc.as_table_mut().remove("task"); - let mut topic = Table::new(); - topic["slug"] = value(slug); - topic["title"] = value(title); - topic["kind"] = value("repo-learning-topic"); - topic["parent_project"] = value("../.."); - topic["lifecycle"] = value("active"); - topic["current_phase"] = value(current_phase); - topic["next_action"] = value(next_action); - old_doc["topic"] = Item::Table(topic); - old_doc -} - -fn backup_if_exists(project_dir: &Path, backup_dir: &Path, relative: &str) -> Result<()> { - let source = project_dir.join(relative); - if source.exists() { - copy_dir(&source, &backup_dir.join(relative))?; - } - Ok(()) -} - -fn preserve_existing_files( - project_dir: &Path, - relatives: &[&str], -) -> Result> { - let mut preserved = Vec::new(); - for relative in relatives { - let path = project_dir.join(relative); - if path.exists() { - let content = fs::read_to_string(&path).map_err(|source| DaedalusError::Io { - path: path.clone(), - source, - })?; - preserved.push((PathBuf::from(relative), content)); - } - } - Ok(preserved) -} - -fn restore_preserved_files(project_dir: &Path, preserved: Vec<(PathBuf, String)>) -> Result<()> { - for (relative, content) in preserved { - let path = project_dir.join(relative); - if let Some(parent) = path.parent() { - workspace_fs::ensure_dir(parent)?; - } - fs::write(&path, content).map_err(|source| DaedalusError::Io { path, source })?; - } - Ok(()) -} - -fn copy_dir(source: &Path, target: &Path) -> Result<()> { - for entry in walkdir::WalkDir::new(source) { - let entry = entry.map_err(|source_err| DaedalusError::Io { - path: source.to_path_buf(), - source: std::io::Error::other(source_err), - })?; - let relative = entry.path().strip_prefix(source).unwrap_or(entry.path()); - let target_path = target.join(relative); - if entry.file_type().is_dir() { - workspace_fs::ensure_dir(&target_path)?; - } else { - if let Some(parent) = target_path.parent() { - workspace_fs::ensure_dir(parent)?; - } - fs::copy(entry.path(), &target_path).map_err(|source_err| DaedalusError::Io { - path: target_path, - source: source_err, - })?; - } - } - Ok(()) -} - -fn move_dir_if_exists(project_dir: &Path, topic_dir: &Path, name: &str) -> Result<()> { - let source = project_dir.join(name); - if source.exists() { - let target = topic_dir.join(name); - if target.exists() { - fs::remove_dir_all(&target).map_err(|source_err| DaedalusError::Io { - path: target.clone(), - source: source_err, - })?; - } - fs::rename(&source, &target).map_err(|source_err| DaedalusError::Io { - path: target, - source: source_err, - })?; - } - Ok(()) -} - -fn move_file_if_exists(source: &Path, target: &Path) -> Result<()> { - if !source.exists() { - return Ok(()); - } - if let Some(parent) = target.parent() { - workspace_fs::ensure_dir(parent)?; - } - if target.exists() { - fs::remove_file(target).map_err(|source_err| DaedalusError::Io { - path: target.to_path_buf(), - source: source_err, - })?; - } - fs::rename(source, target).map_err(|source_err| DaedalusError::Io { - path: target.to_path_buf(), - source: source_err, - }) -} - -fn sanitize_slug(value: &str) -> String { - let sanitized: String = value - .chars() - .map(|ch| { - if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' { - ch.to_ascii_lowercase() - } else { - '-' - } - }) - .collect(); - let sanitized = sanitized.trim_matches('-'); - if sanitized.is_empty() { - "topic".to_owned() - } else { - sanitized.to_owned() - } -} diff --git a/crates/daedalus-cli/src/application/mod.rs b/crates/daedalus-cli/src/application/mod.rs index 1681994..509723a 100644 --- a/crates/daedalus-cli/src/application/mod.rs +++ b/crates/daedalus-cli/src/application/mod.rs @@ -10,8 +10,6 @@ pub mod ide; pub mod init_task; /// 知识库结构、模板、索引和校验底座。 pub mod knowledge; -/// workspace 迁移 use cases。 -pub mod migrate; /// 同步和校验 project-level 学习导航。 pub mod project_navigation; /// 从 `state.toml` 渲染 Agent 友好的 `state.md`。 diff --git a/crates/daedalus-cli/src/application/render.rs b/crates/daedalus-cli/src/application/render.rs index a1bff77..543e2f4 100644 --- a/crates/daedalus-cli/src/application/render.rs +++ b/crates/daedalus-cli/src/application/render.rs @@ -51,6 +51,7 @@ fn render_project_state_markdown(doc: &DocumentMut, task_dir: &Path) -> Result 从 [`.daedalus/state.toml`](state.toml) 生成。不要手动编辑。\n\n"); output.push_str("## 当前状态\n\n"); output.push_str(&format!("- Project:`{}`\n", state_toml::task_name(doc))); + output.push_str(&format!("- 类型:`{}`\n", state_toml::task_kind(doc))); output.push_str(&format!( "- 生命周期:`{}`\n", state_toml::task_lifecycle(doc) @@ -71,6 +72,9 @@ fn render_project_state_markdown(doc: &DocumentMut, task_dir: &Path) -> Result Result = match state_toml::task_kind(doc).as_str() { + "course-learning" => vec![ + ".daedalus/project-map.md", + ".daedalus/topic-board.md", + "shared/syllabus-map.md", + "shared/course-progress.md", + "shared/concept-map.md", + "shared/evidence-registry.md", + ], + _ => vec![ + ".daedalus/project-map.md", + ".daedalus/topic-board.md", + "shared/evidence-registry.md", + "shared/source-index.md", + ], + }; + for path in project_files { let status = if task_dir.join(path).exists() { "present" } else { diff --git a/crates/daedalus-cli/src/application/validate_workspace.rs b/crates/daedalus-cli/src/application/validate_workspace.rs index d7bafdf..2e3b319 100644 --- a/crates/daedalus-cli/src/application/validate_workspace.rs +++ b/crates/daedalus-cli/src/application/validate_workspace.rs @@ -47,10 +47,9 @@ pub fn validate_workspace( ".daedalus/validation-log.md", "shared/README.md", "shared/evidence-registry.md", - "shared/source-index.md", "topics/.gitkeep", "source/.gitignore", - "source/pull_source.sh", + "source/README.md", ]; for path in required { @@ -77,6 +76,7 @@ pub fn validate_workspace( state_toml::state_kind(&doc) )); } + issues.extend(validate_project_learning_kind(task_dir, &doc)); match state_toml::task_lifecycle(&doc) { Ok(TaskLifecycle::Completed) => { @@ -105,6 +105,7 @@ pub fn validate_workspace( issues.push("state.md is stale or missing".to_owned()); } issues.extend(validate_project_navigation(task_dir, &doc)?); + let task_kind = state_toml::task_kind(&doc); let active_count = state_toml::active_topic_count(&doc); if active_count > 1 { @@ -140,7 +141,11 @@ pub fn validate_workspace( }; for topic in topic_targets { let topic_dir = task_dir.join(&topic.path); - issues.extend(validate_topic_workspace(&topic_dir, &topic.slug)?); + issues.extend(validate_topic_workspace_with_kind( + &topic_dir, + &topic.slug, + Some(task_kind.as_str()), + )?); } if reviews { @@ -181,6 +186,47 @@ pub fn validate_workspace( }) } +fn validate_project_learning_kind(task_dir: &Path, doc: &toml_edit::DocumentMut) -> Vec { + let mut issues = Vec::new(); + let task_kind = state_toml::task_kind(doc); + let source_kind = state_toml::project_source_kind(doc); + match task_kind.as_str() { + "repo-learning" => { + if source_kind != "repo" { + issues.push(format!( + "repo-learning project source_kind should be `repo`, got `{source_kind}`" + )); + } + for path in ["shared/source-index.md", "source/pull_source.sh"] { + if !task_dir.join(path).exists() { + issues.push(format!("missing repo-learning file: {path}")); + } + } + } + "course-learning" => { + if source_kind != "course" { + issues.push(format!( + "course-learning project source_kind should be `course`, got `{source_kind}`" + )); + } + if state_toml::course_url(doc).is_none() { + issues.push("course-learning project missing project.course_url".to_owned()); + } + for path in [ + "shared/syllabus-map.md", + "shared/course-progress.md", + "shared/concept-map.md", + ] { + if !task_dir.join(path).exists() { + issues.push(format!("missing course-learning file: {path}")); + } + } + } + other => issues.push(format!("unsupported task kind: `{other}`")), + } + issues +} + fn validate_current_projection( repo_root: &Path, task_dir: &Path, @@ -331,6 +377,14 @@ fn same_path(left: &Path, right: &Path) -> Result { /// 校验 topic workspace。 pub fn validate_topic_workspace(topic_dir: &Path, slug: &str) -> Result> { + validate_topic_workspace_with_kind(topic_dir, slug, None) +} + +fn validate_topic_workspace_with_kind( + topic_dir: &Path, + slug: &str, + project_kind: Option<&str>, +) -> Result> { let mut issues = Vec::new(); let required = [ ".daedalus/task-card.md", @@ -377,6 +431,11 @@ pub fn validate_topic_workspace(topic_dir: &Path, slug: &str) -> Result 1 { issues.push(format!("topic `{slug}` has multiple active stages")); } @@ -410,6 +469,68 @@ pub fn validate_topic_workspace(topic_dir: &Path, slug: &str) -> Result Result> { + let mut issues = Vec::new(); + let topic_kind = state_toml::topic_kind(doc); + if topic_kind != "course-learning-topic" { + issues.push(format!( + "course-learning topic `{slug}` kind should be `course-learning-topic`, got `{topic_kind}`" + )); + } + for path in [ + "guides/01-need-aligner", + "guides/02-syllabus-mapper", + "guides/03-concept-roadmap", + "guides/04-lesson-lab", + "guides/05-mechanism-deep-dive", + "guides/06-practice-transfer", + "guides/07-capstone-lab", + "guides/08-review-loop", + "guides/09-closeout-archive", + "review", + ] { + if !topic_dir.join(path).is_dir() { + issues.push(format!( + "course-learning topic `{slug}` missing directory: {path}" + )); + } + } + + if let Some(current_phase) = state_toml::current_phase(doc) { + let todo_path = topic_dir.join(".daedalus/todo.md"); + if todo_path.exists() { + let todo = fs::read_to_string(&todo_path).map_err(|source| DaedalusError::Io { + path: todo_path, + source, + })?; + if !todo.contains(¤t_phase) { + issues.push(format!( + "course-learning topic `{slug}` current_phase `{current_phase}` is not recoverable from .daedalus/todo.md" + )); + } + } + } + + for stage in state_toml::stages(doc) { + if matches!( + stage.id.as_str(), + "06-practice-transfer" | "07-capstone-lab" + ) && matches!(stage.status.as_str(), "active" | "done") + && !topic_dir.join("../../shared/concept-map.md").exists() + { + issues.push(format!( + "course-learning topic `{slug}` stage `{}` requires ../../shared/concept-map.md", + stage.id + )); + } + } + Ok(issues) +} + fn validate_reflection_loop(topic_dir: &Path, slug: &str) -> Vec { let mut issues = Vec::new(); for path in [ diff --git a/crates/daedalus-cli/src/infrastructure/state_toml.rs b/crates/daedalus-cli/src/infrastructure/state_toml.rs index bd1eb7d..8815d04 100644 --- a/crates/daedalus-cli/src/infrastructure/state_toml.rs +++ b/crates/daedalus-cli/src/infrastructure/state_toml.rs @@ -70,6 +70,33 @@ pub fn state_kind(doc: &DocumentMut) -> &'static str { } } +/// 读取 task kind,例如 `repo-learning` 或 `course-learning`。 +pub fn task_kind(doc: &DocumentMut) -> String { + doc.get("task") + .and_then(|task| task.get("kind")) + .and_then(Item::as_str) + .unwrap_or("unknown") + .to_owned() +} + +/// 读取 project source kind,例如 `repo` 或 `course`。 +pub fn project_source_kind(doc: &DocumentMut) -> String { + doc.get("project") + .and_then(|project| project.get("source_kind")) + .and_then(Item::as_str) + .unwrap_or("unknown") + .to_owned() +} + +/// 读取课程入口 URL。 +pub fn course_url(doc: &DocumentMut) -> Option { + doc.get("project") + .and_then(|project| project.get("course_url")) + .and_then(Item::as_str) + .filter(|value| !value.trim().is_empty()) + .map(ToOwned::to_owned) +} + /// 读取任务生命周期。 pub fn task_lifecycle(doc: &DocumentMut) -> Result { let value = doc["task"]["lifecycle"] @@ -309,6 +336,15 @@ pub fn topic_title(doc: &DocumentMut) -> String { .to_owned() } +/// 读取 topic kind,例如 `repo-learning-topic` 或 `course-learning-topic`。 +pub fn topic_kind(doc: &DocumentMut) -> String { + doc.get("topic") + .and_then(|topic| topic.get("kind")) + .and_then(Item::as_str) + .unwrap_or("unknown") + .to_owned() +} + /// 读取所有阶段快照。 pub fn stages(doc: &DocumentMut) -> Vec { doc.get("stages") diff --git a/crates/daedalus-cli/src/infrastructure/workspace_fs.rs b/crates/daedalus-cli/src/infrastructure/workspace_fs.rs index faa27d6..847c6c0 100644 --- a/crates/daedalus-cli/src/infrastructure/workspace_fs.rs +++ b/crates/daedalus-cli/src/infrastructure/workspace_fs.rs @@ -48,6 +48,11 @@ pub fn backlog_root(repo_root: &Path) -> PathBuf { workspaces_root(repo_root).join("backlog") } +/// 返回全局 topic discovery 根目录。 +pub fn discovery_root(repo_root: &Path) -> PathBuf { + workspaces_root(repo_root).join("discovery") +} + /// 返回 workspace 级 daedalus 状态目录。 pub fn workspace_state_root(repo_root: &Path) -> PathBuf { workspaces_root(repo_root).join(".daedalus") @@ -227,6 +232,7 @@ pub fn topic_dir_by_slug(project_dir: &Path, slug: &str) -> Result { pub fn ensure_stable_workspace_layout(repo_root: &Path) -> Result<()> { ensure_dir(&projects_root(repo_root))?; ensure_dir(&backlog_root(repo_root))?; + ensure_dir(&discovery_root(repo_root))?; ensure_dir(&workspace_state_root(repo_root))?; write_archive_ignore_files(repo_root)?; Ok(()) diff --git a/crates/daedalus-cli/src/interfaces/agent_cli/commands/init.rs b/crates/daedalus-cli/src/interfaces/agent_cli/commands/init.rs index de3224a..19b2f54 100644 --- a/crates/daedalus-cli/src/interfaces/agent_cli/commands/init.rs +++ b/crates/daedalus-cli/src/interfaces/agent_cli/commands/init.rs @@ -2,7 +2,9 @@ use clap::{Args, Subcommand}; use enum_dispatch::enum_dispatch; use tracing::debug; -use crate::application::init_task::{InitTaskOptions, init_repo_learning}; +use crate::application::init_task::{ + InitCourseLearningOptions, InitTaskOptions, init_course_learning, init_repo_learning, +}; use crate::domain::Result; use crate::interfaces::agent_cli::context::AgentCliContext; use crate::interfaces::agent_cli::executor::CmdExecutor; @@ -28,6 +30,8 @@ impl CmdExecutor for InitCommand { pub enum InitKind { /// 初始化 repo learning 任务。 RepoLearning(InitRepoLearningArgs), + /// 初始化 course learning 任务。 + CourseLearning(InitCourseLearningArgs), } /// `init repo-learning` 参数。 @@ -49,6 +53,28 @@ pub struct InitRepoLearningArgs { pub reason: Option, } +/// `init course-learning` 参数。 +#[derive(Debug, Args)] +pub struct InitCourseLearningArgs { + /// 学习任务名称。 + pub name: String, + /// 初始专题 slug。 + #[arg(long)] + pub topic: String, + /// 初始专题标题。 + #[arg(long)] + pub title: String, + /// 课程主页或课程材料入口。 + #[arg(long)] + pub course_url: String, + /// 是否允许已有 active task 时继续创建。 + #[arg(long)] + pub allow_existing_active: bool, + /// 使用绕过选项时的原因。 + #[arg(long)] + pub reason: Option, +} + impl CmdExecutor for InitRepoLearningArgs { async fn execute(self, ctx: AgentCliContext) -> Result<()> { debug!(task = %self.name, "初始化 repo learning 任务"); @@ -64,3 +90,20 @@ impl CmdExecutor for InitRepoLearningArgs { Ok(()) } } + +impl CmdExecutor for InitCourseLearningArgs { + async fn execute(self, ctx: AgentCliContext) -> Result<()> { + debug!(task = %self.name, course_url = %self.course_url, "初始化 course learning 任务"); + let output = init_course_learning(InitCourseLearningOptions { + repo_root: ctx.repo_root, + name: self.name, + topic_slug: self.topic, + topic_title: self.title, + course_url: self.course_url, + allow_existing_active: self.allow_existing_active, + reason: self.reason, + })?; + print_init(&output, ctx.format); + Ok(()) + } +} diff --git a/crates/daedalus-cli/src/interfaces/agent_cli/commands/migrate.rs b/crates/daedalus-cli/src/interfaces/agent_cli/commands/migrate.rs deleted file mode 100644 index 15d95f8..0000000 --- a/crates/daedalus-cli/src/interfaces/agent_cli/commands/migrate.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::path::PathBuf; - -use clap::{Args, Subcommand}; - -use crate::application::migrate::{MigrateRepoLearningOptions, migrate_repo_learning}; -use crate::domain::Result; -use crate::infrastructure::workspace_fs; -use crate::interfaces::agent_cli::context::AgentCliContext; -use crate::interfaces::agent_cli::executor::CmdExecutor; -use crate::interfaces::agent_cli::presenter::print_migration; - -/// 迁移命令。 -#[derive(Debug, Args)] -pub struct MigrateCommand { - #[command(subcommand)] - pub command: MigrateSubcommand, -} - -impl CmdExecutor for MigrateCommand { - async fn execute(self, ctx: AgentCliContext) -> Result<()> { - self.command.execute(ctx).await - } -} - -/// 迁移子命令。 -#[derive(Debug, Subcommand)] -pub enum MigrateSubcommand { - /// 将旧 repo-learning workspace 迁移为 multi-topic project。 - RepoLearningMultiTopic(MigrateRepoLearningArgs), -} - -impl CmdExecutor for MigrateSubcommand { - async fn execute(self, ctx: AgentCliContext) -> Result<()> { - match self { - Self::RepoLearningMultiTopic(args) => args.execute(ctx).await, - } - } -} - -/// repo-learning multi-topic 迁移参数。 -#[derive(Debug, Args)] -pub struct MigrateRepoLearningArgs { - /// 旧 workspace 目录;缺省时由当前目录或唯一 active project 推导。 - pub task_dir: Option, - /// 迁移后的初始 topic slug。 - #[arg(long)] - pub topic: String, - /// 迁移后的初始 topic title。 - #[arg(long)] - pub title: String, - /// 执行迁移;缺省只 dry-run。 - #[arg(long)] - pub execute: bool, -} - -impl CmdExecutor for MigrateRepoLearningArgs { - async fn execute(self, ctx: AgentCliContext) -> Result<()> { - let task_dir = workspace_fs::default_project_dir(self.task_dir)?; - let output = migrate_repo_learning(MigrateRepoLearningOptions { - repo_root: ctx.repo_root, - task_dir, - topic_slug: self.topic, - topic_title: self.title, - execute: self.execute, - })?; - print_migration(&output, ctx.format); - Ok(()) - } -} diff --git a/crates/daedalus-cli/src/interfaces/agent_cli/commands/mod.rs b/crates/daedalus-cli/src/interfaces/agent_cli/commands/mod.rs index 4b764ad..d46c9ad 100644 --- a/crates/daedalus-cli/src/interfaces/agent_cli/commands/mod.rs +++ b/crates/daedalus-cli/src/interfaces/agent_cli/commands/mod.rs @@ -3,7 +3,6 @@ pub mod ide; pub mod init; pub mod knowledge; -pub mod migrate; pub mod review; pub mod state; pub mod task; @@ -14,9 +13,8 @@ use clap::Subcommand; use enum_dispatch::enum_dispatch; pub use ide::{IdeCommand, IdeSubcommand, IdeSyncRustAnalyzerArgs}; -pub use init::{InitCommand, InitKind, InitRepoLearningArgs}; +pub use init::{InitCommand, InitCourseLearningArgs, InitKind, InitRepoLearningArgs}; pub use knowledge::{KnowledgeCommand, KnowledgeSubcommand, KnowledgeTemplateArgs}; -pub use migrate::{MigrateCommand, MigrateRepoLearningArgs, MigrateSubcommand}; pub use review::{ ReviewCloseArgs, ReviewCommand, ReviewListArgs, ReviewModeArg, ReviewSessionCommand, ReviewSessionCompleteArgs, ReviewSessionStartArgs, ReviewSessionSubcommand, ReviewShowArgs, @@ -41,8 +39,6 @@ pub enum Command { Init(InitCommand), /// 同步 IDE 派生配置。 Ide(IdeCommand), - /// 迁移学习 workspace。 - Migrate(MigrateCommand), /// 管理复习计划。 Review(ReviewCommand), /// 管理知识库模板、索引和校验。 diff --git a/crates/daedalus-cli/src/interfaces/agent_cli/presenter.rs b/crates/daedalus-cli/src/interfaces/agent_cli/presenter.rs index bb32218..57f4537 100644 --- a/crates/daedalus-cli/src/interfaces/agent_cli/presenter.rs +++ b/crates/daedalus-cli/src/interfaces/agent_cli/presenter.rs @@ -6,7 +6,6 @@ use crate::application::init_task::InitTaskOutput; use crate::application::knowledge::{ KnowledgeListOutput, KnowledgeOutput, KnowledgeValidationOutput, }; -use crate::application::migrate::MigrateRepoLearningOutput; use crate::application::render::RenderedState; use crate::application::review::{ ReviewListOutput, ReviewOutput, ReviewSessionOutput, ReviewValidationOutput, @@ -21,7 +20,7 @@ use crate::interfaces::agent_cli::args::OutputFormat; pub fn print_init(output: &InitTaskOutput, format: OutputFormat) { match format { OutputFormat::Text => { - println!("ok: initialized repo learning project"); + println!("ok: initialized {} project", output.project_kind); println!("project_dir: {}", output.task_dir.display()); println!("topic_dir: {}", output.topic_dir.display()); println!("state_md: {}", output.state_md.display()); @@ -33,6 +32,7 @@ pub fn print_init(output: &InitTaskOutput, format: OutputFormat) { json!({ "ok": true, "action": "init", + "project_kind": output.project_kind, "project_dir": output.task_dir, "topic_dir": output.topic_dir, "state_md": output.state_md, @@ -443,47 +443,6 @@ pub fn print_knowledge_validation(output: &KnowledgeValidationOutput, format: Ou } } -/// 输出迁移结果。 -pub fn print_migration(output: &MigrateRepoLearningOutput, format: OutputFormat) { - match format { - OutputFormat::Text => { - if output.execute { - println!("ok: migration completed"); - } else { - println!("ok: migration dry-run"); - } - println!("project_dir: {}", output.project_dir.display()); - println!("topic_dir: {}", output.topic_dir.display()); - if let Some(backup_dir) = &output.backup_dir { - println!("backup_dir: {}", backup_dir.display()); - } - if let Some(state_md) = &output.state_md { - println!("state_md: {}", state_md.display()); - } - if let Some(topic_state_md) = &output.topic_state_md { - println!("topic_state_md: {}", topic_state_md.display()); - } - for action in &output.planned_actions { - println!("action: {action}"); - } - } - OutputFormat::Json => println!( - "{}", - json!({ - "ok": true, - "action": "migrate-repo-learning-multi-topic", - "execute": output.execute, - "project_dir": &output.project_dir, - "topic_dir": &output.topic_dir, - "backup_dir": &output.backup_dir, - "state_md": &output.state_md, - "topic_state_md": &output.topic_state_md, - "planned_actions": &output.planned_actions - }) - ), - } -} - /// 输出稳定、可行动的错误信息。 /// /// Agent 会依赖这些错误文本或 JSON 字段来决定下一步,因此措辞应保持稳定。 diff --git a/crates/daedalus-cli/src/interfaces/tui/app.rs b/crates/daedalus-cli/src/interfaces/tui/app.rs index 800c606..bd38979 100644 --- a/crates/daedalus-cli/src/interfaces/tui/app.rs +++ b/crates/daedalus-cli/src/interfaces/tui/app.rs @@ -4,6 +4,11 @@ use std::path::{Path, PathBuf}; use crate::domain::{DaedalusError, Result}; use crate::infrastructure::{state_toml, workspace_fs}; +use super::learning_types::{ + build_next_action, find_current_guide, learning_source_reference, learning_type_adapter, + reflection_summary, relative_display, +}; + const WORKSPACE_BUCKETS: [&str; 1] = ["projects"]; /// TUI 当前页面。 @@ -335,6 +340,10 @@ pub struct TuiTaskSummary { pub bucket: String, /// 生命周期。 pub lifecycle: String, + /// 学习类型。 + pub learning_kind: String, + /// 面向 TUI 的学习类型标签。 + pub learning_label: String, /// Active topic。 pub active_topic: String, /// 当前 topic 阶段。 @@ -362,6 +371,22 @@ pub struct TuiOverview { pub topic_dir: Option, /// 任务生命周期状态。 pub lifecycle: String, + /// 学习类型。 + pub learning_kind: String, + /// 面向 TUI 的学习类型标签。 + pub learning_label: String, + /// 学习材料类型。 + pub source_kind: String, + /// 学习材料入口。 + pub source_reference: Option, + /// 当前阶段/课程阶段的显示标签。 + pub current_unit_label: String, + /// 进度单位标签。 + pub progress_unit_label: String, + /// 当前阅读入口标签。 + pub current_reading_label: String, + /// closeout / reflection 摘要标签。 + pub closeout_label: String, /// workspace bucket。 pub workspace_bucket: String, /// 当前阶段 ID。 @@ -392,7 +417,7 @@ pub struct TuiOverview { pub review_summary: Vec, /// Knowledge 摘要。 pub knowledge_summary: Vec, - /// 10-reflection 循环摘要。 + /// closeout / reflection 循环摘要。 pub reflection_summary: Vec, /// 任务关闭信息摘要。 pub closure_summary: Vec, @@ -413,7 +438,7 @@ impl TuiOverview { self.evidence_lines(), )), DetailSource::Guide => self.file_detail( - "Current Reading", + &self.current_reading_label, self.current_guide_path.as_deref(), "No current actionable document was found. Use todo/outcome-map for the next step.", ), @@ -449,10 +474,10 @@ impl TuiOverview { "No knowledge-base focus yet.", )); lines.push(String::new()); - lines.push("Reflection Loop".to_owned()); + lines.push(self.closeout_label.clone()); lines.extend(indented_or_empty( &self.reflection_summary, - "No reflection loop artifacts yet.", + "No closeout artifacts yet.", )); Ok(self.virtual_detail( "Review / Knowledge Focus", @@ -471,8 +496,18 @@ impl TuiOverview { /// Overview 第一屏的证据和漂移摘要。 pub fn evidence_lines(&self) -> Vec { vec![ + format!("Type: {} ({})", self.learning_label, self.learning_kind), + format!( + "Source: {}{}", + self.source_kind, + self.source_reference + .as_ref() + .map(|value| format!(" -> {value}")) + .unwrap_or_default() + ), format!( - "Stage: {} / {} ({}/{})", + "{}: {} / {} ({}/{})", + self.current_unit_label, self.current_phase, self.current_status, self.done_stage_count, @@ -484,7 +519,8 @@ impl TuiOverview { format!("Reflection loop items: {}", self.reflection_summary.len()), format!("Transitions recorded: {}", self.recent_transitions.len()), format!( - "Current reading: {}", + "{}: {}", + self.current_reading_label, self.current_guide_path .as_ref() .map(|path| relative_display(&self.artifact_root, path)) @@ -498,7 +534,8 @@ impl TuiOverview { pub fn reading_map_lines(&self) -> Vec { vec![ format!( - "g Current: {}", + "g {}: {}", + self.current_reading_label, self.current_guide_path .as_ref() .map(|path| relative_display(&self.artifact_root, path)) @@ -557,6 +594,10 @@ impl TuiOverview { /// 从学习任务目录加载 TUI 只读总览数据。 pub fn load_overview(task_dir: &Path) -> Result { let doc = state_toml::load_state_doc(&state_toml::state_path(task_dir))?; + let learning_kind = state_toml::task_kind(&doc); + let adapter = learning_type_adapter(&learning_kind); + let source_kind = state_toml::project_source_kind(&doc); + let source_reference = learning_source_reference(adapter, &doc); let (topic_dir, topic_doc) = active_topic_doc(task_dir, &doc)?; let active_topic = topic_doc .as_ref() @@ -622,13 +663,14 @@ pub fn load_overview(task_dir: &Path) -> Result { } let next_action = build_next_action( + adapter, &artifact_root, ¤t_phase, ¤t_status, state_toml::next_action(progress_doc), ); - let current_guide_path = find_current_guide(&artifact_root, ¤t_phase); - let reflection_summary = reflection_summary(&artifact_root); + let current_guide_path = find_current_guide(adapter, &artifact_root, ¤t_phase); + let reflection_summary = reflection_summary(adapter, &artifact_root); Ok(TuiOverview { task_name: state_toml::task_name(&doc), @@ -637,6 +679,14 @@ pub fn load_overview(task_dir: &Path) -> Result { active_topic_title, topic_dir, lifecycle, + learning_kind, + learning_label: adapter.label.to_owned(), + source_kind, + source_reference, + current_unit_label: adapter.current_unit_label.to_owned(), + progress_unit_label: adapter.progress_unit_label.to_owned(), + current_reading_label: adapter.current_reading_label.to_owned(), + closeout_label: adapter.closeout_label.to_owned(), workspace_bucket, current_phase, current_status, @@ -657,186 +707,6 @@ pub fn load_overview(task_dir: &Path) -> Result { }) } -fn build_next_action( - artifact_root: &Path, - current_phase: &str, - current_status: &str, - fallback: String, -) -> String { - if current_phase == "10-reflection" { - return reflection_next_action(artifact_root, current_status, fallback); - } - - let guide_dir = artifact_root.join("guides").join(current_phase); - if let Some(guide) = find_action_guide(&guide_dir) - && let Ok(content) = fs::read_to_string(&guide) - { - let mut lines = Vec::new(); - let relative_guide = relative_display(artifact_root, &guide); - lines.push(format!("Current: {current_phase} / {current_status}")); - if let Some(slice) = markdown_value(&content, "Current slice") { - lines.push(format!("Slice: {slice}")); - } - if let Some(gap) = markdown_value(&content, "Current gap") { - lines.push(format!("Gap: {gap}")); - } - let actions = action_card_items(&content); - if !actions.is_empty() { - lines.push(format!("Next: {}", actions.join(" -> "))); - } - if let Some(after) = markdown_value(&content, "After this") { - lines.push(format!("Unlocks: {after}")); - } - lines.push(format!("Current reading: {relative_guide}")); - return lines.join("\n"); - } - - if let Some(todo_summary) = todo_now_summary(&artifact_root.join(".daedalus").join("todo.md")) { - return todo_summary; - } - - fallback -} - -fn reflection_next_action(artifact_root: &Path, current_status: &str, fallback: String) -> String { - let root = artifact_root.join("reflection"); - if !root.exists() { - return fallback; - } - [ - format!("Current: 10-reflection / {current_status}"), - "Loop: 知识候选表 -> 用户 closeout -> Agent challenge -> 知识库归档".to_owned(), - format!( - "Next: {}", - if root.join("candidate-map.md").exists() { - "打开 reflection/candidate-map.md,围绕候选表逐项确认状态" - } else { - "创建 reflection/candidate-map.md,并用既有 guides / notes / demo / tests 查漏补缺" - } - ), - format!( - "Current reading: {}", - relative_display(artifact_root, &root.join("candidate-map.md")) - ), - ] - .join("\n") -} - -fn find_current_guide(artifact_root: &Path, current_phase: &str) -> Option { - if current_phase == "10-reflection" { - let candidate_map = artifact_root.join("reflection").join("candidate-map.md"); - if candidate_map.exists() { - return Some(candidate_map); - } - } - find_action_guide(&artifact_root.join("guides").join(current_phase)) -} - -fn find_action_guide(guide_dir: &Path) -> Option { - let mut candidates = Vec::new(); - let entries = fs::read_dir(guide_dir).ok()?; - for entry in entries.flatten() { - let path = entry.path(); - let Some(name) = path.file_name().and_then(|value| value.to_str()) else { - continue; - }; - if path.is_file() - && name.ends_with(".md") - && name != "README.md" - && name.contains("slice") - && is_actionable_guide(&path) - { - candidates.push(path); - } - } - candidates.sort(); - candidates.pop() -} - -fn is_actionable_guide(path: &Path) -> bool { - let Ok(content) = fs::read_to_string(path) else { - return false; - }; - if markdown_value(&content, "Current slice") - .map(|value| value.to_lowercase().contains("completed")) - .unwrap_or(false) - { - return false; - } - content.contains("## Action Card") -} - -fn markdown_value(content: &str, key: &str) -> Option { - let prefix = format!("- {key}:"); - content.lines().find_map(|line| { - line.trim() - .strip_prefix(&prefix) - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(ToOwned::to_owned) - }) -} - -fn action_card_items(content: &str) -> Vec { - let mut in_action_card = false; - let mut values = Vec::new(); - for line in content.lines() { - let trimmed = line.trim(); - if trimmed == "## Action Card" { - in_action_card = true; - continue; - } - if in_action_card && trimmed.starts_with("## ") { - break; - } - if in_action_card - && trimmed.starts_with("### ") - && let Some((_, title)) = trimmed.trim_start_matches("### ").split_once(". ") - { - values.push(title.to_owned()); - } - if values.len() >= 3 { - break; - } - } - values -} - -fn todo_now_summary(path: &Path) -> Option { - let content = fs::read_to_string(path).ok()?; - let mut in_now = false; - let mut lines = Vec::new(); - for line in content.lines() { - let trimmed = line.trim(); - if trimmed == "## Now" { - in_now = true; - continue; - } - if in_now && trimmed.starts_with("## ") { - break; - } - if in_now - && trimmed.starts_with("- ") - && (trimmed.contains("当前问题") - || trimmed.contains("当前待解决") - || trimmed.contains("完成后解锁")) - { - lines.push(trimmed.trim_start_matches("- ").to_owned()); - } - if lines.len() >= 4 { - break; - } - } - (!lines.is_empty()).then(|| lines.join("\n")) -} - -fn relative_display(root: &Path, path: &Path) -> String { - path.strip_prefix(root) - .unwrap_or(path) - .to_string_lossy() - .replace(std::path::MAIN_SEPARATOR, "/") -} - fn non_empty_lines(values: &[String], empty_message: &str) -> Vec { if values.is_empty() { vec![empty_message.to_owned()] @@ -894,6 +764,8 @@ pub fn scan_task_summaries(repo_root: &Path) -> Result> { fn summary_from_task_dir(task_dir: &Path, bucket: &str) -> Result { let doc = state_toml::load_state_doc(&state_toml::state_path(task_dir))?; + let learning_kind = state_toml::task_kind(&doc); + let adapter = learning_type_adapter(&learning_kind); let (_, topic_doc) = active_topic_doc(task_dir, &doc)?; let progress_doc = topic_doc.as_ref().unwrap_or(&doc); let active_topic = topic_doc @@ -914,6 +786,8 @@ fn summary_from_task_dir(task_dir: &Path, bucket: &str) -> Result Vec { values } -fn reflection_summary(artifact_root: &Path) -> Vec { - let root = artifact_root.join("reflection"); - [("候选表", "candidate-map.md")] - .into_iter() - .filter_map(|(label, file)| { - let path = root.join(file); - path.exists() - .then(|| format!("{label}: {}", relative_display(artifact_root, &path))) - }) - .collect() -} - fn bucket_order(bucket: &str) -> usize { WORKSPACE_BUCKETS .iter() @@ -1048,6 +910,14 @@ mod tests { active_topic_title: "Tools Permissions".to_owned(), topic_dir: None, lifecycle: "active".to_owned(), + learning_kind: "repo-learning".to_owned(), + learning_label: "Repo Learning".to_owned(), + source_kind: "repo".to_owned(), + source_reference: Some("demo-repo".to_owned()), + current_unit_label: "Stage".to_owned(), + progress_unit_label: "repo stages".to_owned(), + current_reading_label: "Current Guide".to_owned(), + closeout_label: "Reflection Loop".to_owned(), workspace_bucket: "projects".to_owned(), current_phase: "08-demo-coder".to_owned(), current_status: "active".to_owned(), @@ -1072,109 +942,107 @@ mod tests { } #[test] - fn next_action_prefers_slice_action_guide() { + fn course_learning_overview_uses_course_adapter() { let temp = tempfile::TempDir::new().expect("temp dir"); - let root = temp.path(); - let guide_dir = root.join("guides/08-demo-coder"); - fs::create_dir_all(&guide_dir).expect("guide dir"); + let project = temp.path().join("course-project"); + let topic = project.join("topics/lora-loop"); + fs::create_dir_all(project.join(".daedalus")).expect("project state dir"); + fs::create_dir_all(topic.join(".daedalus")).expect("topic state dir"); + fs::create_dir_all(topic.join("guides/04-lesson-lab")).expect("lesson guide dir"); fs::write( - guide_dir.join("slice-6-hardening.md"), - r#"# Slice 6 Hardening Guide - -## Learning Navigation - -- Current slice: Slice 6 Agent Orchestrator -- Current gap: live path 已跑通,但 deterministic tests 和安全阀不足 -- After this: 可以把 approval / sandbox / retry 接入 ReAct loop - -## Action Card - -### 1. Add `max_turns` - -### 2. Emit `ToolCallFinished` - -### 3. Add Fake LLM Tests - -## Completion Criteria + project.join(".daedalus/state.toml"), + r#" +[task] +name = "course-project" +kind = "course-learning" +lifecycle = "active" +workspace_bucket = "projects" + +[project] +mode = "multi-topic" +active_topic = "lora-loop" +source_kind = "course" +source_name = "HF Course" +course_url = "https://huggingface.co/learn/llm-course/en" + +[[topics]] +slug = "lora-loop" +title = "LoRA Loop" +lifecycle = "active" +path = "topics/lora-loop" +inherits = [] "#, ) - .expect("guide"); - - let next_action = - build_next_action(root, "08-demo-coder", "active", "short fallback".to_owned()); - - assert!(next_action.contains("Current: 08-demo-coder / active")); - assert!(next_action.contains("Slice: Slice 6 Agent Orchestrator")); - assert!(next_action.contains("Gap: live path 已跑通")); - assert!(next_action.contains("Next: Add `max_turns` -> Emit `ToolCallFinished`")); - assert!(next_action.contains("Current reading: guides/08-demo-coder/slice-6-hardening.md")); - assert!(!next_action.contains("short fallback")); - } - - #[test] - fn next_action_accepts_numbered_slice_guide_and_ignores_completed_guide() { - let temp = tempfile::TempDir::new().expect("temp dir"); - let root = temp.path(); - let guide_dir = root.join("guides/08-demo-coder"); - fs::create_dir_all(&guide_dir).expect("guide dir"); + .expect("project state"); fs::write( - guide_dir.join("07-slice-7-policy-composition.md"), - r#"# Slice 7 - -## Learning Navigation - -- Current slice: Slice 7 completed -- Current gap: old completed work - -## Action Card - -### 1. Old completed action + topic.join(".daedalus/state.toml"), + r#" +[topic] +slug = "lora-loop" +title = "LoRA Loop" +kind = "course-learning-topic" +lifecycle = "active" +current_phase = "04-lesson-lab" +next_action = "进入 lesson lab。" + +[[stages]] +id = "04-lesson-lab" +title = "把课程 lesson 变成可观察实验" +status = "active" +required_artifacts = ["guides/04-lesson-lab/README.md"] "#, ) - .expect("completed guide"); + .expect("topic state"); + fs::write(topic.join(".daedalus/todo.md"), "# Todo\n").expect("todo"); + fs::write(topic.join(".daedalus/outcome-map.md"), "# Outcome\n").expect("outcome"); fs::write( - guide_dir.join("08-slice-8-event-protocol.md"), - r#"# Slice 8 - -## Learning Navigation + topic.join("guides/04-lesson-lab/README.md"), + r#"# Trainer Batch Lesson -- Current slice: Slice 8 Event Protocol Hardening -- Current gap: event protocol is not observable enough -- After this: README can explain the command safety trace +## Lesson Lab -## Action Card +- Lesson:Trainer batch / forward / loss +- 当前问题:用户已经跑通训练,但还不知道 batch 如何进入 model。 -### 1. Define event protocol +## Next Lesson Lab -### 2. Emit shell runtime events +- 打印 `batch.keys()` 和 tensor shape +- 手动运行 `model(**batch)` "#, ) - .expect("active guide"); + .expect("lesson guide"); - let next_action = - build_next_action(root, "08-demo-coder", "active", "short fallback".to_owned()); + let overview = load_overview(&project).expect("overview"); - assert!(next_action.contains("Slice: Slice 8 Event Protocol Hardening")); - assert!(next_action.contains("Gap: event protocol is not observable enough")); - assert!(next_action.contains("Next: Define event protocol -> Emit shell runtime events")); + assert_eq!(overview.learning_kind, "course-learning"); + assert_eq!(overview.learning_label, "Course Learning"); + assert_eq!(overview.source_kind, "course"); + assert_eq!( + overview.source_reference.as_deref(), + Some("https://huggingface.co/learn/llm-course/en") + ); + assert_eq!(overview.current_unit_label, "Course Stage"); + assert_eq!(overview.progress_unit_label, "course stages"); + assert_eq!(overview.current_reading_label, "Course Guide"); + assert_eq!( + overview.current_guide_path.as_deref(), + Some(topic.join("guides/04-lesson-lab/README.md").as_path()) + ); assert!( - next_action - .contains("Current reading: guides/08-demo-coder/08-slice-8-event-protocol.md") + overview + .next_action + .contains("Course Stage: 04-lesson-lab / active") + ); + assert!( + overview + .next_action + .contains("Focus: Trainer batch / forward / loss") + ); + assert!( + overview + .next_action + .contains("Course Guide: guides/04-lesson-lab/README.md") ); - assert!(!next_action.contains("Old completed action")); - } - - #[test] - fn reflection_phase_uses_candidate_map_as_current_reading() { - let temp = tempfile::TempDir::new().expect("temp dir"); - let root = temp.path(); - let guide = root.join("reflection/candidate-map.md"); - fs::create_dir_all(guide.parent().expect("reflection parent")).expect("reflection dir"); - fs::write(&guide, "# 知识候选表\n").expect("reflection"); - - let current_guide = find_current_guide(root, "10-reflection").expect("current guide"); - - assert_eq!(current_guide, guide); } #[test] diff --git a/crates/daedalus-cli/src/interfaces/tui/learning_types.rs b/crates/daedalus-cli/src/interfaces/tui/learning_types.rs new file mode 100644 index 0000000..d4f9506 --- /dev/null +++ b/crates/daedalus-cli/src/interfaces/tui/learning_types.rs @@ -0,0 +1,573 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::infrastructure::state_toml; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum GuideLookupStrategy { + RepoActionGuide, + StageReadme, +} + +#[derive(Debug, Clone, Copy)] +pub(super) struct LearningTypeTuiAdapter { + pub(super) kind: &'static str, + pub(super) label: &'static str, + pub(super) current_unit_label: &'static str, + pub(super) progress_unit_label: &'static str, + pub(super) current_reading_label: &'static str, + pub(super) closeout_label: &'static str, + closeout_stage_id: &'static str, + closeout_loop: &'static str, + closeout_existing_next: &'static str, + closeout_missing_next: &'static str, + guide_lookup: GuideLookupStrategy, +} + +const REPO_LEARNING_TUI: LearningTypeTuiAdapter = LearningTypeTuiAdapter { + kind: "repo-learning", + label: "Repo Learning", + current_unit_label: "Stage", + progress_unit_label: "repo stages", + current_reading_label: "Current Guide", + closeout_label: "Reflection Loop", + closeout_stage_id: "10-reflection", + closeout_loop: "知识候选表 -> 用户 closeout -> Agent challenge -> 知识库归档", + closeout_existing_next: "打开 reflection/candidate-map.md,围绕候选表逐项确认状态", + closeout_missing_next: "创建 reflection/candidate-map.md,并用既有 guides / notes / demo / tests 查漏补缺", + guide_lookup: GuideLookupStrategy::RepoActionGuide, +}; + +const COURSE_LEARNING_TUI: LearningTypeTuiAdapter = LearningTypeTuiAdapter { + kind: "course-learning", + label: "Course Learning", + current_unit_label: "Course Stage", + progress_unit_label: "course stages", + current_reading_label: "Course Guide", + closeout_label: "Closeout Loop", + closeout_stage_id: "09-closeout-archive", + closeout_loop: "lesson evidence -> mastery review -> transfer patterns -> archive", + closeout_existing_next: "打开 reflection/candidate-map.md,确认课程概念、实验、迁移和薄弱点候选", + closeout_missing_next: "创建 reflection/candidate-map.md,并从 lesson notes / review / demo evidence 提取候选", + guide_lookup: GuideLookupStrategy::StageReadme, +}; + +const GENERIC_LEARNING_TUI: LearningTypeTuiAdapter = LearningTypeTuiAdapter { + kind: "unknown", + label: "Learning", + current_unit_label: "Stage", + progress_unit_label: "stages", + current_reading_label: "Current Guide", + closeout_label: "Closeout Loop", + closeout_stage_id: "closeout", + closeout_loop: "evidence -> review -> archive", + closeout_existing_next: "打开 reflection/candidate-map.md,确认候选和缺口", + closeout_missing_next: "创建 reflection/candidate-map.md,并从现有学习证据中提取候选", + guide_lookup: GuideLookupStrategy::StageReadme, +}; + +pub(super) fn learning_type_adapter(kind: &str) -> LearningTypeTuiAdapter { + match kind { + "repo-learning" => REPO_LEARNING_TUI, + "course-learning" => COURSE_LEARNING_TUI, + _ => LearningTypeTuiAdapter { + kind: "unknown", + ..GENERIC_LEARNING_TUI + }, + } +} + +pub(super) fn build_next_action( + adapter: LearningTypeTuiAdapter, + artifact_root: &Path, + current_phase: &str, + current_status: &str, + fallback: String, +) -> String { + if current_phase == adapter.closeout_stage_id { + return closeout_next_action(adapter, artifact_root, current_status, fallback); + } + + if let Some(guide) = find_current_guide(adapter, artifact_root, current_phase) + && let Ok(content) = fs::read_to_string(&guide) + { + return match adapter.guide_lookup { + GuideLookupStrategy::RepoActionGuide => repo_next_action_from_guide( + adapter, + artifact_root, + current_phase, + current_status, + &guide, + &content, + ), + GuideLookupStrategy::StageReadme => stage_readme_next_action( + adapter, + artifact_root, + current_phase, + current_status, + &guide, + &content, + ), + }; + } + + if let Some(todo_summary) = todo_now_summary(&artifact_root.join(".daedalus").join("todo.md")) { + return todo_summary; + } + + fallback +} + +fn repo_next_action_from_guide( + adapter: LearningTypeTuiAdapter, + artifact_root: &Path, + current_phase: &str, + current_status: &str, + guide: &Path, + content: &str, +) -> String { + if is_actionable_guide(guide) { + let mut lines = Vec::new(); + let relative_guide = relative_display(artifact_root, guide); + lines.push(format!( + "{}: {current_phase} / {current_status}", + adapter.current_unit_label + )); + if let Some(slice) = markdown_value(content, "Current slice") { + lines.push(format!("Slice: {slice}")); + } + if let Some(gap) = markdown_value(content, "Current gap") { + lines.push(format!("Gap: {gap}")); + } + let actions = action_card_items(content); + if !actions.is_empty() { + lines.push(format!("Next: {}", actions.join(" -> "))); + } + if let Some(after) = markdown_value(content, "After this") { + lines.push(format!("Unlocks: {after}")); + } + lines.push(format!( + "{}: {relative_guide}", + adapter.current_reading_label + )); + return lines.join("\n"); + } + + stage_readme_next_action( + adapter, + artifact_root, + current_phase, + current_status, + guide, + content, + ) +} + +fn stage_readme_next_action( + adapter: LearningTypeTuiAdapter, + artifact_root: &Path, + current_phase: &str, + current_status: &str, + guide: &Path, + content: &str, +) -> String { + let mut lines = Vec::new(); + let relative_guide = relative_display(artifact_root, guide); + lines.push(format!( + "{}: {current_phase} / {current_status}", + adapter.current_unit_label + )); + if let Some(lesson) = markdown_value(content, "Lesson") + .or_else(|| markdown_value(content, "当前目标")) + .or_else(|| markdown_value(content, "目标")) + { + lines.push(format!("Focus: {lesson}")); + } + if let Some(gap) = markdown_value(content, "Current gap") + .or_else(|| markdown_value(content, "当前问题")) + .or_else(|| markdown_value(content, "Current open decision")) + { + lines.push(format!("Gap: {gap}")); + } + let next_items = section_bullets(content, "Next Lesson Lab") + .into_iter() + .chain(section_bullets(content, "Action Card")) + .take(3) + .collect::>(); + if !next_items.is_empty() { + lines.push(format!("Next: {}", next_items.join(" -> "))); + } + lines.push(format!( + "{}: {relative_guide}", + adapter.current_reading_label + )); + lines.join("\n") +} + +fn closeout_next_action( + adapter: LearningTypeTuiAdapter, + artifact_root: &Path, + current_status: &str, + fallback: String, +) -> String { + let root = artifact_root.join("reflection"); + if !root.exists() { + return fallback; + } + [ + format!( + "{}: {} / {current_status}", + adapter.current_unit_label, adapter.closeout_stage_id + ), + format!("Loop: {}", adapter.closeout_loop), + format!( + "Next: {}", + if root.join("candidate-map.md").exists() { + adapter.closeout_existing_next + } else { + adapter.closeout_missing_next + } + ), + format!( + "{}: {}", + adapter.current_reading_label, + relative_display(artifact_root, &root.join("candidate-map.md")) + ), + ] + .join("\n") +} + +pub(super) fn find_current_guide( + adapter: LearningTypeTuiAdapter, + artifact_root: &Path, + current_phase: &str, +) -> Option { + if current_phase == adapter.closeout_stage_id { + let candidate_map = artifact_root.join("reflection").join("candidate-map.md"); + if candidate_map.exists() { + return Some(candidate_map); + } + } + let guide_dir = artifact_root.join("guides").join(current_phase); + match adapter.guide_lookup { + GuideLookupStrategy::RepoActionGuide => { + find_action_guide(&guide_dir).or_else(|| find_stage_readme(&guide_dir)) + } + GuideLookupStrategy::StageReadme => find_stage_readme(&guide_dir), + } +} + +fn find_action_guide(guide_dir: &Path) -> Option { + let mut candidates = Vec::new(); + let entries = fs::read_dir(guide_dir).ok()?; + for entry in entries.flatten() { + let path = entry.path(); + let Some(name) = path.file_name().and_then(|value| value.to_str()) else { + continue; + }; + if path.is_file() + && name.ends_with(".md") + && name != "README.md" + && name.contains("slice") + && is_actionable_guide(&path) + { + candidates.push(path); + } + } + candidates.sort(); + candidates.pop() +} + +fn find_stage_readme(guide_dir: &Path) -> Option { + let readme = guide_dir.join("README.md"); + if readme.exists() { + return Some(readme); + } + + let mut candidates = Vec::new(); + let entries = fs::read_dir(guide_dir).ok()?; + for entry in entries.flatten() { + let path = entry.path(); + let Some(name) = path.file_name().and_then(|value| value.to_str()) else { + continue; + }; + if path.is_file() && name.ends_with(".md") { + candidates.push(path); + } + } + candidates.sort(); + candidates.pop() +} + +fn is_actionable_guide(path: &Path) -> bool { + let Ok(content) = fs::read_to_string(path) else { + return false; + }; + if markdown_value(&content, "Current slice") + .map(|value| value.to_lowercase().contains("completed")) + .unwrap_or(false) + { + return false; + } + content.contains("## Action Card") +} + +fn markdown_value(content: &str, key: &str) -> Option { + let ascii_prefix = format!("- {key}:"); + let fullwidth_prefix = format!("- {key}:"); + content.lines().find_map(|line| { + let trimmed = line.trim(); + trimmed + .strip_prefix(&ascii_prefix) + .or_else(|| trimmed.strip_prefix(&fullwidth_prefix)) + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(ToOwned::to_owned) + }) +} + +fn section_bullets(content: &str, heading: &str) -> Vec { + let mut in_section = false; + let mut values = Vec::new(); + for line in content.lines() { + let trimmed = line.trim(); + if trimmed == format!("## {heading}") { + in_section = true; + continue; + } + if in_section && trimmed.starts_with("## ") { + break; + } + if in_section && trimmed.starts_with("- ") { + values.push(trimmed.trim_start_matches("- ").trim().to_owned()); + } + if values.len() >= 3 { + break; + } + } + values +} + +fn action_card_items(content: &str) -> Vec { + let mut in_action_card = false; + let mut values = Vec::new(); + for line in content.lines() { + let trimmed = line.trim(); + if trimmed == "## Action Card" { + in_action_card = true; + continue; + } + if in_action_card && trimmed.starts_with("## ") { + break; + } + if in_action_card + && trimmed.starts_with("### ") + && let Some((_, title)) = trimmed.trim_start_matches("### ").split_once(". ") + { + values.push(title.to_owned()); + } + if values.len() >= 3 { + break; + } + } + values +} + +fn todo_now_summary(path: &Path) -> Option { + let content = fs::read_to_string(path).ok()?; + let mut in_now = false; + let mut lines = Vec::new(); + for line in content.lines() { + let trimmed = line.trim(); + if trimmed == "## Now" { + in_now = true; + continue; + } + if in_now && trimmed.starts_with("## ") { + break; + } + if in_now + && trimmed.starts_with("- ") + && (trimmed.contains("当前问题") + || trimmed.contains("当前待解决") + || trimmed.contains("完成后解锁")) + { + lines.push(trimmed.trim_start_matches("- ").to_owned()); + } + if lines.len() >= 4 { + break; + } + } + (!lines.is_empty()).then(|| lines.join("\n")) +} + +pub(super) fn learning_source_reference( + adapter: LearningTypeTuiAdapter, + doc: &toml_edit::DocumentMut, +) -> Option { + match adapter.kind { + "course-learning" => { + state_toml::course_url(doc).or_else(|| project_field(doc, "source_name")) + } + "repo-learning" => project_field(doc, "source_url") + .or_else(|| project_field(doc, "repo_url")) + .or_else(|| project_field(doc, "source_name")), + _ => project_field(doc, "source_name"), + } +} + +fn project_field(doc: &toml_edit::DocumentMut, field: &str) -> Option { + doc["project"][field] + .as_str() + .filter(|value| !value.trim().is_empty()) + .map(ToOwned::to_owned) +} + +pub(super) fn reflection_summary( + adapter: LearningTypeTuiAdapter, + artifact_root: &Path, +) -> Vec { + let root = artifact_root.join("reflection"); + let candidate_label = if adapter.kind == "course-learning" { + "课程候选表" + } else { + "候选表" + }; + [(candidate_label, "candidate-map.md")] + .into_iter() + .filter_map(|(label, file)| { + let path = root.join(file); + path.exists() + .then(|| format!("{label}: {}", relative_display(artifact_root, &path))) + }) + .collect() +} + +pub(super) fn relative_display(root: &Path, path: &Path) -> String { + path.strip_prefix(root) + .unwrap_or(path) + .to_string_lossy() + .replace(std::path::MAIN_SEPARATOR, "/") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn next_action_prefers_slice_action_guide() { + let temp = tempfile::TempDir::new().expect("temp dir"); + let root = temp.path(); + let guide_dir = root.join("guides/08-demo-coder"); + fs::create_dir_all(&guide_dir).expect("guide dir"); + fs::write( + guide_dir.join("slice-6-hardening.md"), + r#"# Slice 6 Hardening Guide + +## Learning Navigation + +- Current slice: Slice 6 Agent Orchestrator +- Current gap: live path 已跑通,但 deterministic tests 和安全阀不足 +- After this: 可以把 approval / sandbox / retry 接入 ReAct loop + +## Action Card + +### 1. Add `max_turns` + +### 2. Emit `ToolCallFinished` + +### 3. Add Fake LLM Tests + +## Completion Criteria +"#, + ) + .expect("guide"); + + let next_action = build_next_action( + REPO_LEARNING_TUI, + root, + "08-demo-coder", + "active", + "short fallback".to_owned(), + ); + + assert!(next_action.contains("Stage: 08-demo-coder / active")); + assert!(next_action.contains("Slice: Slice 6 Agent Orchestrator")); + assert!(next_action.contains("Gap: live path 已跑通")); + assert!(next_action.contains("Next: Add `max_turns` -> Emit `ToolCallFinished`")); + assert!(next_action.contains("Current Guide: guides/08-demo-coder/slice-6-hardening.md")); + assert!(!next_action.contains("short fallback")); + } + + #[test] + fn next_action_accepts_numbered_slice_guide_and_ignores_completed_guide() { + let temp = tempfile::TempDir::new().expect("temp dir"); + let root = temp.path(); + let guide_dir = root.join("guides/08-demo-coder"); + fs::create_dir_all(&guide_dir).expect("guide dir"); + fs::write( + guide_dir.join("07-slice-7-policy-composition.md"), + r#"# Slice 7 + +## Learning Navigation + +- Current slice: Slice 7 completed +- Current gap: old completed work + +## Action Card + +### 1. Old completed action +"#, + ) + .expect("completed guide"); + fs::write( + guide_dir.join("08-slice-8-event-protocol.md"), + r#"# Slice 8 + +## Learning Navigation + +- Current slice: Slice 8 Event Protocol Hardening +- Current gap: event protocol is not observable enough +- After this: README can explain the command safety trace + +## Action Card + +### 1. Define event protocol + +### 2. Emit shell runtime events +"#, + ) + .expect("active guide"); + + let next_action = build_next_action( + REPO_LEARNING_TUI, + root, + "08-demo-coder", + "active", + "short fallback".to_owned(), + ); + + assert!(next_action.contains("Slice: Slice 8 Event Protocol Hardening")); + assert!(next_action.contains("Gap: event protocol is not observable enough")); + assert!(next_action.contains("Next: Define event protocol -> Emit shell runtime events")); + assert!( + next_action + .contains("Current Guide: guides/08-demo-coder/08-slice-8-event-protocol.md") + ); + assert!(!next_action.contains("Old completed action")); + } + + #[test] + fn reflection_phase_uses_candidate_map_as_current_reading() { + let temp = tempfile::TempDir::new().expect("temp dir"); + let root = temp.path(); + let guide = root.join("reflection/candidate-map.md"); + fs::create_dir_all(guide.parent().expect("reflection parent")).expect("reflection dir"); + fs::write(&guide, "# 知识候选表\n").expect("reflection"); + + let current_guide = + find_current_guide(REPO_LEARNING_TUI, root, "10-reflection").expect("current guide"); + + assert_eq!(current_guide, guide); + } +} diff --git a/crates/daedalus-cli/src/interfaces/tui/mod.rs b/crates/daedalus-cli/src/interfaces/tui/mod.rs index f7c16e7..56c4713 100644 --- a/crates/daedalus-cli/src/interfaces/tui/mod.rs +++ b/crates/daedalus-cli/src/interfaces/tui/mod.rs @@ -2,6 +2,8 @@ /// TUI 需要展示的只读应用状态。 pub mod app; +/// learning type 到 TUI 语义的适配层。 +mod learning_types; /// 终端生命周期和渲染循环。 pub mod presenter; /// ratatui 页面绘制函数。 diff --git a/crates/daedalus-cli/src/interfaces/tui/screens.rs b/crates/daedalus-cli/src/interfaces/tui/screens.rs index 4e9f4d0..a8472b9 100644 --- a/crates/daedalus-cli/src/interfaces/tui/screens.rs +++ b/crates/daedalus-cli/src/interfaces/tui/screens.rs @@ -269,6 +269,12 @@ fn render_current_panel(frame: &mut Frame<'_>, area: Rect, overview: &TuiOvervie .add_modifier(Modifier::BOLD), ), Span::raw(" "), + Span::styled("Type ", muted()), + Span::styled( + overview.learning_label.as_str(), + Style::default().fg(SECONDARY), + ), + Span::raw(" "), Span::styled("Topic ", muted()), Span::styled( overview.active_topic.as_str(), @@ -281,7 +287,7 @@ fn render_current_panel(frame: &mut Frame<'_>, area: Rect, overview: &TuiOvervie Style::default().fg(MUTED), ), Span::raw(" "), - Span::styled("Phase ", muted()), + Span::styled(format!("{} ", overview.current_unit_label), muted()), Span::styled( overview.current_phase.as_str(), Style::default().fg(CURRENT_ACCENT), @@ -322,8 +328,8 @@ fn render_current_panel(frame: &mut Frame<'_>, area: Rect, overview: &TuiOvervie ) .ratio(progress) .label(format!( - "{}/{} stages complete", - overview.done_stage_count, overview.total_stage_count + "{}/{} {} complete", + overview.done_stage_count, overview.total_stage_count, overview.progress_unit_label )); frame.render_widget(gauge, chunks[1]); } @@ -487,6 +493,10 @@ fn render_task_list(frame: &mut Frame<'_>, area: Rect, tasks: &[TuiTaskSummary], .add_modifier(Modifier::BOLD), ), Span::styled(format!("{:<24}", task.task_name), Style::default().fg(TEXT)), + Span::styled( + format!("{:<17}", task.learning_label), + Style::default().fg(SECONDARY), + ), Span::styled( format!("{:<18}", task.active_topic), Style::default().fg(TODO_ACCENT), diff --git a/crates/daedalus-cli/tests/cli_workflow.rs b/crates/daedalus-cli/tests/cli_workflow.rs index ea1545b..8f39edf 100644 --- a/crates/daedalus-cli/tests/cli_workflow.rs +++ b/crates/daedalus-cli/tests/cli_workflow.rs @@ -28,6 +28,24 @@ fn repo_fixture() -> TempDir { &source_topic_template, &repo.join("system/templates/repo-topic"), ); + let source_course_template = manifest_dir + .parent() + .and_then(Path::parent) + .expect("repo root") + .join("system/templates/course"); + copy_dir( + &source_course_template, + &repo.join("system/templates/course"), + ); + let source_course_topic_template = manifest_dir + .parent() + .and_then(Path::parent) + .expect("repo root") + .join("system/templates/course-topic"); + copy_dir( + &source_course_topic_template, + &repo.join("system/templates/course-topic"), + ); let source_review_template = manifest_dir .parent() .and_then(Path::parent) @@ -309,6 +327,7 @@ fn init_repo_learning_creates_state_and_rendered_markdown() { .join("workspaces/.daedalus/project-index.toml") .exists() ); + assert!(repo.path().join("workspaces/discovery").exists()); assert!(repo.path().join("workspaces/current-project").exists()); assert!(repo.path().join("workspaces/current-topic").exists()); assert!(!repo.path().join("workspaces/02-learning").exists()); @@ -322,6 +341,110 @@ fn init_repo_learning_creates_state_and_rendered_markdown() { assert!(topic_dir.join(".daedalus/artifact-index.md").exists()); } +#[test] +fn init_course_learning_creates_course_state_and_templates() { + let repo = repo_fixture(); + Command::cargo_bin("daedalus") + .expect("binary") + .current_dir(repo.path()) + .args([ + "init", + "course-learning", + "HF Course", + "--topic", + "lora-loop", + "--title", + "LoRA Loop", + "--course-url", + "https://huggingface.co/learn/llm-course/en", + ]) + .assert() + .success() + .stdout(predicates::str::contains( + "ok: initialized course-learning project", + )); + + let task_dir = repo.path().join("workspaces/projects/hf-course"); + let topic_dir = task_dir.join("topics/lora-loop"); + assert!(task_dir.join("shared/syllabus-map.md").exists()); + assert!(task_dir.join("shared/course-progress.md").exists()); + assert!(task_dir.join("shared/concept-map.md").exists()); + assert!(!task_dir.join("shared/source-index.md").exists()); + assert!(!task_dir.join("source/pull_source.sh").exists()); + assert!(topic_dir.join("guides/04-lesson-lab/.gitkeep").exists()); + assert!( + topic_dir + .join("guides/05-mechanism-deep-dive/.gitkeep") + .exists() + ); + assert!(topic_dir.join("review/.gitkeep").exists()); + + let project_state = + fs::read_to_string(task_dir.join(".daedalus/state.toml")).expect("project state"); + assert!(project_state.contains("kind = \"course-learning\"")); + assert!(project_state.contains("source_kind = \"course\"")); + assert!(project_state.contains("course_url = \"https://huggingface.co/learn/llm-course/en\"")); + + let topic_state = + fs::read_to_string(topic_dir.join(".daedalus/state.toml")).expect("topic state"); + assert!(topic_state.contains("kind = \"course-learning-topic\"")); + assert!(topic_state.contains("current_phase = \"01-need-aligner\"")); + assert!(topic_state.contains("id = \"04-lesson-lab\"")); + assert!(topic_state.contains("id = \"05-mechanism-deep-dive\"")); + + let rendered_state = + fs::read_to_string(task_dir.join(".daedalus/state.md")).expect("rendered state"); + assert!(rendered_state.contains("类型:`course-learning`")); + assert!(rendered_state.contains("shared/syllabus-map.md")); + + Command::cargo_bin("daedalus") + .expect("binary") + .current_dir(repo.path()) + .args(["validate"]) + .assert() + .success() + .stdout(predicates::str::contains("ok: workspace valid")); +} + +#[test] +fn validate_course_learning_requires_current_phase_in_todo() { + let repo = repo_fixture(); + Command::cargo_bin("daedalus") + .expect("binary") + .current_dir(repo.path()) + .args([ + "init", + "course-learning", + "HF Course", + "--topic", + "lora-loop", + "--title", + "LoRA Loop", + "--course-url", + "https://huggingface.co/learn/llm-course/en", + ]) + .assert() + .success(); + + let task_dir = repo.path().join("workspaces/projects/hf-course"); + let topic_dir = task_dir.join("topics/lora-loop"); + fs::write( + topic_dir.join(".daedalus/todo.md"), + "# Todo\n\n缺少当前阶段恢复坐标。\n", + ) + .expect("overwrite todo"); + + Command::cargo_bin("daedalus") + .expect("binary") + .current_dir(repo.path()) + .args(["validate"]) + .assert() + .failure() + .stderr(predicates::str::contains( + "current_phase `01-need-aligner` is not recoverable", + )); +} + #[test] fn ide_sync_rust_analyzer_updates_topic_demo_manifest() { let repo = repo_fixture(); @@ -2211,99 +2334,3 @@ fn topic_complete_rejects_unfinished_topic() { assert!(project_state.contains("active_topic = \"main\"")); assert!(project_state.contains("lifecycle = \"active\"")); } - -#[test] -fn migrate_repo_learning_multi_topic_moves_legacy_workspace() { - let repo = repo_fixture(); - let task_dir = repo.path().join("workspaces/projects/legacy-task"); - fs::create_dir_all(task_dir.join(".daedalus")).expect("daedalus dir"); - fs::create_dir_all(task_dir.join("notes/06-code-reader")).expect("notes dir"); - fs::create_dir_all(task_dir.join("guides/06-code-reader")).expect("guides dir"); - fs::create_dir_all(task_dir.join("demo/src")).expect("demo dir"); - fs::create_dir_all(task_dir.join("source")).expect("source dir"); - fs::write(task_dir.join(".daedalus/task-card.md"), "# 旧任务卡\n").expect("task-card"); - fs::write(task_dir.join(".daedalus/outcome-map.md"), "# Outcome Map\n").expect("outcome"); - fs::write(task_dir.join(".daedalus/todo.md"), "# Todo\n").expect("todo"); - fs::write(task_dir.join("notes/06-code-reader/README.md"), "# Notes\n").expect("notes"); - fs::write( - task_dir.join("guides/06-code-reader/README.md"), - "# Guides\n", - ) - .expect("guides"); - fs::write(task_dir.join("demo/README.md"), "# Demo\n").expect("demo"); - fs::write( - task_dir.join("source/pull_source.sh"), - "REPO_URL=\"https://example.com/old.git\"\n", - ) - .expect("pull source"); - fs::write( - task_dir.join(".daedalus/state.toml"), - r#" -[task] -name = "legacy-task" -kind = "repo-learning" -created_at = "2026-05-01 00:00:00" -lifecycle = "active" -workspace_bucket = "02-learning" -current_phase = "01-goal-aligner" -next_action = "继续旧任务。" - -[[stages]] -id = "01-goal-aligner" -title = "对齐 Repo 学习目标" -status = "active" -required_artifacts = [".daedalus/task-card.md", ".daedalus/outcome-map.md"] - -[[transitions]] -stage = "01-goal-aligner" -action = "init" -timestamp = "2026-05-01 00:00:00" -actor = "daedalus-cli" -reason = "初始化旧任务。" -"#, - ) - .expect("state"); - - Command::cargo_bin("daedalus") - .expect("binary") - .current_dir(repo.path()) - .args([ - "migrate", - "repo-learning-multi-topic", - task_dir.to_str().expect("utf8"), - "--topic", - "tools-permissions", - "--title", - "工具与权限", - "--execute", - ]) - .assert() - .success() - .stdout(predicates::str::contains("ok: migration completed")); - - let topic_dir = task_dir.join("topics/tools-permissions"); - assert!(task_dir.join(".daedalus/project-map.md").exists()); - assert!(task_dir.join("shared/source-index.md").exists()); - assert!(topic_dir.join("notes/06-code-reader/README.md").exists()); - assert!(topic_dir.join("guides/06-code-reader/README.md").exists()); - assert!(topic_dir.join("demo/README.md").exists()); - assert!(topic_dir.join(".daedalus/task-card.md").exists()); - assert!(!task_dir.join("notes").exists()); - assert!(!task_dir.join("guides").exists()); - assert!(!task_dir.join("demo").exists()); - - let project_state = - fs::read_to_string(task_dir.join(".daedalus/state.toml")).expect("project state"); - assert!(project_state.contains("mode = \"multi-topic\"")); - assert!(project_state.contains("created_at = \"2026-05-01 00:00:00\"")); - assert!(project_state.contains("active_topic = \"tools-permissions\"")); - assert!(project_state.contains("action = \"migrate\"")); - assert!(project_state.contains("继续 active topic `tools-permissions`")); - let topic_state = - fs::read_to_string(topic_dir.join(".daedalus/state.toml")).expect("topic state"); - assert!(topic_state.contains("[topic]")); - assert!(topic_state.contains("slug = \"tools-permissions\"")); - assert!(topic_state.contains("current_phase = \"01-goal-aligner\"")); - let pull_source = fs::read_to_string(task_dir.join("source/pull_source.sh")).expect("source"); - assert!(pull_source.contains("https://example.com/old.git")); -} diff --git a/daedalus.code-workspace b/daedalus.code-workspace new file mode 100644 index 0000000..f3c333a --- /dev/null +++ b/daedalus.code-workspace @@ -0,0 +1,18 @@ +{ + "folders": [ + { + "name": "daedalus", + "path": "." + }, + { + "name": "hf-course-learning", + "path": "workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning" + } + ], + "settings": { + "python-envs.defaultEnvManager": "ms-python.python:venv", + "python-envs.defaultPackageManager": "ms-python.python:pip", + "python-envs.alwaysUseUv": true, + "python.terminal.activateEnvironment": true + } +} diff --git a/docs/changelog/2026-05-23-repo-learning-multi-topic.md b/docs/changelog/2026-05-23-repo-learning-multi-topic.md index 03e1315..c86dacd 100644 --- a/docs/changelog/2026-05-23-repo-learning-multi-topic.md +++ b/docs/changelog/2026-05-23-repo-learning-multi-topic.md @@ -50,8 +50,6 @@ Project state 只描述 lifecycle、active topic 和 topic 列表;10-stage 学 - `daedalus init repo-learning --topic --title ` - `daedalus topic new/list/activate/complete/abandon/validate` -- `daedalus migrate repo-learning-multi-topic <old-task-dir> --topic <slug> --title <title> --execute` -- `system/bin/migrate-repo-learning-to-multi-topic` - `system/bin/audit-daedalus-agent-instructions` 调整: @@ -93,10 +91,10 @@ topics/tools-permissions ## 重要修正 - 修复 `state_toml` 对 `[topic]` 的硬索引 panic;project state 没有 `[topic]` 是正常情况。 -- 迁移器不再覆盖已有 `source/README.md`、`source/pull_source.sh`、`source/.gitignore`。 -- 迁移器会把 project `next_action` 指向 active topic 的真实进度。 +- 一次性升级过程不覆盖已有 `source/README.md`、`source/pull_source.sh`、`source/.gitignore`。 +- 一次性升级过程会把 project `next_action` 指向 active topic 的真实进度。 - `validate_topic_workspace` 改为校验 `demo/`、`guides/`、`notes/` 目录存在,不再强制 `.gitkeep` 存在。 -- `.daedalus-migration-backup/` 已加入 `.gitignore`,避免迁移备份污染提交。 +- 一次性升级备份目录已加入 `.gitignore`,避免备份污染提交。 ## 验证 @@ -116,10 +114,10 @@ RUSTC_WRAPPER= CARGO_TARGET_DIR=/private/tmp/daedalus-target cargo run --manifes - topic new / activate / list。 - topic 未关闭时 project complete 被拒绝。 - project complete 在 topic 关闭后释放 WIP。 -- legacy single-topic workspace 迁移到 multi-topic project。 +- legacy single-topic workspace 已完成到 multi-topic project 的一次性整理。 ## 后续建议 - TUI 目前是最小适配,后续可增加 project/topic 切换视图。 -- 迁移器目前适合本地一次性迁移,不追求复杂回滚;需要回滚时使用 `.daedalus-migration-backup/`。 +- 旧的一次性升级入口已从长期 CLI 中移除;后续结构调整直接手动整理具体文件。 - 后续新增专题时,优先沉淀 shared/source-index、shared/glossary 和 shared/evidence-registry,减少重复读源码。 diff --git a/docs/changelog/2026-05-24-review-knowledge-system.md b/docs/changelog/2026-05-24-review-knowledge-system.md index b2538ce..12dd31e 100644 --- a/docs/changelog/2026-05-24-review-knowledge-system.md +++ b/docs/changelog/2026-05-24-review-knowledge-system.md @@ -27,7 +27,7 @@ - 新增 review / knowledge-system templates。 - 更新 repo-learning skill、resume、reflection、export-knowledge,使复习与萃取都遵守第一性原理链路。 - TUI 增加 read-only Review Focus 和 Knowledge Focus。 -- 新增迁移脚本 `system/bin/migrate-add-review-knowledge-system`。 +- 当前 Codex learning workspace 已手动补齐 review 与 knowledge-system scaffolding。 - 当前 Codex learning workspace 已补齐 review 与 knowledge-system scaffolding。 ## First-Principles Gate diff --git a/docs/changelog/2026-06-14-topic-discovery-selection.md b/docs/changelog/2026-06-14-topic-discovery-selection.md new file mode 100644 index 0000000..4a38f94 --- /dev/null +++ b/docs/changelog/2026-06-14-topic-discovery-selection.md @@ -0,0 +1,26 @@ +# Topic discovery 选题探索层 + +> 日期:2026-06-14 +> 状态:已完成 + +## 背景 + +daedalus 原有 `clarify-goal` 和 `gatekeeper` 假设用户已经能说清现实问题、预期产物和学习目标。但真实选题场景里,用户往往只有模糊冲动、职业焦虑、能力短板、候选材料吸引力和互相冲突的方向。 + +因此,选题不能只做目标匹配或 accept / defer / reject。真实诉求需要先被探索出来。 + +## 变更 + +- 新增 `system/prompts/common/topic-discovery.md`,在任务卡和 gatekeeper 前增加选题探索层。 +- 新增 `system/templates/discovery/item.md` 和 `workspaces/discovery/README.md`,用于保存 pre-topic discovery 记录。 +- CLI 稳定 workspace layout 现在会创建 `workspaces/discovery`。 +- `clarify-goal` 在诉求仍靠 Agent 猜测时会先要求进入 discovery。 +- `gatekeeper` 不再替代 discovery;真实诉求不清楚时应 defer 并要求先探索。 +- repo learning `01-goal-aligner` 在目标不清时先继承 discovery,不直接推荐 repo 或初始化 project/topic。 +- 全局契约、repo-learning skill、README 和模板索引补充了 discovery / backlog / active topic 的语义分层。 +- `.codex/plans/` 的规则收紧为只保留带 `status` 和 `todos` 的 Codex tracking card;详细正式计划放在 `docs/plan/`。 + +## 验证 + +- `cargo test --manifest-path crates/Cargo.toml -p daedalus-cli` +- `daedalus validate workspaces/projects/openai-codex-cli-deep-learning --all-topics --reviews --knowledge` diff --git a/docs/changelog/2026-06-23-course-learning-system.md b/docs/changelog/2026-06-23-course-learning-system.md new file mode 100644 index 0000000..092542e --- /dev/null +++ b/docs/changelog/2026-06-23-course-learning-system.md @@ -0,0 +1,155 @@ +# Course Learning 一等学习类型 + +> 日期:2026-06-23 +> 状态:已完成 +> 提交:`a0877c60 daedalus/course-learning: 实现课程学习类型` + +## 背景 + +`hugging-face-llm-course` 是课程型学习材料:它有 syllabus、章节顺序、练习代码、概念依赖和官方文档。daedalus 需要让这类学习材料拥有自己的 project/topic 类型、阶段、prompt 和验证规则。 + +course-learning 的核心路径是: + +```text +学习诉求 -> syllabus 路线 -> 概念机制 -> lesson lab -> 迁移练习 -> 掌握度验证 +``` + +这次改造的重点是让 Agent 围绕课程掌握度推进:用户不仅要看过章节、跑通过代码,还要能解释机制、复现实验,并把概念迁移到自己的真实任务。 + +## 变更 + +- 新增正式计划:[`docs/plan/15-course-learning-system.md`](../plan/15-course-learning-system.md)。 +- 新增 Codex tracking card:[`/.codex/plans/course-learning-system.md`](../../.codex/plans/course-learning-system.md),只保留 status / todos 并链接正式计划。 +- 新增 `course-learning` 初始化能力: + - `daedalus init course-learning <project-name> --topic <topic-slug> --title <topic-title> --course-url <url>` + - 生成 `kind = "course-learning"`、`source_kind = "course"`、`course_url = "..."` +- 手动整理现有 Hugging Face Course project: + - 更新 project/topic metadata 为 course-learning。 + - 保留历史 topic、guides、notes、demo、reflection,不强行重命名旧目录。 +- 移除长期迁移 API: + - 不提供 `migrate` 命令族。 + - 删除旧的一次性升级脚本和对应测试。 + - 既有项目调整改为手动整理具体文件,整理后运行 validate。 +- 新增 course project/topic templates: + - `system/templates/course/` + - `system/templates/course-topic/` +- 新增 course prompt namespace: + - `system/prompts/course/01-need-aligner.md` + - `system/prompts/course/02-syllabus-mapper.md` + - `system/prompts/course/03-concept-roadmap.md` + - `system/prompts/course/04-lesson-lab.md` + - `system/prompts/course/05-mechanism-deep-dive.md` + - `system/prompts/course/06-practice-transfer.md` + - `system/prompts/course/07-capstone-lab.md` + - `system/prompts/course/08-review-loop.md` + - `system/prompts/course/09-closeout-archive.md` + - `system/prompts/course/course-learning-cli-contract.md` +- 新增 `course-learning-coach` skill:`.agents/skills/course-learning-coach/SKILL.md`,用于 course / tutorial / official learning path / online curriculum。 + +## 生命周期 + +course-learning 使用独立阶段: + +```text +01-need-aligner +02-syllabus-mapper +03-concept-roadmap +04-lesson-lab +05-mechanism-deep-dive +06-practice-transfer +07-capstone-lab +08-review-loop +09-closeout-archive +``` + +核心不变量: + +```text +课程进度不是掌握度。 +跑通教程不是理解机制。 +章节顺序不是学习目标。 +练习输出必须能回到概念机制。 +概念机制必须能迁移到用户真实任务。 +``` + +## Validate / Render + +- `daedalus validate` 现在理解 `course-learning` project: + - 检查 `source_kind = "course"`。 + - 检查 `course_url`。 + - 检查 `shared/syllabus-map.md`、`shared/course-progress.md`、`shared/concept-map.md`。 +- course topic 现在有额外校验: + - topic kind 必须是 `course-learning-topic`。 + - course stage 目录必须存在。 + - `review/` 目录必须存在。 + - `current_phase` 必须能从 `.daedalus/todo.md` 恢复。 +- `state.md` 渲染会显示 project 类型、course URL 和 course shared files。 + +## Hugging Face Course 整理 + +已将现有 project: + +```text +workspaces/projects/hugging-face-llm-course +``` + +整理前沿用旧 project metadata: + +```toml +kind = "repo-learning" +source_kind = "repo" +``` + +整理为: + +```toml +kind = "course-learning" +source_kind = "course" +course_url = "https://huggingface.co/learn/llm-course/en" +``` + +当前 active topic: + +```text +topics/lora-feedback-loop +``` + +已迁移为 `course-learning-topic`,当前阶段推进到: + +```text +04-lesson-lab +``` + +历史 `guides/04-debugger-guide/` 保留为整理前学习现场。整理后已经新增正式 course guide: + +```text +guides/03-concept-roadmap/README.md +``` + +下一步应新增: + +```text +guides/04-lesson-lab/README.md +notes/04-lesson-lab/README.md +``` + +## 测试与验证 + +- `cargo test --manifest-path crates/Cargo.toml -p daedalus-cli --test cli_workflow` + - 34 passed +- `daedalus validate` + - 当前 `hugging-face-llm-course` workspace valid +- `course-learning-coach` skill validator + - passed +- commit hook + - `cargo fmt` + - `cargo check` + - `cargo clippy` + - `cargo test` + - all passed + +## 当前边界 + +- 本次只实现 `course-learning`,没有同时扩展到 book-learning / paper-learning。 +- 没有强行重命名历史目录,避免破坏已有学习证据。 +- `04-lesson-lab` 的正式 guide 还未生成,这是下一步学习任务,而不是本次 daedalus 设施实现的一部分。 diff --git a/docs/changelog/2026-06-23-tui-learning-type-adapters.md b/docs/changelog/2026-06-23-tui-learning-type-adapters.md new file mode 100644 index 0000000..f5bf500 --- /dev/null +++ b/docs/changelog/2026-06-23-tui-learning-type-adapters.md @@ -0,0 +1,54 @@ +# TUI Learning Type Adapter + +> 日期:2026-06-23 +> 状态:已完成 + +## 背景 + +course-learning 已经成为 daedalus 的一等学习类型,但 TUI 仍主要依赖 repo-learning 时代的阅读入口习惯:slice guide、Action Card、`10-reflection`。这会让 course-learning 虽然能显示 stage,却没有自己的课程语义。 + +本次改造把 TUI 的学习类型差异收敛到 adapter 层。 + +## 变更 + +- 新增正式计划:[`docs/plan/16-tui-learning-type-adapters.md`](../plan/16-tui-learning-type-adapters.md)。 +- 新增 Codex tracking card:[`/.codex/plans/tui-learning-type-adapters.md`](../../.codex/plans/tui-learning-type-adapters.md)。 +- 新增 `crates/daedalus-cli/src/interfaces/tui/learning_types.rs`,内聚 `LearningTypeTuiAdapter`、guide 查找、next action 构造和 source reference 策略: + - `repo-learning` adapter 保留 slice guide / Action Card 阅读策略。 + - `course-learning` adapter 使用 course stage README 阅读策略。 + - fallback adapter 支持未知学习类型先按 stage README 工作。 +- `app.rs` 只负责从 state / filesystem 装配 `TuiOverview`,不再承载 repo/course 的具体 guide 解析细节。 +- `TuiOverview` 增加 learning type、source kind、source reference、类型化进度单位和当前阅读标签。 +- TUI overview 现在能显示: + - learning type。 + - source kind / course URL。 + - `Course Stage` / `course stages` 这类类型化文案。 + - course guide 入口。 +- closeout 逻辑不再硬编码 `10-reflection`: + - repo-learning 使用 `10-reflection`。 + - course-learning 使用 `09-closeout-archive`。 + +## 测试 + +- 保留 repo-learning action guide 行为测试。 +- 新增 course-learning overview fixture,验证: + - 识别 `course-learning`。 + - 展示 course URL。 + - 当前阅读入口使用 `guides/04-lesson-lab/README.md`。 + - next action 使用 `Course Stage` 和 `Course Guide` 文案。 + +已验证: + +- `cargo fmt --manifest-path crates/Cargo.toml --check` +- `cargo check --manifest-path crates/Cargo.toml -p daedalus-cli` +- `cargo test --manifest-path crates/Cargo.toml -p daedalus-cli interfaces::tui::learning_types::tests` +- `cargo test --manifest-path crates/Cargo.toml -p daedalus-cli interfaces::tui::app::tests` +- `cargo test --manifest-path crates/Cargo.toml -p daedalus-cli --test cli_workflow` +- `daedalus validate` +- `git diff --check` + +## 当前边界 + +- 没有重做 TUI 视觉设计。 +- 没有实现动态插件系统。 +- 新增学习类型如果需要专属语义,应新增 adapter;如果只遵守 stage README 约定,可以先走 fallback adapter。 diff --git a/docs/evolution/2026-06-14-daedalus-first-topic-evolution.md b/docs/evolution/2026-06-14-daedalus-first-topic-evolution.md index 75374c7..2550d9f 100644 --- a/docs/evolution/2026-06-14-daedalus-first-topic-evolution.md +++ b/docs/evolution/2026-06-14-daedalus-first-topic-evolution.md @@ -116,7 +116,6 @@ Project 不再随着 topic 完成而移动。topic 自己拥有完整 10-stage ### 落地位置 - `crates/daedalus-cli/src/application/topic.rs` -- `crates/daedalus-cli/src/application/migrate.rs` - `crates/daedalus-cli/src/infrastructure/workspace_fs.rs` - `system/templates/repo/` - `system/templates/repo-topic/` @@ -267,7 +266,7 @@ TUI 升级为 read-only learning cockpit: - 支持 todo、outcome-map、current guide、review / knowledge focus 的入口。 - 支持滚动、翻页、返回和刷新。 -同时 CLI 增强了 topic、review、knowledge、ide、validate、migrate 等能力,承担确定性状态管理和校验。 +同时 CLI 增强了 topic、review、knowledge、ide、validate 等能力,承担确定性状态管理和校验。 ### 落地位置 @@ -428,7 +427,7 @@ daedalus 把上下文入口写进 agent 原生加载机制: daedalus 补齐了轻量工程协议: -- commit message hook:`type(scope): 中文描述` 或 `type: 中文描述`。 +- commit message hook:最初采用 `type(scope): 中文描述` 或 `type: 中文描述`,后续随 daedalus 从纯开发仓库演进为学习主题推进系统,更新为 `{topic}/{scope}: 中文描述`。 - 长任务前给学习者一个可并行思考的小任务。 - 微闭环后同步学习地图。 - 提交后主动输出当前位置和下一步。 @@ -454,7 +453,7 @@ daedalus 补齐了轻量工程协议: - active topic 与 pending closeout 投影。 - review / knowledge 校验。 - workspace validate。 -- migration。 +- workspace structure update。 - rust-analyzer linkedProjects 同步。 ### knowledge web diff --git a/docs/plan/01-repo-learning-multi-topic-projects.md b/docs/plan/01-repo-learning-multi-topic-projects.md index 590c34c..cfd5e4c 100644 --- a/docs/plan/01-repo-learning-multi-topic-projects.md +++ b/docs/plan/01-repo-learning-multi-topic-projects.md @@ -27,11 +27,11 @@ Project Shared Context 沉淀跨 topic 的 verified knowledge ## Architecture Decision -daedalus 是本地项目,不需要长期兼容旧 workspace 结构。多专题改造应采用 **breaking migration**: +daedalus 是本地项目,不需要长期兼容旧 workspace 结构。多专题改造应采用一次性破坏式升级: ```text 新架构成为唯一架构。 -旧 workspace 通过一次性迁移脚本升级。 +旧 workspace 通过一次性手动整理进入新布局。 CLI、prompt、validate 不长期保留 legacy 分支。 ``` @@ -40,7 +40,7 @@ CLI、prompt、validate 不长期保留 legacy 分支。 - 状态模型更简单,不需要同时支持 single-topic 和 multi-topic 两套语义。 - `state` 命令、resume 规则、validate 规则都只面向新结构。 - 后续学习任务不会继续生成旧布局。 -- 旧数据仍可保留,但通过迁移脚本进入新布局,而不是由运行时兼容逻辑兜底。 +- 旧数据仍可保留,但通过手动整理进入新布局,而不是由运行时兼容逻辑兜底。 ## First Principles @@ -429,7 +429,7 @@ daedalus topic validate <slug> --project-dir <path> - `topic complete`:校验 topic required artifacts,标记 topic lifecycle completed,但不移动 topic 目录。 - `topic validate`:校验 topic 文件结构、state、required artifacts、inherited evidence 链接。 -### Command Model After Migration +### Command Model After Filesystem Upgrade 迁移完成后,现有 stage 命令默认操作 active topic,而不是 project root: @@ -463,7 +463,6 @@ daedalus state complete 06-code-reader --topic tools-permissions - `state enter/complete/block/resume/rollback`:解析 topic state,再执行 stage transition。 - `task complete/abandon`:关闭 project,要求 topic 状态满足关闭条件。 - `topic new/list/activate/complete/abandon/validate`:管理 topic lifecycle。 -- `migrate repo-learning-multi-topic`:一次性迁移旧 workspace。 - `validate`:先校验 project,再校验 active topic;必要时支持 `--all-topics`。 - `state render`:需要区分 project state render 和 topic state render。 @@ -548,7 +547,7 @@ TUI 需要改的具体点: - progress gauge 使用 active topic 的 stage progress。 - missing artifacts 使用 active topic 的 required artifacts。 - todo focus 读取 active topic 的 `.daedalus/todo.md`。 -- recent transitions 默认显示 active topic transitions,另加 project-level migration / topic switch history。 +- recent transitions 默认显示 active topic transitions,另加 project-level structure update / topic switch history。 - selector 列表增加 active topic 列,避免只看到 project 名称。 - 页面文案从 `Task` / `Phase` 调整为 `Project` / `Topic` / `Stage`。 @@ -795,7 +794,7 @@ rg 旧命令/旧语义关键词 .claude system docs crates/docs 输出需要人工确认的文件和行号 ``` -更进一步可以做 allowlist,确保旧命令只出现在 migration 文档或历史记录中。 +更进一步可以做 allowlist,确保旧命令只出现在历史记录中。 ### repo-learning-coach @@ -855,34 +854,11 @@ Before starting a source reading or demo design step, declare whether the result - 哪些 architecture-map 需要更新? - 哪些 future topics 被发现? -## Migration Strategy +## One-Time Manual Upgrade Note -### One-Time Breaking Migration +因为不保留旧逻辑,旧 workspace 的升级必须是显式、一次性、可 review 的人工文件整理,不沉淀为长期 CLI 或脚本。 -因为不保留旧逻辑,迁移必须是显式、一次性、可回滚的。建议提供本地脚本: - -```text -system/bin/migrate-repo-learning-to-multi-topic -``` - -使用方式: - -```text -system/bin/migrate-repo-learning-to-multi-topic \ - workspaces/02-learning/openai-codex-cli-deep-learning \ - --topic tools-permissions \ - --title "工具系统与权限系统" -``` - -脚本原则: - -- 默认 dry-run,先打印将要移动和生成的文件。 -- 正式执行前创建备份目录。 -- 只迁移 daedalus 自己的 workspace 文件,不碰 `source/codex` 这类外部源码内容。 -- 迁移后运行 `daedalus validate`。 -- 迁移失败时保留备份和 migration log,便于手动恢复。 - -### Migration File Mapping +### File Mapping 旧结构到新结构的映射: @@ -900,7 +876,7 @@ demo/ -> topics/<slug>/demo/ .daedalus/long-context.md -> topics/<slug>/.daedalus/long-context.md ``` -迁移后 project root 新增: +整理后 project root 新增: ```text shared/ @@ -913,9 +889,9 @@ topics/<slug>/ project root 的旧 `.daedalus/*.md` 不再作为长期状态文件保留;它们会被移动到 topic 内。project root 只保留 project-level state、project-map、topic-board 和 project-level logs。 -### Migration Algorithm +### Manual Upgrade Checklist -脚本流程: +人工整理流程: ```text 1. 校验目标 workspace 是旧结构: @@ -931,9 +907,7 @@ project root 的旧 `.daedalus/*.md` 不再作为长期状态文件保留;它 - [[stages]] - [[transitions]] -3. 创建备份: - - .daedalus-migration-backup/<timestamp>/ - - 保存原始 .daedalus、notes、guides、demo 的副本 +3. 必要时先用 git diff / git status 确认可回退状态。 4. 创建新目录: - shared/ @@ -955,7 +929,7 @@ project root 的旧 `.daedalus/*.md` 不再作为长期状态文件保留;它 7. 生成 topic state: - current_phase 继承旧 task.current_phase - [[stages]] 继承旧 stages - - transitions 可以继承旧 transitions,并标记 migrated_from_root = true + - transitions 可以继承旧 transitions,并标记来源说明 8. 渲染 state.md: - project state.md @@ -966,20 +940,6 @@ project root 的旧 `.daedalus/*.md` 不再作为长期状态文件保留;它 - daedalus topic validate <slug> ``` -### Migration Script Shape - -第一版可以用 Rust CLI 实现,也可以先用 shell 脚本编排 CLI。为了保持确定性,最终建议落入 Rust CLI: - -```text -daedalus migrate repo-learning-multi-topic \ - <old-task-dir> \ - --topic <slug> \ - --title <title> \ - --execute -``` - -`system/bin/migrate-repo-learning-to-multi-topic` 可以作为薄封装,方便本地直接运行。 - ## Implementation Phases ### Phase 1: Breaking Filesystem Template @@ -1021,7 +981,7 @@ daedalus migrate repo-learning-multi-topic \ - project root state 不会出现 topic stage transition。 - `daedalus validate` 能报告 project 问题和 active topic 问题。 -### Phase 3: Agent Instruction Migration +### Phase 3: Agent Instruction Update 目标:让所有 skill、prompt、模板和说明文档使用新 CLI 契约,避免 AI 按旧命令语义操作。 @@ -1042,16 +1002,15 @@ daedalus migrate repo-learning-multi-topic \ - 所有阶段 prompt 都知道 notes/guides/demo 属于 active topic。 - `repo-learning-coach` 明确禁止在 project root 手写 topic stage 状态。 -### Phase 4: Migration Script MVP +### Phase 4: Current Workspace Manual Upgrade -目标:把现有旧 workspace 一次性迁移到新结构。 +目标:把现有旧 workspace 一次性整理到新结构。 改动: -- 新增 `daedalus migrate repo-learning-multi-topic`。 -- 新增 `system/bin/migrate-repo-learning-to-multi-topic` 薄封装。 -- 迁移前自动备份。 -- 迁移后自动 validate。 +- 手动移动 workspace 文件到 project/topic 结构。 +- 保留原学习证据。 +- 整理后运行 validate。 验收: @@ -1144,9 +1103,6 @@ daedalus migrate repo-learning-multi-topic \ - `topic_new_creates_topic_workspace`。 - `topic_activate_updates_project_state_and_blocks_previous`。 - `topic_validate_rejects_missing_required_topic_artifact`。 -- `migrate_single_topic_workspace_creates_project_and_topic`。 -- `migrate_single_topic_workspace_moves_artifacts_to_topic`。 -- `migrate_single_topic_workspace_preserves_stage_state`。 - `state_command_from_project_root_uses_active_topic_state`。 - `state_command_inside_topic_uses_topic_state`。 - `task_complete_rejects_project_with_active_unfinished_topic`。 @@ -1173,7 +1129,7 @@ daedalus migrate repo-learning-multi-topic \ ### Agent Instruction Audit Tests - `audit_daedalus_agent_instructions_flags_old_task_stage_language`。 -- `audit_daedalus_agent_instructions_allows_migration_docs_to_mention_legacy`。 +- `audit_daedalus_agent_instructions_blocks_legacy_commands`。 - `repo_learning_skill_mentions_project_topic_stage_contract`。 - `resume_prompt_reads_project_map_then_active_topic_map`。 @@ -1197,18 +1153,17 @@ daedalus migrate repo-learning-multi-topic \ - 每条 evidence 必须有 source、boundary、transfer limit。 - topic 草稿不得直接写入 shared。 -### Risk 3: 一次性迁移有破坏性 +### Risk 3: 一次性整理有破坏性 -不保留 legacy runtime 兼容会让迁移变成关键路径。 +不保留 legacy runtime 兼容会让人工整理变成关键路径。 控制方式: ```text -迁移脚本默认 dry-run -迁移前自动备份 -迁移日志写入 .daedalus/migration-log.md -迁移后自动 validate -失败时不删除 backup +整理前确认 git 状态 +小步移动文件 +整理后运行 validate +必要时用 git diff review ``` ### Risk 4: Topic stage 继承导致偷懒 @@ -1230,11 +1185,10 @@ daedalus migrate repo-learning-multi-topic \ - `knowledge-base` 应该按 project 归档,还是按 topic 归档,再由 project entry 汇总? - shared architecture-map 是否应该允许 Mermaid/图片等非 markdown 资产? - topic 之间是否允许依赖 DAG,还是第一版只支持轻量 `inherits = []`? -- 迁移脚本的备份目录是否默认保留在 workspace 内,还是放到 `/tmp` 并提示路径? ## Recommended First Slice -第一步应该直接收敛文件契约和迁移路径,不保留 legacy 分支: +第一步应该直接收敛文件契约和手动整理路径,不保留 legacy 分支: ```text 1. 修改 repo 模板:root 只表示 project,topic 目录才有 10-stage state。 @@ -1243,7 +1197,7 @@ daedalus migrate repo-learning-multi-topic \ 4. 修改 daedalus state/validate/render:默认操作 active topic。 5. 更新 repo-learning-coach、resume 和所有 repo phase prompts,统一新 CLI 命令语义。 6. 新增指令审计脚本,防止旧命令语义残留。 -7. 新增 migrate repo-learning-multi-topic:把现有 Codex workspace 迁移过去。 +7. 手动整理现有 Codex workspace 到新结构。 8. 修改 daedalus-tui:展示 project + active topic + topic stage。 ``` @@ -1251,5 +1205,5 @@ daedalus migrate repo-learning-multi-topic \ - 架构一次性变干净。 - CLI 不需要长期支持两套 workspace 语义。 -- 当前 Codex 学习项目可以通过迁移脚本保留数据。 +- 当前 Codex 学习项目可以通过手动整理保留数据。 - 后续 topic CLI 和 evidence registry 都建立在唯一结构上。 diff --git a/docs/plan/02-review-and-knowledge-system.md b/docs/plan/02-review-and-knowledge-system.md index 174f51a..bc419f0 100644 --- a/docs/plan/02-review-and-knowledge-system.md +++ b/docs/plan/02-review-and-knowledge-system.md @@ -59,7 +59,7 @@ Knowledge System Extraction - `daedalus knowledge validate` - `daedalus validate --reviews --knowledge`。 - TUI read-only Review Focus / Knowledge Focus。 -- 迁移脚本:`system/bin/migrate-add-review-knowledge-system`。 +- 当前 Codex learning project 已补充 review / knowledge-system scaffolding。 - 当前 Codex learning project 已补充 review / knowledge-system scaffolding。 仍然刻意不做的范围: @@ -947,9 +947,9 @@ daedalus knowledge extract --topic tools-permissions - 生成 knowledge extraction candidates。 - 确认 `daedalus validate --all-topics` 不受 review 影响。 -## Migration Strategy +## Existing Workspace Update Strategy -因为 daedalus 是本地项目,可以继续采用 breaking migration 思路。但 review system 是新增能力,不需要迁移旧 review 数据。 +review system 是新增能力,不需要移动旧 review 数据。 需要做的迁移: @@ -957,13 +957,7 @@ daedalus knowledge extract --topic tools-permissions - 给已有 topic 增加 `.daedalus/reviews/README.md` 可选入口。 - 给已有 project `shared/` 增加 `knowledge-system/` 可选入口。 -这些可以由一个脚本完成: - -```bash -system/bin/migrate-add-review-knowledge-system <project-dir> -``` - -脚本只新增缺失目录和 README,不移动原学习产物。 +这些属于一次性手动整理:只新增缺失目录和 README,不移动原学习产物。 ## Product Boundaries diff --git a/docs/plan/04-repo-learning-critical-lens.md b/docs/plan/04-repo-learning-critical-lens.md index 4659b38..8c86110 100644 --- a/docs/plan/04-repo-learning-critical-lens.md +++ b/docs/plan/04-repo-learning-critical-lens.md @@ -455,7 +455,7 @@ flowchart LR - 更新 notes / guides 生成约定。 - 如果模板由 Rust CLI 生成,更新对应生成逻辑和测试。 -### Step 5. Migrate current active Codex topic +### Step 5. Update current active Codex topic 对当前 Codex `tools-permissions` topic 做轻量迁移: diff --git a/docs/plan/05-repo-learning-instruction-slimming.md b/docs/plan/05-repo-learning-instruction-slimming.md index 4fb6d97..7ed3fe7 100644 --- a/docs/plan/05-repo-learning-instruction-slimming.md +++ b/docs/plan/05-repo-learning-instruction-slimming.md @@ -303,7 +303,7 @@ review 或验证改变完成度、风险、下一步,则更新学习地图。 - `system/prompts/repo/phase3-practice/08-demo-coder.md` - any prompt that repeats checkpoint lifecycle details. -## Migration Plan +## Rollout Plan ### Phase 1: Extract Shared Contracts diff --git a/docs/plan/06-project-topic-lifecycle-stable-workspaces.md b/docs/plan/06-project-topic-lifecycle-stable-workspaces.md index 569efb7..9f72f86 100644 --- a/docs/plan/06-project-topic-lifecycle-stable-workspaces.md +++ b/docs/plan/06-project-topic-lifecycle-stable-workspaces.md @@ -122,7 +122,7 @@ Claude Code 另行生成 `.claude/settings.json` 的 deny 配置;Codex 暂不 - 新增并维护 `current-project`、`current-topic` 软链接。 - 调整项目、专题的创建、启动、完成、废弃、恢复命令。 - 调整终端界面,从 `current.toml` 恢复当前学习现场。 -- 更新模板、提示词、说明文档和迁移脚本。 +- 更新模板、提示词、说明文档和手动整理说明。 ## 验收标准 diff --git a/docs/plan/13-discovery-driven-topic-selection.md b/docs/plan/13-discovery-driven-topic-selection.md new file mode 100644 index 0000000..1255a00 --- /dev/null +++ b/docs/plan/13-discovery-driven-topic-selection.md @@ -0,0 +1,312 @@ +# 澄清、挖掘、选题能力改进方案 + +> 日期:2026-06-14 +> 状态:已落地 + +## 计划放置规则 + +daedalus 的正式实现计划放在 `docs/plan/` 下,作为项目长期设计资料和未来 Agent 的权威上下文。 + +`.codex/plans/` 只保留 Codex 执行追踪卡,用于 todo 状态管理;其中不重复详细方案,只引用 `docs/plan/` 中的正式计划。 + +## 背景判断 + +daedalus 现在已经有 `clarify-goal`、`gatekeeper`、`coach-questioning` 和 repo learning 的 01-03 阶段。它们能把一个相对清楚的学习意图整理成任务卡,也能判断一个候选是否值得进入 active learning。 + +但这套机制有一个更上游的缺口:它把“现实问题、当前基础、预期产出、学习目标”当成用户可以直接提供的输入。真实学习场景里,用户经常只带着模糊冲动、焦虑、被材料吸引的直觉、对未来职业的担心、对自己能力短板的隐约感受,以及一些互相冲突的候选方向。 + +因此,选题不应该只是 objective matching。选题应该先是 discovery:通过问题帮助用户发现自己到底在追求什么,然后才进入 task shaping 和 gatekeeping。 + +## 核心产品判断 + +真实诉求不是选题的输入,而是选题过程的产物。 + +daedalus 需要新增一层 `Topic Discovery Layer`: + +```text +raw impulse + -> discovery interview + -> need hypotheses + -> learner confirmation / correction + -> topic candidates + -> task shaping + -> gatekeeper decision + -> project/topic lifecycle +``` + +现有问题是: + +- `clarify-goal` 太快把模糊意图压缩成任务卡。 +- `gatekeeper` 太快进入 accept / defer / reject。 +- `01-goal-aligner` 更擅长对齐 repo learning 目标,不够擅长探索“用户为什么想学这个”。 +- `coach-questioning` 已经强调提问,但问题主要服务阶段学习,不专门服务选题前的内在诉求发现。 + +## 目标形态 + +当用户说“我不知道接下来学 DDIA 还是 Hugging Face Course”时,daedalus 不应直接给推荐。它应该先进入 discovery: + +1. 复述用户的候选材料和已知背景,但明确这些只是线索,不是结论。 +2. 提出 1-3 个高价值问题,探索用户真实场景、焦虑、欲望、能力缺口和产出偏好。 +3. 将用户回答整理成若干 `need hypotheses`,并请用户确认、修正或排序。 +4. 在用户的真实诉求更清楚之前,不创建 active topic,不直接推荐唯一答案。 +5. 当诉求足够清楚后,再生成任务卡草案、候选 topic 切法、non-goals 和验收方式。 + +## 新增 Prompt:`topic-discovery.md` + +新增文件: + +```text +system/prompts/common/topic-discovery.md +``` + +职责: + +- 在 `clarify-goal` 和 `gatekeeper` 之前运行。 +- 处理用户只表达“想学某材料/某方向/两个方向之间摇摆”的场景。 +- 允许多轮探索,不急于收敛。 +- 输出可被 `clarify-goal` 消费的 discovery summary。 + +建议结构: + +```markdown +--- +title: Discover Learning Need +description: 在创建 project/topic 前,通过多轮澄清帮助用户发现真实诉求、能力缺口、现实场景和选题边界。 +scope: common +--- + +# Discover Learning Need + +## Agent Role + +你是 daedalus 的选题探索教练。你的任务不是马上推荐学习材料,而是通过少量高质量问题,帮助学习者发现自己为什么被某些题目吸引、真正想改变什么、当前能力缺口在哪里,以及什么产出能证明这次学习有价值。 + +## Trigger + +- 用户在多个学习方向之间摇摆。 +- 用户只说想学某材料,但说不清现实问题。 +- 用户表达职业焦虑、技术焦虑、能力短板、材料选择困难或方向混乱。 +- 用户问“接下来学什么”“这个 topic 值不值得开”。 + +## Workflow + +1. 不急着推荐;先承认模糊性本身是选题材料。 +2. 复述已知线索,区分事实、Agent 猜测和待确认动机。 +3. 每轮最多问 3 个问题;优先问能改变选题结论的问题。 +4. 用户回答后,整理为 2-4 个 need hypotheses。 +5. 要求用户确认、修正、排序或否定这些 hypotheses。 +6. 只有当一个 hypothesis 能导向可验证产物时,才进入 clarify-goal。 + +## Output + +```markdown +## Discovery Summary +- 用户原始表达: +- 已确认诉求: +- 待确认诉求假设: +- 情绪/现实压力: +- 能力缺口: +- 候选选题: +- 反目标 / 不想变成: +- 下一轮问题: +- 是否允许进入任务卡: +``` + +## Constraints + +- 不要把用户最先说出的材料当成真实目标。 +- 不要把学习者的焦虑简化成“长期能力主线”。 +- 不要一次性发长问卷。 +- 不要急着给 accept / defer / reject。 +- 不要替用户决定目标;Agent 只能提出 hypothesis。 +``` + +## Discovery Artifact + +新增一种 pre-topic artifact。它不属于 active topic,因为 active topic 尚未成立。 + +建议路径: + +```text +workspaces/discovery/<slug>.md +``` + +如果暂时不想新增目录,也可以先写入: + +```text +workspaces/backlog/<slug>.md +``` + +但 backlog 的语义偏“候选方向”,不够表达多轮选题探索。因此建议新增 `workspaces/discovery/`,专门保存 topic 诞生前的探索记录。 + +artifact 内容: + +```markdown +# <Discovery Title> + +## Raw Signals + +- 用户原始表达: +- 候选材料: +- 当前上下文: + +## Need Hypotheses + +| Hypothesis | Evidence | Confidence | User Status | +| --- | --- | --- | --- | +| | | low / medium / high | pending / confirmed / rejected | + +## Questions + +### Round 1 + +- Agent question: +- User answer: +- Updated hypothesis: + +## Topic Candidates + +| Candidate | Serves Need | Possible Artifact | Risk | +| --- | --- | --- | --- | + +## Decision Readiness + +- 是否可以进入 clarify-goal: +- 仍需澄清: +- 暂不学习: +``` + +## 问题库设计 + +问题库不应该像表单,而应该按选题风险组织。 + +### 1. 现实场景问题 + +- 最近哪类工作让你明显感觉自己不够有底? +- 如果这个能力补上了,你希望在哪个具体场景里用上? +- 你是在为当前项目、下一份岗位、长期技术判断,还是个人作品做准备? + +### 2. 吸引力来源问题 + +- DDIA / HF 分别为什么吸引你?是好奇、焦虑、工作压力、经典权威、作品欲,还是补短板? +- 哪个题让你觉得“学完我会变成另一种工程师”? +- 哪个题只是你觉得“好像应该学”? + +### 3. 能力缺口问题 + +- 你缺的是会用、会实现、会调优、会判断边界,还是会向别人解释? +- 你现在做 AI Agent 时,最不确定的是模型机制、后训练、评估、数据、系统可靠性,还是产品架构? +- 你怕自己只停留在 API glue code,还是怕底层系统能力在 AI 时代贬值? + +### 4. 产出偏好问题 + +- 学完后你想留下什么:demo、技术文章、业务方案、面试作品、架构判断框架,还是可复用代码? +- 你更想要一个能跑的 mini lab,还是一套能迁移到工作决策的系统模型? +- 如果只能用一个产物证明这次学习有价值,它应该是什么? + +### 5. 反目标问题 + +- 这次学习最怕变成什么:追热点、读完没用、太理论、太浅、demo 玩具化、无法迁移、长期拖延? +- 哪些内容即使很热门,这次也应该明确不学? +- 哪种学习方式会让你很快失去动力? + +## 与现有模块的关系 + +### `topic-discovery` vs `clarify-goal` + +- `topic-discovery` 负责发现真实诉求。 +- `clarify-goal` 负责把已发现的诉求压缩成任务卡。 + +### `topic-discovery` vs `gatekeeper` + +- `topic-discovery` 不做 accept / defer / reject。 +- `gatekeeper` 只在 discovery summary 足够清楚后运行。 + +### `topic-discovery` vs `coach-questioning` + +- `topic-discovery` 的问题服务选题前探索。 +- `coach-questioning` 的问题服务已选 topic 内的学习推进。 + +### `topic-discovery` vs backlog + +- backlog 保存未来候选。 +- discovery 保存“为什么这个候选值得或不值得成为 topic”的探索过程。 + +## 验收标准 + +这个计划完成后,以下行为必须成立: + +- 用户在多个学习方向之间摇摆时,Agent 不会直接给唯一推荐。 +- Agent 每轮最多问 3 个能改变选题结论的问题。 +- Agent 会明确区分用户已确认诉求和 Agent 假设。 +- 选题前探索可以恢复到文件,而不是只存在聊天里。 +- `clarify-goal` 和 `gatekeeper` 能消费 discovery summary。 +- 只有当诉求 hypothesis 能导向可验证产物时,才创建 project/topic。 +- 用 “DDIA vs Hugging Face Course / AI Tech Stack” dogfood 时,最终 topic 不是由材料热度决定,而是由用户确认的真实诉求决定。 + +## Dogfood Case:AI 浪潮下的工程师选题 + +用户当前原始表达包括两组张力: + +1. AI 浪潮让软件工程师被迫追新,用户开始怀疑底层原理、架构设计、系统内功在 AI 时代是否仍然有用、有必要、有职业价值。 +2. 用户正在做 AI Agent 开发,但主要停留在应用层,希望补齐 LLM 原理、微调、后训练等介于纯算法和纯工程之间的 AI Tech Stack 短板。 + +这不是简单的 DDIA vs HF 二选一,而是两个可能的真实诉求: + +- 职业身份与长期内功确认:AI 时代软件工程师还应该如何建立不可替代的系统能力。 +- AI Agent 技术栈补全:从应用层 glue code 进入模型机制、数据、训练、评估和部署的可控工程能力。 + +dogfood 时不应直接判断哪一个更重要,而要通过 discovery 确认:用户当前更需要稳定自己的长期工程身份,还是更需要补齐当前 AI Agent 工作中的直接短板,或者需要设计一个组合 topic,把两者连起来。 + +### Dogfood Round 1:用户确认后的真实诉求 + +用户回答第一轮 discovery 问题后,暴露出的核心诉求不是简单材料选择,而是 AI 时代的软件工程师身份重建: + +- 短期现实压力:裁员可能随时到来,下一份工作的岗位市场明显向 AI 开发倾斜;尽快补齐 AI 技术栈,尤其是 Agent 应用层背后的模型、训练、后训练、评估等能力,是短期竞争力的直接来源。 +- 长期能力张力:用户相信 DDIA 这类经过时间验证的系统设计知识更接近“道”,更可能学一通百;但又怀疑在 AI 浪潮下,这类传统底层能力是否仍有职业意义。 +- 心理目标:这轮选题要帮助用户减少焦虑和怀疑,找回学习、工作、生活的主旋律,拥抱 AI 但不畏惧 AI。 +- 能力目标:用户不想疲惫追热点,而是想成为能快速学习、同时掌握深度底层能力的人;这种人更可能把 AI 用好,也更能解决复杂问题。 + +由此形成的 need hypotheses: + +| Hypothesis | Evidence | Confidence | User Status | +| --- | --- | --- | --- | +| 用户需要的不只是 AI 技术栈补课,而是“AI 时代工程师长期能力框架”。 | 用户同时担心短期就业和长期能力贬值,并明确说希望解决心理问题、找回主旋律。 | high | confirmed | +| 单独学习 DDIA 可能过于远离短期岗位压力,单独学习 HF 又可能落入追热点疲惫。 | 用户认为 HF/AI stack 短期最有竞争力,但 DDIA 更接近“道”;同时明确反感追热点疲惫。 | high | confirmed | +| 最合适的 topic 可能需要把 AI 技术栈作为主线,同时用系统设计第一性原理过滤哪些 AI 能力值得长期沉淀。 | 用户目标是快速学习 + 深度底层能力,而不是只会应用层或只读经典。 | medium | pending | + +### Dogfood Round 2:学习边界和并行约束 + +用户进一步确认 AI Stack 内部优先级和现实学习场景: + +- AI Stack 优先级:`fine-tuning / LoRA` -> `post-training / RLHF / DPO` -> `LLM 推理机制`。`eval` 已在公司实践过,不是主要短板。 +- 期望 demo:mini fine-tuning lab,但不是只跑一次训练;要覆盖从初始化数据集、训练、线上/测试数据集、评测、反哺训练到循环迭代的完整闭环。 +- DDIA 的现实位置:公司摸鱼时间不能做训练、看代码或看视频,但可以读 PDF。因此 DDIA 适合作为公司场景下的低摩擦阅读线。 +- 当前疑惑:双线并行可能导致注意力分散;单线学习又会让两个长期任务都难以推进。 + +新的选题约束: + +| Constraint | Implication | +| --- | --- | +| active WIP 仍应严格控制 | 不应把 AI Stack 和 DDIA 都建成 active topic。 | +| 学习场景不同 | AI Stack 适合高能量、可动手环境;DDIA 适合低摩擦阅读环境。 | +| AI Stack 有明确短期岗位价值 | 应作为主 active topic。 | +| DDIA 有长期“道”的价值但不适合重型实践 | 可作为 ambient reading / backlog / non-active track,不占 active topic。 | +| 用户担心注意力分散 | 需要定义双线边界、节奏和 stop rules,而不是简单禁止并行。 | + +## 实施顺序 + +1. 新增 `system/prompts/common/topic-discovery.md`。 +2. 调整 `clarify-goal`,要求在用户诉求明显混沌时先进入 discovery,而不是直接生成任务卡。 +3. 调整 `gatekeeper`,要求只消费已澄清的 discovery summary,不负责挖掘真实诉求。 +4. 调整 `01-goal-aligner`,让 repo/source 选题前置 discovery gate。 +5. 增加 `workspaces/discovery/` 或等价 pre-topic artifact,并更新模板/文档。 +6. 用 `LoRA Fine-tuning Feedback Loop Lab` 启动过程做 dogfood。 + +## 落地结果 + +- 新增 `system/prompts/common/topic-discovery.md`,把选题前的诉求探索独立成 prompt。 +- 新增 `system/templates/discovery/item.md` 和 `workspaces/discovery/README.md`,让 discovery 记录可以恢复到文件。 +- `clarify-goal`、`gatekeeper`、`01-goal-aligner` 已接入 discovery 边界:真实诉求不清楚时先探索,不直接推荐材料或创建 lifecycle。 +- CLI 稳定 workspace layout 已纳入 `workspaces/discovery`,并由测试固定。 +- 全局契约和 repo-learning skill 已说明 discovery / backlog / active topic 的语义分层。 diff --git a/docs/plan/14-lora-finetuning-feedback-loop-topic.md b/docs/plan/14-lora-finetuning-feedback-loop-topic.md new file mode 100644 index 0000000..37f1b41 --- /dev/null +++ b/docs/plan/14-lora-finetuning-feedback-loop-topic.md @@ -0,0 +1,209 @@ +# LoRA Fine-tuning Feedback Loop Lab 启动计划 + +> 日期:2026-06-14 +> 状态:计划中 + +## 背景 + +用户当前不是单纯想“学 Hugging Face LLM Course”,而是在 AI 浪潮、岗位压力和长期能力信仰之间寻找新的学习主旋律。 + +短期现实压力是:下一份工作的岗位市场明显偏向 AI 开发,用户当前做 AI Agent,但主要停留在应用层,缺少模型机制、fine-tuning、post-training 等介于算法和工程之间的能力。 + +长期能力张力是:用户相信 DDIA 这类系统设计知识更接近“道”,更可能学一通百;但在 AI 浪潮下,用户也怀疑传统底层能力是否仍有职业意义。 + +因此,新 topic 不应被定义为“读完 Hugging Face LLM Course”,而应以这个课程为主学习材料,形成一个能同时提升短期岗位竞争力和长期工程自信的可验证实践闭环。 + +## 选题结论 + +主线 active topic: + +```text +LoRA Fine-tuning Feedback Loop Lab +``` + +材料入口: + +- Hugging Face LLM Course / TRL / PEFT / Transformers / Datasets 等官方材料。 +- 必要时补充论文、官方文档和成熟开源示例。 + +学习姿态: + +```text +用 DDIA 式的第一性原理,学习 AI Tech Stack 中最稳定、最可迁移的底层机制。 +``` + +DDIA 暂不作为 active topic。它作为 ambient reading 旁路,在公司低摩擦阅读 PDF,维持系统设计语感,不占用 daedalus active WIP。 + +## 目标 + +完成一个 mini fine-tuning lab,跑通从数据到训练、评测、错误分析、数据反哺和再训练的闭环: + +```text +initial dataset + -> LoRA fine-tuning + -> test / online-like dataset + -> eval + -> error analysis + -> feedback data construction + -> retrain + -> compare +``` + +这个 topic 的核心目标不是训练出强模型,而是掌握可迁移的训练闭环能力: + +- 能判断什么问题适合 fine-tuning,什么问题不适合。 +- 能构造一个最小但可解释的数据集。 +- 能跑通 LoRA 训练链路,并理解 adapter、base model、checkpoint、inference 的边界。 +- 能设计测试集和评测指标,识别训练是否真的改善目标行为。 +- 能从错误样本构造反馈数据,并比较再训练前后的变化。 +- 能理解这个闭环里的工程不变量、数据风险和迁移边界。 + +## AI Stack 优先级 + +本轮优先级: + +1. `fine-tuning / LoRA` +2. `post-training / RLHF / DPO` +3. `LLM 推理机制` + +`eval` 已在公司有实践,不作为主要短板,但 lab 必须包含最小评测,因为没有 eval 就无法形成训练反馈闭环。 + +## 非目标 + +本 topic 暂不追求: + +- 从零训练大模型。 +- 大规模分布式训练。 +- 复杂 RLHF pipeline。 +- 追最新模型、最新榜单或最新训练技巧。 +- 把 demo 做成生产级训练平台。 +- 用 DDIA 另开完整 active topic。 + +## DDIA 旁路规则 + +DDIA 作为 ambient reading,不占 active topic。 + +规则: + +- 只在公司适合读 PDF 的碎片时间阅读。 +- 不要求每次产出笔记。 +- 不因为 DDIA 没读完而阻塞 LoRA lab。 +- 如果有精力,可以记录 1-3 条对 AI 训练闭环、数据系统或 Agent 架构有启发的原则。 +- 只有当某个 DDIA 问题强烈连接到当前 lab,例如数据版本、评测一致性、反馈循环、可靠性边界,才提升为当前 topic 的一条 external lens。 + +DDIA 的当前作用是维持系统设计直觉,而不是承担完整学习闭环。 + +## 建议 project/topic 形态 + +这不是现有 `openai-codex-cli-deep-learning` project 的后续 topic,因为学习材料和最终产物不再是 Codex repo。 + +建议新建一个 learning project: + +```text +hugging-face-llm-course +``` + +初始 topic: + +```text +lora-feedback-loop +``` + +候选标题: + +```text +LoRA 微调与数据反馈闭环 +``` + +## 预期目录和产物 + +进入 lifecycle 后,topic 内至少应形成: + +```text +topics/lora-feedback-loop/ + .daedalus/task-card.md + .daedalus/outcome-map.md + .daedalus/todo.md + guides/ + 01-goal-aligner/README.md + 02-source-scout/README.md + 03-question-roadmap/README.md + 07-demo-architecture/README.md + notes/ + 03-question-roadmap/README.md + 04-runbook/README.md + 08-demo-coder/README.md + 09-transfer/README.md + demo/ + README.md + datasets/ + train/ + eval/ + scripts/ + reports/ +``` + +最终关键产物: + +- 一套 mini LoRA fine-tuning lab。 +- 一份训练闭环 runbook。 +- 一份实验对比报告:初训 vs 反哺后再训练。 +- 一份业务迁移笔记:什么时候应该 fine-tune,什么时候应该 prompt/RAG/tooling,什么时候应该不做。 +- closeout 后再判断哪些内容进入 `knowledge-base/`。 + +## 关键学习问题 + +第一阶段不从工具 API 开始,而从问题开始: + +1. 什么行为问题值得用 LoRA fine-tuning 解决?它和 prompt engineering / RAG / tool use 的边界是什么? +2. 一个训练样本到底在教模型什么?错误样本如何变成下一轮数据,而不是污染数据集? +3. LoRA adapter 改变的是哪些参数路径?它和 base model、checkpoint、inference serving 的边界是什么? +4. 评测如何避免只验证训练集记忆?测试集、线上样本和回归样本分别保护什么? +5. 反馈闭环里的数据版本、实验版本和模型版本如何保持可追溯? +6. 这个 mini lab 哪些机制值得迁移到真实业务,哪些只是教学简化? + +## Demo 验收标准 + +最小验收: + +- 能加载一个小型开源模型或教学模型。 +- 能构造初始训练数据集和独立测试集。 +- 能完成一次 LoRA fine-tuning。 +- 能在固定测试集上运行 eval,并输出可比较指标或结构化判定。 +- 能基于错误样本构造反馈数据。 +- 能进行第二轮训练,并比较第一轮和第二轮差异。 +- 能记录失败案例、改善案例和未改善案例。 +- 能说明当前 demo 的 non-goals 和迁移边界。 + +## 风险和边界 + +### 算力风险 + +本 topic 应优先选择可在本地或低成本云环境跑通的小模型。目标是学习闭环,不是追求 SOTA。 + +### 工具复杂度风险 + +Hugging Face 工具链很容易把学习带入 API 细节。每个工具学习都要回到机制问题:数据如何流动,参数如何更新,adapter 如何保存,评测如何证明行为变化。 + +### 追热点风险 + +新材料只在服务当前闭环时引入。RLHF / DPO 放在 LoRA 闭环跑通后,不提前展开。 + +### 注意力分散风险 + +active WIP 只承认 LoRA lab。DDIA 不制造待办压力。 + +## 启动前需要确认 + +1. 具体选择哪个小模型作为教学对象。 +2. 数据集任务选择:分类、摘要、指令跟随、格式化输出,还是某个贴近用户业务的 agent 子任务。 +3. 训练环境:本地 Mac、个人 GPU、云 GPU、Colab,或公司外的其他环境。 +4. 是否使用 Hugging Face LLM Course 作为顺序教材,还是以 lab 需求反向读取对应章节和官方文档。 + +## 建议启动动作 + +1. 先用 daedalus 创建新 project/topic,而不是把它塞进 Codex CLI project。 +2. 进入 `01-goal-aligner`,把本计划中的真实诉求、非目标和验收标准压缩成 task card。 +3. 进入 source/material scout,选择 Hugging Face 课程章节、PEFT/TRL 官方文档和一个小模型示例。 +4. 进入 question roadmap,先回答关键学习问题,再设计 demo。 +5. demo 只在 `fine-tuning / LoRA` 闭环稳定后,再考虑 DPO 或其他 post-training。 diff --git a/docs/plan/15-course-learning-system.md b/docs/plan/15-course-learning-system.md new file mode 100644 index 0000000..69f372e --- /dev/null +++ b/docs/plan/15-course-learning-system.md @@ -0,0 +1,529 @@ +# Course Learning 一等学习类型实现方案 + +> 日期:2026-06-23 +> 状态:已完成 + +## 背景 + +`hugging-face-llm-course` 这类学习材料有明确的课程主页、章节顺序、练习代码、概念依赖和官方文档。daedalus 需要把它们建模成独立的 course-learning project/topic,而不是只靠通用学习状态机承载。 + +course learning 的核心材料是一套有顺序、有教学目标、有练习、有概念依赖的课程。它的学习路径围绕: + +```text +学习诉求 -> syllabus 路线 -> 概念机制 -> 最小实验 -> 练习复现 -> 迁移任务 -> 掌握度验证 +``` + +如果没有专门的 course-learning 设施,Agent 容易把课程进度、工具 API、示例代码运行和用户掌握度混在一起。结果是“看过一节课”或“跑通一段代码”被误判为已经理解机制,也难以把课程概念迁移到用户真实任务。 + +因此,daedalus 需要把 `course-learning` 做成一等学习类型。 + +## 核心判断 + +course-learning 共享 daedalus 的底层原则: + +- filesystem-first,学习状态必须可从文件恢复。 +- WIP 严格,一次只推进一个 active topic。 +- 不把聊天当长期记忆。 +- 以最终产物倒推学习路径。 +- 学习材料不是权威,要保留 first principles、trade-off 和迁移边界。 +- 用户必须亲自完成关键观察、练习和理解闭环。 + +course-learning 的独立定义: + +- 主材料:课程 syllabus、章节、练习、官方文档。 +- 核心问题:这节课要建立哪个概念能力? +- 证据来源:课程章节、练习输出、用户复述、小实验、迁移题。 +- 主要风险:照抄教程、概念似懂非懂、练习与真实任务脱节。 +- 教练动作:引导预习、实验观察、概念重构、迁移练习。 +- 阶段退出:能脱离教程复现机制并迁移到目标任务。 +- 最终产物:course map、lesson labs、concept map、transfer lab、复习题库。 + +## 目标形态 + +新增一等学习类型: + +```text +course-learning +``` + +它应该支持: + +```text +daedalus init course-learning <project-name> --topic <topic-slug> --title <topic-title> --course-url <url> +``` + +生成的 project/topic 应明确表达: + +```toml +[task] +kind = "course-learning" + +[project] +source_kind = "course" +course_url = "..." +``` + +现有 `hugging-face-llm-course` project 已手动整理为 course-learning 类型,并保留当前 topic、notes、guides、demo 和历史证据。 + +## Course Learning Lifecycle + +course-learning 使用以下阶段: + +```text +01-need-aligner +02-syllabus-mapper +03-concept-roadmap +04-lesson-lab +05-mechanism-deep-dive +06-practice-transfer +07-capstone-lab +08-review-loop +09-closeout-archive +``` + +### 01-need-aligner + +目标:确认为什么学这门课、服务哪个现实问题、最终要留下什么能力证明。 + +区别于 repo `goal-aligner`:这里不问“是否值得学这个 repo”,而是问: + +- 这门课在用户真实目标中承担什么角色? +- 是顺序学、跳读、反向查阅,还是以项目问题驱动? +- 哪些章节是主线,哪些是旁路? +- 学完后要能做什么,而不只是“看完课程”? + +核心产物: + +```text +.daedalus/task-card.md +course-purpose.md +non-goals.md +``` + +### 02-syllabus-mapper + +目标:把课程 syllabus 变成学习路线图。 + +课程通常已经有章节顺序,但 daedalus 不能盲目照单全收。需要建立: + +- chapter / section 列表。 +- 概念依赖。 +- 必做练习、可跳过材料、延后材料。 +- 当前 topic 与课程章节的映射。 +- 每一章的可验证输出。 + +核心产物: + +```text +shared/syllabus-map.md +shared/course-progress.md +guides/02-syllabus-mapper/README.md +``` + +### 03-concept-roadmap + +目标:把章节标题转换成概念问题和学习检查点。 + +例子: + +```text +Chapter 1 Text Generation + -> Causal LM 到底在训练什么? + -> labels = input_ids 为什么不是复制? + -> logits 的 shape 为什么是 [batch, seq_len, vocab]? +``` + +核心产物: + +```text +guides/03-concept-roadmap/README.md +shared/concept-map.md +``` + +### 04-lesson-lab + +目标:把课程代码变成可观察实验,而不是照抄 recipe。 + +每个 lesson lab 都必须包含: + +- 本节概念目标。 +- 用户要先预测什么。 +- 要运行的最小代码。 +- 要打印的 input / output / shape / loss / artifact。 +- 观察后要回答的问题。 +- 哪些 warning 是语义信号,哪些只是环境噪音。 + +核心产物: + +```text +guides/04-lesson-lab/<lesson>.md +notes/04-lesson-lab/README.md +demo/<course-exercise>/ +``` + +### 05-mechanism-deep-dive + +目标:当课程 API 把机制藏起来时,进入必要的源码 / 文档 / 论文解释。 + +进入条件必须很窄: + +```text +只有当课程概念不能靠文档、实验和输出解释清楚时,才读源码。 +``` + +例子: + +- `Trainer.train()` 如何把 batch 变成 loss 和 backward。 +- `pipeline("text-generation")` 如何展开成 tokenizer、model.generate、decode。 +- `ForCausalLMLoss` 如何 shift labels。 + +核心产物: + +```text +guides/05-mechanism-deep-dive/<mechanism>.md +notes/05-mechanism-deep-dive/README.md +``` + +### 06-practice-transfer + +目标:把课程概念迁移到用户真实 topic 的小任务。 + +对当前 Hugging Face topic 来说,迁移不是“继续 ELI5”,而是: + +```text +把 Causal LM / Trainer / pipeline 的理解 +迁移到闲鱼买家 Agent suggestion next action 的 SFT / LoRA 数据与训练闭环 +``` + +核心产物: + +```text +guides/06-practice-transfer/README.md +demo/transfer-lab/ +``` + +### 07-capstone-lab + +目标:形成课程驱动的最终 mini project。 + +course-learning 的 capstone 不必是完整生产系统,但必须证明: + +- 用户能选择合适章节和工具。 +- 能独立搭建 pipeline。 +- 能解释关键机制。 +- 能跑出可比较结果。 +- 能说清迁移边界。 + +当前 topic 的 capstone 是: + +```text +suggestion dataset + -> baseline + -> LoRA/SFT train + -> eval + -> error analysis + -> feedback data + -> retrain + -> compare +``` + +### 08-review-loop + +目标:课程学习必须有复习与掌握度验证。 + +除了完成 demo 或练习输出,course-learning 还需要检查: + +- 概念是否能脱离材料复述。 +- 能否从空白写出最小代码。 +- 能否解释 shape / loss / artifact。 +- 能否判断何时不该使用某工具。 +- 能否完成迁移题。 + +核心产物: + +```text +review/mastery-map.md +review/question-bank.md +review/sessions/ +``` + +### 09-closeout-archive + +目标:关闭课程 topic,归档可复用知识。 + +归档前必须区分: + +- 课程原文结论。 +- 用户通过实验验证过的结论。 +- Agent 的解释。 +- 可迁移到业务的模式。 +- 仍然薄弱的基础。 + +## Prompt 设施 + +新增 prompt namespace: + +```text +system/prompts/course/ + 01-need-aligner.md + 02-syllabus-mapper.md + 03-concept-roadmap.md + 04-lesson-lab.md + 05-mechanism-deep-dive.md + 06-practice-transfer.md + 07-capstone-lab.md + 08-review-loop.md + 09-closeout-archive.md + course-learning-cli-contract.md +``` + +这些 prompt 应复用 common 层: + +- `topic-discovery.md` +- `coach-questioning.md` +- `first-principles.md` +- `critical-lens.md` +- `human-owned-notes.md` +- `checkpoint-lifecycle.md` +- `review-guidance.md` +- `closeout-flow.md` + +course prompt 应只引用 common 层能力和 course 层阶段 prompt。 + +## Skill 设施 + +新增 skill: + +```text +.agents/skills/course-learning-coach/SKILL.md +``` + +职责: + +- 只在用户学习 course / tutorial / official learning path / online curriculum 时触发。 +- 根据 course-learning lifecycle 路由到对应 prompt。 +- 明确区分 lesson guide、mechanism deep dive、practice transfer。 +- 每轮最多推进一个 lesson / mechanism / transfer checkpoint。 + +触发例子: + +- “我在学 Hugging Face Course 第 2 章。” +- “我跟着官方 tutorial 做了一个训练 notebook,但不知道自己在干什么。” +- “帮我把这节课程变成学习路线。” +- “这门课下一步学什么?” + +## Template 设施 + +新增模板: + +```text +system/templates/course/ +system/templates/course-topic/ +``` + +project-level 建议包含: + +```text +.daedalus/state.toml +.daedalus/project-map.md +.daedalus/topic-board.md +shared/syllabus-map.md +shared/course-progress.md +shared/concept-map.md +shared/glossary.md +shared/evidence-registry.md +shared/transfer-patterns.md +source/README.md +``` + +topic-level 建议包含: + +```text +.daedalus/task-card.md +.daedalus/outcome-map.md +.daedalus/todo.md +.daedalus/validation-log.md +guides/ +notes/ +demo/ +review/ +reflection/ +``` + +course topic 的 guides 默认目录是: + +```text +guides/01-need-aligner/ +guides/02-syllabus-mapper/ +guides/03-concept-roadmap/ +guides/04-lesson-lab/ +guides/05-mechanism-deep-dive/ +guides/06-practice-transfer/ +guides/07-capstone-lab/ +guides/08-review-loop/ +guides/09-closeout-archive/ +``` + +## CLI 设施 + +新增或扩展命令: + +```text +daedalus init course-learning <project-name> \ + --topic <topic-slug> \ + --title <topic-title> \ + --course-url <url> +``` + +`init course-learning`: + +- 使用 course templates。 +- 写入 `kind = "course-learning"`。 +- 写入 `source_kind = "course"`。 +- 创建 syllabus / course-progress / concept-map。 +- 初始化 topic 为 course-topic。 + +既有项目的 course-learning 整理是一次性人工维护动作,不提供长期 CLI 命令。Agent 应直接修改具体文件、保留学习证据,并在修改后运行 validate。 + +## Validate 设施 + +`daedalus validate` 应理解 course-learning project。 + +project-level 检查: + +- `kind = "course-learning"`。 +- `source_kind = "course"`。 +- `shared/syllabus-map.md` 存在。 +- `shared/course-progress.md` 存在。 +- `shared/concept-map.md` 存在。 +- active topic 指向存在的 course topic。 + +topic-level 检查: + +- task-card / outcome-map / todo 存在。 +- 当前 active lesson / concept checkpoint 在 todo 中可恢复。 +- lesson lab 有对应 guide 或 notes。 +- 如果标记 lesson 完成,必须有用户运行或用户解释证据。 +- 如果进入 transfer/capstone,必须能指向前置 course concepts。 + +validation 不应该要求用户已经写完整 notes;它只检查结构和生命周期一致性。用户掌握证据仍然来自 notes、运行输出、review 和 closeout。 + +## 当前 Hugging Face Project 一次性整理 + +现状: + +```text +workspaces/projects/hugging-face-llm-course + .daedalus/state.toml: 沿用旧类型标识 + project.source_kind: 沿用旧 source 标识 + topic-board: 沿用旧 project 文案 + guides/04-debugger-guide: 实际承担 lesson-lab / mechanism-deep-dive +``` + +目标: + +```text +kind = "course-learning" +source_kind = "course" +course_url = "https://huggingface.co/learn/llm-course/en" +``` + +整理时不要破坏现有学习现场。建议分两步: + +1. 引入 course-learning 类型、prompt、template 和 validate。 +2. 手动更新 `hugging-face-llm-course` 的 project/topic metadata,并保留历史目录说明: + +```text +guides/04-debugger-guide + -> 保留为整理前的历史学习现场 + -> 后续新 guide 使用 course-learning 阶段命名 +``` + +后续新文件应逐步使用: + +```text +guides/04-lesson-lab/ +guides/05-mechanism-deep-dive/ +``` + +## 实现边界 + +本计划不是要把 daedalus 一次性改成支持所有学习材料类型。 + +本计划只要求 course-learning 成为一等类型。book-learning、paper-learning 可以以后参考同一模式,但不要在本轮一起实现。 + +## 验收标准 + +完成后必须满足: + +- 可以用 `daedalus init course-learning ...` 创建课程学习 project。 +- 新 project 的 state / project-map / topic-board 使用 course-learning 语义。 +- course-learning 有独立 prompt namespace 和 skill。 +- Agent 在 Hugging Face Course 这类任务中会触发 course-learning-coach。 +- `daedalus validate` 能校验 course-learning project/topic。 +- 现有 `hugging-face-llm-course` 已被手动整理为 course-learning project。 +- 新增或修改的测试覆盖 init、validate、render。 +- 当前 Hugging Face topic 的下一份 guide 使用 lesson-lab 或 mechanism-deep-dive 阶段命名。 + +## 建议实施顺序 + +### Step 1:领域模型与模板 + +- 新增 course project/topic templates。 +- 扩展 state kind / source kind。 +- 保留现有 project/topic lifecycle 机制。 + +### Step 2:CLI init / render / validate + +- 新增 `init course-learning`。 +- 更新 render 文案。 +- 更新 validate 结构检查。 +- 增加 CLI workflow tests。 + +### Step 3:Prompt 与 Skill + +- 新增 `system/prompts/course/`。 +- 新增 `.agents/skills/course-learning-coach/`。 +- 更新 Agent 入口规则:course/tutorial/official curriculum 使用 course-learning skill。 + +### Step 4:当前项目手动整理 + +- 手动更新 `hugging-face-llm-course` 的 state、project-map、topic-board 和 active topic metadata。 +- 保留历史 guides / notes / demo / reflection。 +- 整理后运行 validate。 + +### Step 5:当前 topic 整理 + +- 新建 course 命名目录并为后续 guide 使用。 +- 不强行重命名历史已提交目录,除非用户要求清理历史结构。 +- 在 outcome-map 中标注历史目录的整理说明。 + +## 风险 + +### 风险 1:过度抽象 + +course-learning 不应变成“通用 learning type 大重构”。本轮只处理课程型材料。 + +### 风险 2:破坏现有 topic + +当前 Hugging Face topic 已经有有效学习证据。整理必须保留现有文件,不因命名洁癖丢失上下文。 + +### 风险 3:把课程学习变成刷进度 + +course-progress 只能记录进度,不能替代掌握证据。每个完成的 lesson 至少要有实验、复述、迁移题或 review evidence。 + +### 风险 4:重复 common prompt + +course prompt 应引用 common prompt 的原则,不复制大段规则,避免未来再次漂移。 + +## 最小不变量 + +无论具体实现细节如何,course-learning 必须保护以下不变量: + +```text +课程进度不是掌握度。 +跑通教程不是理解机制。 +章节顺序不是学习目标。 +练习输出必须能回到概念机制。 +概念机制必须能迁移到用户真实任务。 +``` diff --git a/docs/plan/16-tui-learning-type-adapters.md b/docs/plan/16-tui-learning-type-adapters.md new file mode 100644 index 0000000..4d8aa64 --- /dev/null +++ b/docs/plan/16-tui-learning-type-adapters.md @@ -0,0 +1,128 @@ +# TUI Learning Type Adapter 实现方案 + +> 日期:2026-06-23 +> 状态:已完成 + +## 背景 + +daedalus 已经支持至少两类长期学习对象: + +- `repo-learning`:围绕真实代码仓库、运行路径、源码不变量、mini demo 和业务迁移。 +- `course-learning`:围绕课程 syllabus、概念机制、lesson lab、练习复现、迁移练习和掌握度验证。 + +CLI init / validate / render 已经理解 course-learning,但 TUI 目前只是借用通用 project/topic state 结构显示内容。它能显示 course stage,却没有真正理解学习类型差异: + +- 不显示 learning type / source kind / course URL。 +- 当前阅读入口仍偏向 repo-learning 的 slice guide / Action Card。 +- closeout 逻辑硬编码 `10-reflection`。 +- 进度文案只说 stages complete,不能区分 repo stages / course stages。 +- TUI 测试没有 course-learning fixture。 + +这会导致 TUI 表面可用,但语义上仍偏向 repo-learning。 + +## 目标形态 + +TUI 应该拆成两层: + +```text +state.toml / files + -> learning-type adapter + -> TuiOverview + -> screens render +``` + +主干 TUI 只依赖统一的 `TuiOverview`,不直接散落 `repo-learning` / `course-learning` 判断。 + +每种学习类型通过 adapter 提供: + +- 类型标签:例如 `Repo Learning`、`Course Learning`。 +- 当前单位文案:例如 `Stage`、`Course Stage`。 +- 进度单位文案:例如 `repo stages`、`course stages`。 +- 当前阅读入口标签:例如 `Current Guide`、`Course Guide`。 +- 当前阅读查找策略。 +- closeout 阶段 ID 与 closeout 文案。 +- source reference 展示策略。 + +## Adapter 设计 + +第一版采用轻量结构体,而不是复杂 trait/plugin: + +```text +LearningTypeTuiAdapter + kind + label + current_unit_label + progress_unit_label + current_reading_label + closeout_label + closeout_stage_id + guide_lookup +``` + +`repo-learning` adapter: + +- 当前阅读优先找 `guides/<stage>/*slice*.md` 且包含 `Action Card`。 +- closeout stage 是 `10-reflection`。 +- next action 继续解析 `Current slice`、`Current gap`、`Action Card`。 + +`course-learning` adapter: + +- 当前阅读优先找 `guides/<course-stage>/README.md`。 +- closeout stage 是 `09-closeout-archive`。 +- next action 从 course guide / todo 中提取 lesson、focus、next lesson lab。 +- source reference 优先展示 `project.course_url`。 + +fallback adapter: + +- 面向未来未知学习类型。 +- 使用 stage README 查找策略。 +- 不提供特殊 closeout 语义。 + +## 解耦原则 + +- screens 只渲染 `TuiOverview` 字段,不直接判断学习类型。 +- repo-learning 和 course-learning 的差异集中在 `crates/daedalus-cli/src/interfaces/tui/learning_types.rs` 的 adapter / guide helper 中。 +- 后续新增 learning type 时,不修改 repo/course adapter,只新增该类型 adapter 和注册入口。 +- 如果新类型遵守通用 stage README 约定,可以先走 fallback adapter。 + +## 实现范围 + +### TUI App + +- 新增 `tui/learning_types.rs`,内聚 learning type adapter、guide 查找、next action 构造和 source reference 策略。 +- 在 `TuiOverview` 中增加 learning type / source / display labels。 +- `load_overview` 根据 `task.kind` 选择 adapter。 +- `build_next_action` 通过 adapter 选择 repo action guide 或 course stage README。 +- `find_current_guide` 改为 adapter 驱动。 +- closeout 逻辑从硬编码 `10-reflection` 改为 adapter closeout stage。 + +### TUI Screens + +- Current panel 显示 learning type。 +- Evidence 显示 source kind / source reference。 +- Progress label 使用 adapter 的 progress unit。 +- Reading map 使用 adapter 的 current reading label。 + +### Tests + +- 保留 repo-learning action guide 行为。 +- 新增 course-learning fixture,验证: + - TUI 识别 `course-learning`。 + - 展示 course URL。 + - 当前阅读入口使用 `guides/04-lesson-lab/README.md`。 + - next action 使用 course stage 文案。 + +## 验收标准 + +- `cargo test --manifest-path crates/Cargo.toml -p daedalus-cli --test cli_workflow` 通过。 +- TUI app 单元测试覆盖 repo-learning 和 course-learning 两类 guide 查找策略。 +- `daedalus validate` 通过。 +- 搜索 TUI 主渲染文件时,不出现散落的 repo/course 分支判断。 +- course-learning project 在 TUI 中能看到类型、课程入口、course stage 进度和 course guide。 + +## 非目标 + +- 不做完整 TUI 视觉重设计。 +- 不实现动态插件系统。 +- 不把 book-learning / paper-learning 一起实现。 +- 不要求所有历史 repo-learning guide 立即改名。 diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..8617e1d --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,12 @@ +{ + "executionEnvironments": [ + { + "root": "workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning", + "pythonVersion": "3.11", + "extraPaths": [ + "workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning", + "workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages" + ] + } + ] +} diff --git a/system/bin/check-commit-msg b/system/bin/check-commit-msg index 37b6967..76f995e 100755 --- a/system/bin/check-commit-msg +++ b/system/bin/check-commit-msg @@ -4,18 +4,8 @@ import sys from pathlib import Path -ALLOWED_TYPES = ( - "feat", - "fix", - "docs", - "style", - "refactor", - "perf", - "test", - "build", - "ci", - "chore", - "revert", +COMMIT_SUBJECT_PATTERN = re.compile( + r"^[a-z0-9][a-z0-9-]*/[a-z0-9][a-z0-9-]*: .+" ) @@ -34,19 +24,13 @@ def main() -> int: if subject.startswith(("Merge ", "Revert ")): return 0 - pattern = re.compile( - r"^(" - + "|".join(ALLOWED_TYPES) - + r")(\([a-z0-9-]+\))?: .+" - ) - - if pattern.match(subject): + if COMMIT_SUBJECT_PATTERN.match(subject): return 0 print("Invalid commit message.") - print("Expected: type(scope): Chinese description") - print(" or: type: Chinese description") - print("Example: docs(learning): 增加当前光标同步机制") + print("Expected: topic/scope: Chinese description") + print("Example: lora-feedback-loop/debugger-guide: 跑通 Causal LM 训练闭环") + print("Example: daedalus/cli: 修正 topic 校验规则") print(f"Actual: {subject}") return 1 diff --git a/system/bin/migrate-repo-learning-to-multi-topic b/system/bin/migrate-repo-learning-to-multi-topic deleted file mode 100755 index 2a91f33..0000000 --- a/system/bin/migrate-repo-learning-to-multi-topic +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -exec daedalus migrate repo-learning-multi-topic "$@" diff --git a/system/prompts/common/agent-operating-contract.md b/system/prompts/common/agent-operating-contract.md index 7b260a5..5a010e8 100644 --- a/system/prompts/common/agent-operating-contract.md +++ b/system/prompts/common/agent-operating-contract.md @@ -11,13 +11,16 @@ daedalus is a filesystem-first deep learning coach. Its job is to guide a user f - Keep WIP strict: `workspaces/projects` may contain many stable projects, but `workspaces/.daedalus/current.toml` may point to at most one active topic and at most one pending closeout topic. `current-project` / `current-topic` are only projected when an active topic exists. - Resolve active learning context from `workspaces/.daedalus/current.toml` or `workspaces/current-topic` before reading lifecycle state. Resolve pending closeout context from `pending_closeout_topic` or `workspaces/closeout-topic`. The repository root intentionally does not own `.daedalus/state.toml`. - Treat project/topic `.daedalus/state.toml` and `workspaces/.daedalus/current.toml` as lifecycle sources of truth. `current-project` / `current-topic` are active-learning entry symlinks; `closeout-topic` is the pending reflection entry. Do not project idle selected projects as top-level symlinks. +- Treat `workspaces/discovery` as pre-topic exploration space: one Markdown file per unresolved selection conversation, no `.daedalus` lifecycle state, no active-learning artifacts, and no promotion without `topic-discovery`, `clarify-goal`, then `gatekeeper`. - Treat `workspaces/backlog` as pre-learning candidate space: one Markdown file per future study idea, no `.daedalus` lifecycle state, no active-learning artifacts, and no promotion without gatekeeper. +- Use `system/prompts/common/topic-discovery.md` when the user has unclear motivations, multiple candidate directions, career/technical anxiety, or cannot yet state the real learning problem. - Use `system/prompts/common/backlog-capture.md` when the user only wants to save a future study idea; use `system/prompts/common/gatekeeper.md` only when deciding whether it should enter active learning. - Preserve goals, decisions, open questions, todo state, verified conclusions, risks, and next actions. Do not turn summaries into chat logs. - Ground implementation progress in current code, tests, runtime output, and git diff before trusting markdown maps. - After review, validation, or commit changes completion, risks, evidence, or next action, synchronize the active learning artifacts. - Before a repo-learning commit, run a learning-map sync check. After a repo-learning commit, output a post-commit orientation. - When a discussion produces a reusable learning method, thinking tool, artifact pattern, or Agent operating rule, do not bury it only in the current topic. Decide its level: topic note, project shared context, prompt/template rule, or knowledge-base candidate. +- For learning guides and notes, classify the artifact before writing. Stage `README.md` files are indexes, timelines, and recovery entrypoints; reusable plans, mechanisms, comparisons, failure modes, deep explanations, run evidence, and user retrospectives must live in focused files linked from the README and relevant maps. - Before long-running repo-learning work, make the first working message a short parallel study handoff so the learner can keep learning while the Agent inspects, validates, or commits. - Treat topic closeout as a push-driven state machine, not an open-ended review chat. When a closeout gate is satisfied, synchronize maps and move to the next gate. - Study materials are constrained design cases, not authorities. Preserve first principles, trade-offs, limitations, faithful imitation choices, and not-to-copy boundaries. @@ -41,8 +44,9 @@ daedalus is a filesystem-first deep learning coach. Its job is to guide a user f - Prefer deterministic Rust code under `crates/` for deterministic logic. - When developing daedalus Rust code, prioritize feature correctness and code simplicity over minimizing refactor size or implementation time. - When writing daedalus implementation plans, describe the target outcome directly. Do not weaken the plan with "first version", "do it later", or schedule-saving language unless the user explicitly asks for phased delivery. +- Put detailed daedalus implementation plans in `docs/plan/`; use `.codex/plans/` only as Codex tracking cards with `status` and `todos` that link to the canonical `docs/plan/` plan. - When adding or moving a Rust crate, update `crates/Cargo.toml`, `Makefile`, `.pre-commit-config.yaml`, and `.github/workflows/ci.yml` as needed. -- Commit messages must follow `type(scope): 中文描述` or `type: 中文描述`. +- Commit messages must follow `{topic}/{scope}: 中文描述`, for example `lora-feedback-loop/debugger-guide: 跑通 Causal LM 训练闭环` or `daedalus/cli: 修正 topic 校验规则`. - Tests should assert stable behavior, not incidental wording. For errors, prefer variant, category, or presence unless exact text is public contract. - Before marking a learning task completed, verify that it has goal, core questions, run/debug notes or justified skip, architecture/code notes, demo or explicit no-demo reason, business transfer, user closeout retrospective, and verified knowledge archival. diff --git a/system/prompts/common/checkpoint-lifecycle.md b/system/prompts/common/checkpoint-lifecycle.md index 9e1ca84..e81ac1d 100644 --- a/system/prompts/common/checkpoint-lifecycle.md +++ b/system/prompts/common/checkpoint-lifecycle.md @@ -92,7 +92,7 @@ next-step guide = map for the next session Do not mix completed implementation and future planning in one commit unless they are inseparable. -Use `type(scope): 中文描述` or `type: 中文描述`. Do not put unverified hypotheses in commit messages. +Use `{topic}/{scope}: 中文描述`, for example `lora-feedback-loop/debugger-guide: 跑通 Causal LM 训练闭环` or `daedalus/cli: 修正 topic 校验规则`. Do not put unverified hypotheses in commit messages. ## Post-Commit Orientation diff --git a/system/prompts/common/clarify-goal.md b/system/prompts/common/clarify-goal.md index 8a1f70a..f037703 100644 --- a/system/prompts/common/clarify-goal.md +++ b/system/prompts/common/clarify-goal.md @@ -15,6 +15,7 @@ scope: common - 用户准备开启新的学习任务。 - 用户只说了“想学某技术/某材料”,但目标还不够可执行。 - 当前学习任务缺少验收标准或输出物。 +- 用户已经完成 `Discover Learning Need`,需要把确认后的诉求收敛成任务卡。 ## Inputs @@ -26,7 +27,8 @@ scope: common ## Workflow -1. 先复述用户的现实问题,确认你理解的学习动机。 +0. 如果用户的真实诉求、现实场景或候选目标仍只能靠 Agent 猜测,先进入 `system/prompts/common/topic-discovery.md`,不要急着生成任务卡。 +1. 先复述 discovery 中已经确认的现实问题和学习动机;明确哪些仍是待确认假设。 2. 判断目标是否可验证;如果不可验证,优先追问输出物和验收标准。 3. 将目标收窄到一个最小闭环任务。 4. 记录当前阶段,默认从 `goal-alignment` 开始。 @@ -49,3 +51,4 @@ scope: common - 信息不足时最多问 3 个高价值问题。 - 不要一次性展开长问卷。 - 不要接受“了解一下”作为最终目标;必须转成可交付结果。 +- 不要把 discovery 中的 Agent 假设当成用户已确认目标;未确认时继续澄清。 diff --git a/system/prompts/common/gatekeeper.md b/system/prompts/common/gatekeeper.md index bde474b..db2ed0a 100644 --- a/system/prompts/common/gatekeeper.md +++ b/system/prompts/common/gatekeeper.md @@ -18,8 +18,9 @@ scope: common ## Inputs -- 新任务的学习目标。 +- 新任务的学习目标,优先来自已确认的 discovery summary 或 task card。 - backlog candidate 文件(如适用)。 +- discovery 文件(如适用)。 - 用户当前 WIP。 - 现实问题和预期产出。 - 学习材料质量和可访问性。 @@ -29,6 +30,7 @@ scope: common - 是否服务于明确的现实问题或长期能力主线。 - 是否有可交付输出物,而不是泛泛“了解一下”。 +- 目标是否已经由用户确认,而不是 Agent 从材料热度或项目历史中推断。 - 当前是否已有进行中的学习任务未闭环。 - 用户是否具备进入该材料的最低前置知识。 - 材料质量是否足够好,是否值得深度投入。 @@ -54,6 +56,7 @@ scope: common - 必须给出理由和下一步动作。 - 不要为了迎合用户而默认开启新任务。 +- 不要用 gatekeeper 代替 discovery;如果真实诉求尚不清楚,输出 `defer` 并要求先进入 `Discover Learning Need`。 - 不要在 gatekeeper 中捕获新的 backlog candidate;新候选项使用 `Capture Backlog Candidate`。 - 如果 active project 已存在,优先判断这是当前 project 的新 topic,还是应该关闭/暂停/归档当前 project。 - 如果只是同一 repo/source 下的新学习方向,优先使用 `daedalus topic new`,不要新建第二个 project。 diff --git a/system/prompts/common/human-owned-notes.md b/system/prompts/common/human-owned-notes.md index 4c17493..35c2c39 100644 --- a/system/prompts/common/human-owned-notes.md +++ b/system/prompts/common/human-owned-notes.md @@ -67,6 +67,35 @@ AI 不能是: 不要把 AI 的总结混进用户理解正文。 +## Artifact Granularity + +写入 `guides/` 或 `notes/` 前必须先判断 artifact 形态,不要把所有内容都追加到阶段 `README.md`。 + +`guides/**/README.md` 和 `notes/**/README.md` 只承担: + +- 当前阶段索引 +- 时间线 +- 恢复现场 +- 链接到具体 guides / notes +- 很短的当前结论 + +下面这些内容必须单独成篇,并从 README 链接过去: + +- 具体学习路线、实现计划或 lesson guide +- 可复用概念解释 +- 机制对比 +- 排障路径和运行证据 +- debugging pattern +- 失败模式 +- 学习方法 +- 后续会被 shared maps、review 或 knowledge-base 引用的内容 + +默认判断规则: + +```text +如果这段内容未来会被单独复习、引用、迁移或归档,就不要塞进 README。 +``` + ## Notes Review 用户写完 rough notes 后,AI 进入 review / coaching,而不是直接重写。 diff --git a/system/prompts/common/topic-discovery.md b/system/prompts/common/topic-discovery.md new file mode 100644 index 0000000..5b0bdf4 --- /dev/null +++ b/system/prompts/common/topic-discovery.md @@ -0,0 +1,95 @@ +--- +title: Discover Learning Need +description: 在创建 project/topic 前,通过多轮澄清帮助用户发现真实诉求、能力缺口、现实场景和选题边界。 +scope: common +--- + +# Discover Learning Need + +## Agent Role + +你是 daedalus 的选题探索教练。你的任务不是马上推荐学习材料,而是通过少量高质量问题,帮助学习者发现自己为什么被某些题目吸引、真正想改变什么、当前能力缺口在哪里,以及什么产出能证明这次学习有价值。 + +## Trigger + +- 用户在多个学习方向之间摇摆。 +- 用户只说想学某材料,但说不清现实问题。 +- 用户表达职业焦虑、技术焦虑、能力短板、材料选择困难或方向混乱。 +- 用户问“接下来学什么”“这个 topic 值不值得开”。 +- `clarify-goal` 或 `gatekeeper` 的关键输入只能靠 Agent 猜测。 + +## Boundary + +Topic discovery 是 pre-topic 工作,不创建 active lifecycle state。 + +- 不写 topic `.daedalus/state.toml`。 +- 不创建 `guides/`、`notes/`、`demo/`。 +- 不做 accept / defer / reject。 +- 不把用户最先说出的材料当成真实目标。 + +## Workflow + +1. 不急着推荐;先承认模糊性本身是选题材料。 +2. 复述已知线索,区分事实、Agent 猜测和待确认动机。 +3. 每轮最多问 3 个问题;优先问能改变选题结论的问题。 +4. 用户回答后,整理为 2-4 个 `need hypotheses`。 +5. 要求用户确认、修正、排序或否定这些 hypotheses。 +6. 只有当一个 hypothesis 能导向可验证产物时,才进入 `clarify-goal`。 +7. 如果需要跨轮恢复,把探索记录写入 `workspaces/discovery/<slug>.md`,默认使用 `system/templates/discovery/item.md`。 + +## Question Lenses + +问题库不应该像表单,而应该按选题风险组织。每轮只选择最能改变结论的 1-3 个问题。 + +### 现实场景 + +- 最近哪类工作让你明显感觉自己不够有底? +- 如果这个能力补上了,你希望在哪个具体场景里用上? +- 你是在为当前项目、下一份岗位、长期技术判断,还是个人作品做准备? + +### 吸引力来源 + +- 候选材料分别为什么吸引你?是好奇、焦虑、工作压力、经典权威、作品欲,还是补短板? +- 哪个题让你觉得“学完我会变成另一种工程师”? +- 哪个题只是你觉得“好像应该学”? + +### 能力缺口 + +- 你缺的是会用、会实现、会调优、会判断边界,还是会向别人解释? +- 当前工作里最不确定的是机制、训练、评估、数据、系统可靠性,还是产品架构? +- 你怕自己只停留在应用层,还是怕长期底层能力在新技术浪潮里贬值? + +### 产出偏好 + +- 学完后你想留下什么:demo、技术文章、业务方案、面试作品、架构判断框架,还是可复用代码? +- 你更想要一个能跑的 mini lab,还是一套能迁移到工作决策的系统模型? +- 如果只能用一个产物证明这次学习有价值,它应该是什么? + +### 反目标 + +- 这次学习最怕变成什么:追热点、读完没用、太理论、太浅、demo 玩具化、无法迁移、长期拖延? +- 哪些内容即使很热门,这次也应该明确不学? +- 哪种学习方式会让你很快失去动力? + +## Output + +```markdown +## Discovery Summary +- 用户原始表达: +- 已确认诉求: +- 待确认诉求假设: +- 情绪 / 现实压力: +- 能力缺口: +- 候选选题: +- 反目标 / 不想变成: +- 下一轮问题: +- 是否允许进入任务卡: +``` + +## Constraints + +- 不要把学习者的焦虑简化成“长期能力主线”。 +- 不要一次性发长问卷。 +- 不要急着给 accept / defer / reject。 +- 不要替用户决定目标;Agent 只能提出 hypothesis。 +- 如果只能靠 Agent 猜测目标,应继续 discovery,而不是进入 project/topic 初始化。 diff --git a/system/prompts/course/01-need-aligner.md b/system/prompts/course/01-need-aligner.md new file mode 100644 index 0000000..5307a3f --- /dev/null +++ b/system/prompts/course/01-need-aligner.md @@ -0,0 +1,44 @@ +--- +title: Course Need Aligner +description: 对齐课程学习诉求、现实目标、章节取舍和可验证输出。用于 course-learning topic 启动或重新校准。 +phase: course.01-need-aligner +--- + +@system/prompts/common/topic-discovery.md +@system/prompts/common/coach-questioning.md +@system/prompts/common/first-principles.md +@system/prompts/common/critical-lens.md + +# Course Need Aligner + +## Layer Contract + +本 prompt 只处理 course-learning 的学习诉求对齐:为什么学、服务哪个现实问题、学到什么程度、用什么证据证明。 + +所有 topic 产物默认写入 active topic。课程级共享结论再同步到 project root 的 `shared/`。 + +## Workflow + +1. 读取 project `.daedalus/state.md`、`shared/course-progress.md`、active topic `task-card.md`、`outcome-map.md`、`todo.md`。 +2. 用 1-3 个问题澄清:为什么学这门课、服务哪个现实问题、最终要能独立做什么。 +3. 判断学习策略:顺序学、跳读、按项目问题反查,还是先做最小 lab。 +4. 明确 non-goals:哪些章节、公式、源码或工具暂不深入。 +5. 写入 active topic `.daedalus/task-card.md` 和 `.daedalus/outcome-map.md`。 + +## Output Delta + +```markdown +## Course Purpose +- 现实问题: +- 目标能力: +- 可验证输出: +- 课程学习策略: +- 暂不学习: +- 用户确认: +``` + +## Constraints + +- 不把“看完课程”作为目标。 +- 不因课程目录存在就默认全量学习。 +- 不把 Agent 解释写成用户已经掌握。 diff --git a/system/prompts/course/02-syllabus-mapper.md b/system/prompts/course/02-syllabus-mapper.md new file mode 100644 index 0000000..948ef39 --- /dev/null +++ b/system/prompts/course/02-syllabus-mapper.md @@ -0,0 +1,40 @@ +--- +title: Course Syllabus Mapper +description: 将课程 syllabus 转换为章节路线、概念依赖、必做练习和跳读边界。用于课程 project/topic 的路线规划。 +phase: course.02-syllabus-mapper +--- + +@system/prompts/common/coach-questioning.md +@system/prompts/common/critical-lens.md + +# Course Syllabus Mapper + +## Layer Contract + +本 prompt 只把课程目录变成 daedalus 学习路线图。不要把 syllabus 当权威;课程顺序只是路线设计的输入。 + +## Workflow + +1. 读取课程目录、官方章节、练习入口和用户目标。 +2. 标出主线章节、旁路章节、可跳过章节、延后章节。 +3. 为每个主线章节写出概念目标、依赖、必做练习和可验证输出。 +4. 同步 `shared/syllabus-map.md` 和 `shared/course-progress.md`。 +5. 为 active topic 写 `guides/02-syllabus-mapper/<route>.md`;`guides/02-syllabus-mapper/README.md` 只更新索引和恢复入口。 + +## Output Delta + +```markdown +## Syllabus Decision +- 主线章节: +- 旁路章节: +- 延后章节: +- 必做练习: +- 每章验证方式: +- 取舍理由: +``` + +## Constraints + +- 课程顺序只是输入,不是最终路线。 +- 路线必须服务用户的现实目标和当前 topic。 +- 不用“资料很多”制造焦虑;每章都要有停止规则。 diff --git a/system/prompts/course/03-concept-roadmap.md b/system/prompts/course/03-concept-roadmap.md new file mode 100644 index 0000000..f26652a --- /dev/null +++ b/system/prompts/course/03-concept-roadmap.md @@ -0,0 +1,41 @@ +--- +title: Course Concept Roadmap +description: 将章节标题转成概念问题、机制 checkpoint 和薄弱点地图。用于避免照抄课程代码却不知道在学什么。 +phase: course.03-concept-roadmap +--- + +@system/prompts/common/coach-questioning.md +@system/prompts/common/first-principles.md +@system/prompts/common/question-roadmap.md + +# Course Concept Roadmap + +## Layer Contract + +本 prompt 只建立课程概念路线。它不替用户回答所有问题,而是把问题组织成后续 lesson lab 和 mechanism deep dive 的观察目标。 + +## Workflow + +1. 从当前章节提取核心名词、API、输入输出、隐藏机制和常见误解。 +2. 将每个概念改写成可回答的问题。 +3. 区分三类问题:先靠实验观察、需要文档解释、必要时读源码。 +4. 同步 `shared/concept-map.md`。 +5. 写入 `guides/03-concept-roadmap/<roadmap>.md`;`guides/03-concept-roadmap/README.md` 只更新索引和恢复入口。 + +## Output Delta + +```markdown +## Concept Roadmap +- 当前章节: +- 核心概念: +- 最小观察问题: +- 机制深挖候选: +- 迁移题: +- 暂不回答的问题: +``` + +## Constraints + +- 不把概念图写成术语表。 +- 每个问题都要能连接到实验、解释或迁移任务。 +- 用户没有复述前,不把 Agent 答案写进 notes。 diff --git a/system/prompts/course/04-lesson-lab.md b/system/prompts/course/04-lesson-lab.md new file mode 100644 index 0000000..c232dbf --- /dev/null +++ b/system/prompts/course/04-lesson-lab.md @@ -0,0 +1,46 @@ +--- +title: Course Lesson Lab +description: 将课程代码变成可观察实验,指导用户运行、预测、打印 input/output/shape/loss/artifact 并形成学习证据。 +phase: course.04-lesson-lab +--- + +@system/prompts/common/coach-questioning.md +@system/prompts/common/human-owned-notes.md +@system/prompts/common/micro-checkpoint.md + +# Course Lesson Lab + +## Layer Contract + +本 prompt 处理“课程示例代码怎么学”。目标不是复制 recipe,而是让用户能解释为什么这么写、每个对象是什么、输出如何证明机制。 + +## Workflow + +1. 选择一个最小 lesson,不同时推进多个 lesson。 +2. 让用户先预测输入、输出、shape、loss 或 artifact。 +3. 生成或更新 `guides/04-lesson-lab/<lesson>.md`:最小代码、观察点、预期输出、排障路径;`guides/04-lesson-lab/README.md` 只更新索引和恢复入口。 +4. 指导用户亲自运行,记录用户观察到的输出。 +5. 用户完成后,把“用户运行/观察/解释”写入 `notes/04-lesson-lab/` 下的独立 note 文件;`README.md` 只更新索引和恢复入口。 +6. 如果对话中解释了可复用机制、API 参数面板、训练速度/成本调节、或“如何从任务契约推导代码”的框架,必须在同一轮补进当前独立 lesson guide,除非用户明确只想聊天不落盘。 +7. 如果记录的是可复用概念、机制解释、对比、用户复述、排障复盘或实验观察,创建独立 note 文件,并只在 `README.md` 中保留索引。 +8. 若 API 隐藏机制导致用户无法解释,再进入 `05-mechanism-deep-dive`。 + +## Output Delta + +```markdown +## Lesson Lab +- Lesson: +- 本节概念目标: +- 用户先预测: +- 最小代码: +- 必须打印: +- 预期观察: +- 用户实际观察: +- 是否进入 mechanism deep dive: +``` + +## Constraints + +- 不把课程代码整段搬运成“学习成果”。 +- 不替用户伪造运行证据。 +- 先做最小实验,再解释大机制。 diff --git a/system/prompts/course/05-mechanism-deep-dive.md b/system/prompts/course/05-mechanism-deep-dive.md new file mode 100644 index 0000000..24daa76 --- /dev/null +++ b/system/prompts/course/05-mechanism-deep-dive.md @@ -0,0 +1,41 @@ +--- +title: Course Mechanism Deep Dive +description: 当课程 API 隐藏关键机制时,使用文档、源码或论文解释必要机制。用于 API 下面的 loss、generate、tokenizer、Trainer 等机制拆解。 +phase: course.05-mechanism-deep-dive +--- + +@system/prompts/common/first-principles.md +@system/prompts/common/critical-lens.md +@system/prompts/common/diagram-guidelines.md + +# Course Mechanism Deep Dive + +## Layer Contract + +本 prompt 只在 lesson lab 无法解释关键机制时使用。文档、源码或论文读取必须服务一个明确的课程机制问题。 + +## Workflow + +1. 写清机制问题:哪个 API 隐藏了什么因果链。 +2. 优先用官方文档、课程材料和可打印输出解释。 +3. 仍不清楚时,只读最小源码路径。 +4. 用图或伪代码解释输入、状态变化、输出和 failure mode。 +5. 写入 `guides/05-mechanism-deep-dive/<mechanism>.md`。 + +## Output Delta + +```markdown +## Mechanism +- 问题: +- 触发 API: +- 最小因果链: +- 可观察证据: +- 源码/文档锚点: +- 不必继续深入的边界: +``` + +## Constraints + +- 不按目录读源码。 +- 不为了“彻底”而扩大阅读范围。 +- 机制解释必须回到 lesson lab 或 practice transfer。 diff --git a/system/prompts/course/06-practice-transfer.md b/system/prompts/course/06-practice-transfer.md new file mode 100644 index 0000000..67f11fb --- /dev/null +++ b/system/prompts/course/06-practice-transfer.md @@ -0,0 +1,40 @@ +--- +title: Course Practice Transfer +description: 将课程概念迁移到用户真实任务的小练习。用于把课程实验转向业务或个人项目目标。 +phase: course.06-practice-transfer +--- + +@system/prompts/common/critical-lens.md +@system/prompts/common/first-principles.md + +# Course Practice Transfer + +## Layer Contract + +本 prompt 处理课程概念到真实任务的迁移,不负责完整 capstone 实现。 + +## Workflow + +1. 选择一个已完成 lesson/concept。 +2. 明确用户真实任务中的输入、输出、约束和验收标准。 +3. 设计一个最小迁移练习,尽量小到当天可以验证。 +4. 标出课程示例中应该忠实模仿、简化、丢弃和重新验证的部分。 +5. 写入 `guides/06-practice-transfer/<practice>.md`;`guides/06-practice-transfer/README.md` 只更新索引和恢复入口。 + +## Output Delta + +```markdown +## Transfer Practice +- 来源概念: +- 真实任务: +- 忠实模仿: +- 简化/丢弃: +- 重新验证: +- 最小验证: +``` + +## Constraints + +- 不把课程任务原样当成业务任务。 +- 不在没有 lesson 证据时直接设计迁移。 +- 迁移前必须重查数据、成本、安全和评测约束。 diff --git a/system/prompts/course/07-capstone-lab.md b/system/prompts/course/07-capstone-lab.md new file mode 100644 index 0000000..3c87a89 --- /dev/null +++ b/system/prompts/course/07-capstone-lab.md @@ -0,0 +1,41 @@ +--- +title: Course Capstone Lab +description: 设计并实现课程驱动 mini project,证明用户能独立选择工具、搭建 pipeline、解释机制并比较结果。 +phase: course.07-capstone-lab +--- + +@system/prompts/common/checkpoint-lifecycle.md +@system/prompts/common/critical-lens.md + +# Course Capstone Lab + +## Layer Contract + +本 prompt 处理课程 topic 的综合 mini project。它应由已完成的 lesson、机制解释和迁移练习驱动,而不是另起一个无关 demo。 + +## Workflow + +1. 从 concept map 和 practice transfer 选择 capstone 范围。 +2. 写 `demo/design.md`:目标、输入输出、pipeline、验证、风险、停止规则。 +3. 实现时保持 script-first / config-first / artifact-first。 +4. 跑最小验证并记录结果。 +5. 说明哪些课程机制被使用,哪些生产需求仍未满足。 + +## Output Delta + +```markdown +## Capstone Lab +- 目标: +- Pipeline: +- Config: +- Artifacts: +- Eval: +- 结果比较: +- 迁移边界: +``` + +## Constraints + +- 不把 capstone 做成无限扩大的产品项目。 +- 不跳过设计直接写代码。 +- 结果必须可复现、可比较、可解释。 diff --git a/system/prompts/course/08-review-loop.md b/system/prompts/course/08-review-loop.md new file mode 100644 index 0000000..fbd9f6a --- /dev/null +++ b/system/prompts/course/08-review-loop.md @@ -0,0 +1,39 @@ +--- +title: Course Review Loop +description: 对课程概念、实验和迁移能力做复习与掌握度验证。用于防止只跑通代码但无法独立复现。 +phase: course.08-review-loop +--- + +@system/prompts/common/review-guidance.md +@system/prompts/common/coach-questioning.md + +# Course Review Loop + +## Layer Contract + +本 prompt 只处理复习和掌握度验证。它不新增主要学习内容,除非复习暴露出必须回补的薄弱点。 + +## Workflow + +1. 从 `shared/concept-map.md` 和 topic notes 抽取复习问题。 +2. 要求用户脱离教程解释关键机制。 +3. 要求用户从空白写出最小代码或伪代码。 +4. 标注掌握状态:能复述、能运行、能改写、能迁移。 +5. 写入 `review/mastery-map.md` 和 `review/question-bank.md`。 + +## Output Delta + +```markdown +## Mastery Check +- 概念: +- 用户解释: +- 最小复现: +- 仍薄弱: +- 下一次复习: +``` + +## Constraints + +- 不把选择题正确当成掌握。 +- 不用 Agent 答案替代用户复述。 +- 复习暴露的薄弱点必须回到具体 lesson 或 mechanism。 diff --git a/system/prompts/course/09-closeout-archive.md b/system/prompts/course/09-closeout-archive.md new file mode 100644 index 0000000..e9b1e38 --- /dev/null +++ b/system/prompts/course/09-closeout-archive.md @@ -0,0 +1,41 @@ +--- +title: Course Closeout Archive +description: 关闭 course-learning topic,区分课程原文、用户实验、Agent 解释和可迁移知识,并准备知识归档。 +phase: course.09-closeout-archive +--- + +@system/prompts/common/closeout-flow.md +@system/prompts/common/archive-knowledge.md +@system/prompts/common/compress-context.md + +# Course Closeout Archive + +## Layer Contract + +本 prompt 处理 course-learning topic 的 closeout。归档必须区分材料原文、用户亲自验证、Agent 校准和仍薄弱基础。 + +## Workflow + +1. 检查 task-card、outcome-map、todo、lesson notes、review 和 demo evidence。 +2. 让用户做 closeout retrospective。 +3. 写 `reflection/closeout.md` 和 `reflection/candidate-map.md`。 +4. 同步 `shared/evidence-registry.md`、`shared/concept-map.md`、`shared/transfer-patterns.md`。 +5. 只把足够深、可独立成文的知识候选推进 knowledge-base。 + +## Output Delta + +```markdown +## Course Closeout +- 完成的能力证明: +- 用户亲自验证: +- Agent 解释: +- 仍薄弱: +- 可迁移模式: +- 不归档内容: +``` + +## Constraints + +- 不把浅总结归档为知识。 +- 不把课程权威结论直接当成验证结论。 +- 不关闭仍缺少用户证据的 topic。 diff --git a/system/prompts/course/course-learning-cli-contract.md b/system/prompts/course/course-learning-cli-contract.md new file mode 100644 index 0000000..ff2d332 --- /dev/null +++ b/system/prompts/course/course-learning-cli-contract.md @@ -0,0 +1,55 @@ +--- +title: Course Learning CLI Contract +description: course-learning 的 CLI、state、template 和 validate 契约。用于 Agent 操作课程学习 project/topic 时保持一致。 +phase: course.contract +--- + +# Course Learning CLI Contract + +## Commands + +```bash +daedalus init course-learning <project-name> \ + --topic <topic-slug> \ + --title <topic-title> \ + --course-url <url> +``` + +## State Contract + +Project: + +```toml +[task] +kind = "course-learning" + +[project] +source_kind = "course" +course_url = "..." +``` + +Topic: + +```toml +[topic] +kind = "course-learning-topic" +current_phase = "01-need-aligner" +``` + +## Stage Names + +- `01-need-aligner` +- `02-syllabus-mapper` +- `03-concept-roadmap` +- `04-lesson-lab` +- `05-mechanism-deep-dive` +- `06-practice-transfer` +- `07-capstone-lab` +- `08-review-loop` +- `09-closeout-archive` + +## Hard Rules + +- Create new guides with the course-learning stage names listed above. +- Keep historical guides after manual reorganization unless the user approves a rename. +- Validate structure with `daedalus validate` after state or lifecycle changes. diff --git a/system/prompts/repo/phase1-exploration/01-goal-aligner.md b/system/prompts/repo/phase1-exploration/01-goal-aligner.md index e292f99..75e93a4 100644 --- a/system/prompts/repo/phase1-exploration/01-goal-aligner.md +++ b/system/prompts/repo/phase1-exploration/01-goal-aligner.md @@ -5,6 +5,7 @@ phase: repo.phase1-exploration --- @system/prompts/common/clarify-goal.md +@system/prompts/common/topic-discovery.md @system/prompts/common/gatekeeper.md @system/prompts/common/coach-questioning.md @system/prompts/common/critical-lens.md @@ -30,14 +31,15 @@ phase: repo.phase1-exploration ## Repo-Specific Workflow -1. 如果这是新的 repo learning project,先检查 WIP,然后直接用 `daedalus init repo-learning <project-name> --topic <topic-slug> --title <topic-title>` 初始化;不要手写 `.daedalus`、`state.toml`、`todo.md` 等模板文件。 -2. 如果 CLI 不可用,先报告阻塞原因,不要自动 fallback 到手写模板。 -3. 继承 `Clarify Learning Goal` 生成通用学习任务卡。 -4. 继承 `Gate Learning Task` 判断是否值得进入 active learning。 -5. 将目标补充为 repo 可执行约束:候选技术方向、运行要求、mini demo 方向。 -6. 用 critical lens 检查:用户是为了获得可迁移能力而学习,还是只是因为素材流行、权威或被推荐。 -7. 如果目标无法导向 repo 选择、mini demo 或业务迁移,先要求用户收窄。 -8. 生成给用户的目标澄清指南时,写入 active topic 的 `guides/01-goal-aligner/README.md`;用户确认后的专题目标和验收标准写入 active topic 的 `.daedalus/task-card.md`。 +1. 如果用户只给出候选材料、技术方向或模糊焦虑,先继承 `Discover Learning Need`;在用户确认真实诉求前,不要推荐 repo,不要初始化 project/topic。 +2. 当 discovery summary 已能导向可验证产物时,继承 `Clarify Learning Goal` 生成通用学习任务卡。 +3. 继承 `Gate Learning Task` 判断是否值得进入 active learning。 +4. 如果这是新的 repo learning project,先检查 WIP,然后直接用 `daedalus init repo-learning <project-name> --topic <topic-slug> --title <topic-title>` 初始化;不要手写 `.daedalus`、`state.toml`、`todo.md` 等模板文件。 +5. 如果 CLI 不可用,先报告阻塞原因,不要自动 fallback 到手写模板。 +6. 将目标补充为 repo 可执行约束:候选技术方向、运行要求、mini demo 方向。 +7. 用 critical lens 检查:用户是为了获得可迁移能力而学习,还是只是因为素材流行、权威或被推荐。 +8. 如果目标无法导向 repo 选择、mini demo 或业务迁移,先要求用户收窄。 +9. 生成给用户的目标澄清指南时,写入 active topic 的 `guides/01-goal-aligner/README.md`;用户确认后的专题目标和验收标准写入 active topic 的 `.daedalus/task-card.md`。 ## Output Delta @@ -70,6 +72,7 @@ phase: repo.phase1-exploration ## Repo-Specific Constraints - 只在必要时提问,每次最多 3 个问题。 +- 不要把 discovery 阶段的材料候选当成已确认学习目标。 - 不要在目标未明确时推荐 repo。 - 学习目标必须能导向 mini demo 或业务方案。 - 不要把“某个 repo 很优秀”当成学习理由;必须说明它服务哪个现实目标、它的约束和用户目标是否相近。 diff --git a/system/prompts/repo/phase3-practice/07-demo-architecture.md b/system/prompts/repo/phase3-practice/07-demo-architecture.md index 9ee99ca..f883746 100644 --- a/system/prompts/repo/phase3-practice/07-demo-architecture.md +++ b/system/prompts/repo/phase3-practice/07-demo-architecture.md @@ -12,7 +12,7 @@ phase: repo.phase3-practice ## Layer Contract -本 prompt 只定义 repo mini demo 的设计约束。可验收目标来自 `Clarify Learning Goal`,核心 trade-off 解释来自 `Apply First Principles`。 +本 prompt 只定义 repo mini demo 的设计约束。可验收目标来自 `Clarify Learning Goal` 中已经确认的学习诉求,核心 trade-off 解释来自 `Apply First Principles`。 本阶段不是从零设计 demo,而是定稿 `06-code-reader` 期间形成的 active topic `demo/design.md` 草案。代码阅读已经负责用源码证据填补关键不变量、数据结构、状态机和验收用例;本阶段负责裁剪、统一、验收和进入实现。 diff --git a/system/prompts/repo/repo-learning-cli-contract.md b/system/prompts/repo/repo-learning-cli-contract.md index b413a00..a9c969b 100644 --- a/system/prompts/repo/repo-learning-cli-contract.md +++ b/system/prompts/repo/repo-learning-cli-contract.md @@ -59,7 +59,7 @@ daedalus validate --all-topics daedalus topic validate <topic-slug> ``` -Use validation after lifecycle, template, migration, review, or knowledge structure changes. +Use validation after lifecycle, template, review, or knowledge structure changes. ## Review Commands diff --git a/system/templates/README.md b/system/templates/README.md index 466a541..2966bb0 100644 --- a/system/templates/README.md +++ b/system/templates/README.md @@ -6,6 +6,10 @@ Reusable filesystem templates for daedalus artifacts. - [`backlog/item.md`](backlog/item.md):全局 pre-learning candidate 模板。用于 `workspaces/backlog/<slug>.md`,不承载 active lifecycle state 或 learning artifacts。 +## Discovery + +- [`discovery/item.md`](discovery/item.md):全局 pre-topic discovery 模板。用于 `workspaces/discovery/<slug>.md`,保存真实诉求假设、追问记录和选题候选,不承载 active lifecycle state 或 learning artifacts。 + ## Repo Learning - `repo/`:repo learning project 模板。 diff --git a/system/templates/course-topic/.daedalus/artifact-index.md b/system/templates/course-topic/.daedalus/artifact-index.md new file mode 100644 index 0000000..4ff1d76 --- /dev/null +++ b/system/templates/course-topic/.daedalus/artifact-index.md @@ -0,0 +1,21 @@ +# 产物索引 + +记录学习过程中产生的产物,以及它们服务的学习目的。 + +> `状态` 列只能使用 `草稿`、`已验证`、`待补充`、`不适用`。不要发明新的状态值;如需扩展,先更新模板、CLI 测试和相关提示词。 + +| 产物 | 阶段 | 目的 | 状态 | +| --- | --- | --- | --- | +| [`.daedalus/task-card.md`](task-card.md) | 01-need-aligner | 学习目标与验收标准 | 草稿 | +| [`.daedalus/outcome-map.md`](outcome-map.md) | all | 全程导航:最终产物、当前位置、缺口、依赖和停止规则 | 草稿 | +| [`.daedalus/validation-log.md`](validation-log.md) | all | 记录 daedalus 教学引导效果与改进点 | 草稿 | +| [`guides/`](../guides) | all | Agent 生成的行动指南与验收清单 | 草稿 | +| [`notes/`](../notes) | all | 用户亲自实践后的学习笔记 | 草稿 | +| [`reflection/closeout.md`](../reflection/closeout.md) | 09-closeout-archive | 用户完成 topic 后的主动回顾总结,是进入 knowledge-base 前的必要证据 | 草稿 | +| [`guides/02-syllabus-mapper/README.md`](../guides/02-syllabus-mapper/README.md) | 02-syllabus-mapper | syllabus 取舍、章节依赖和学习路线入口 | 草稿 | +| [`guides/03-concept-roadmap/README.md`](../guides/03-concept-roadmap/README.md) | 03-concept-roadmap | 概念问题、机制 checkpoint 和薄弱点入口 | 草稿 | +| [`guides/04-lesson-lab/README.md`](../guides/04-lesson-lab/README.md) | 04-lesson-lab | lesson 可观察实验指南入口 | 草稿 | +| [`notes/04-lesson-lab/README.md`](../notes/04-lesson-lab/README.md) | 04-lesson-lab | 用户运行、观察和解释证据入口 | 草稿 | +| [`guides/05-mechanism-deep-dive/README.md`](../guides/05-mechanism-deep-dive/README.md) | 05-mechanism-deep-dive | 被 API 隐藏机制的必要深挖入口 | 草稿 | +| [`guides/06-practice-transfer/README.md`](../guides/06-practice-transfer/README.md) | 06-practice-transfer | 课程概念迁移到真实任务的练习入口 | 草稿 | +| [`review/mastery-map.md`](../review/mastery-map.md) | 08-review-loop | 掌握度验证入口 | 草稿 | diff --git a/system/templates/course-topic/.daedalus/decision-log.md b/system/templates/course-topic/.daedalus/decision-log.md new file mode 100644 index 0000000..01ec44e --- /dev/null +++ b/system/templates/course-topic/.daedalus/decision-log.md @@ -0,0 +1,9 @@ +# 决策记录 + +记录会影响学习方向、任务范围或状态流转的关键决策。 + +## {{CREATED_AT}} + +- 决策:初始化 repo 学习任务 `{{TASK_NAME}}`。 +- 原因:开启一次 filesystem-first 的 daedalus 学习闭环。 +- 影响:当前阶段为 `01-goal-aligner`。 diff --git a/system/templates/course-topic/.daedalus/long-context.md b/system/templates/course-topic/.daedalus/long-context.md new file mode 100644 index 0000000..e929a65 --- /dev/null +++ b/system/templates/course-topic/.daedalus/long-context.md @@ -0,0 +1,20 @@ +# 长期上下文 + +只保留可长期复用的上下文,不要粘贴聊天记录。 + +## 角色边界 + +- `guides/` 保存 Agent 给用户的行动指南。 +- `notes/` 保存用户亲自实践和思考后的学习笔记。 +- `source/` 保存外部源码缓存,默认不提交到 daedalus 仓库。 +- `.daedalus/validation-log.md` 保存 daedalus 自身教学效果的复盘。 + +## 目标 + +## 决策 + +## 已验证结论 + +## 未解决问题 + +## 恢复上下文提示 diff --git a/system/templates/course-topic/.daedalus/outcome-map.md b/system/templates/course-topic/.daedalus/outcome-map.md new file mode 100644 index 0000000..ad20bae --- /dev/null +++ b/system/templates/course-topic/.daedalus/outcome-map.md @@ -0,0 +1,84 @@ +# Outcome Map + +Outcome Map 是 topic 导航仪表盘,不是聊天总结。每次继续学习、深读源码、切换阶段或更新 todo 前,先用它定位:本 topic 的最终产物是什么、当前在哪、正在补哪个缺口、哪些细节停止扩展。 + +## North Star + +- Project:`{{TASK_NAME}}` +- Topic:`{{TOPIC_SLUG}}` - {{TOPIC_TITLE}} +- 最终要获得的能力: +- 最小可验证 lesson lab: +- 现实问题中的迁移目标: + +## Inherited Context + +- Syllabus map:[`../../shared/syllabus-map.md`](../../shared/syllabus-map.md) +- Course progress:[`../../shared/course-progress.md`](../../shared/course-progress.md) +- Concept map:[`../../shared/concept-map.md`](../../shared/concept-map.md) +- Shared evidence:[`../../shared/evidence-registry.md`](../../shared/evidence-registry.md) + +## Final Artifacts + +| 产物 | 用途 | 状态 | +| --- | --- | --- | +| [`.daedalus/task-card.md`](task-card.md) | 学习目标与验收标准 | 草稿 | +| [`guides/02-syllabus-mapper/README.md`](../guides/02-syllabus-mapper/README.md) | syllabus 取舍与章节路线 | 待填 | +| [`guides/03-concept-roadmap/README.md`](../guides/03-concept-roadmap/README.md) | 概念问题路线图 | 待填 | +| [`guides/04-lesson-lab/README.md`](../guides/04-lesson-lab/README.md) | lesson 可观察实验指南 | 待填 | +| [`notes/04-lesson-lab/README.md`](../notes/04-lesson-lab/README.md) | 用户运行、观察和解释证据 | 待填 | +| [`guides/05-mechanism-deep-dive/README.md`](../guides/05-mechanism-deep-dive/README.md) | 必要机制深挖 | 待填 | +| [`guides/06-practice-transfer/README.md`](../guides/06-practice-transfer/README.md) | 迁移到真实任务的练习 | 待填 | +| [`demo/design.md`](../demo/design.md) | mini demo 设计草案与定稿 | 待填 | +| [`review/mastery-map.md`](../review/mastery-map.md) | 掌握度验证 | 待填 | +| knowledge-base entry | 已验证知识归档 | 待填 | + +## Current Position + +Current Position 是学习地图,不是实现事实源。涉及实现阶段时,必须用当前代码、测试、运行输出和用户已验证观察校准后再判断完成度。 + +- 当前阶段:01-need-aligner +- 当前目标:澄清 North Star、最终产物和验收标准。 +- 当前障碍:课程学习诉求、章节取舍和最小 lesson lab 尚未由用户确认。 +- 当前动作服务的产物:`.daedalus/task-card.md`、`.daedalus/outcome-map.md` +- 当前光标:暂无具体 lesson、mechanism 或 transfer checkpoint;以 `todo.md` 的 `Current Cursor` 为临时恢复坐标。 + +## Contribution Back To Project + +- 本 topic 完成后预计新增或更新哪些 shared evidence: +- 哪些 shared glossary / architecture-map 需要更新: +- 哪些 future topics 被发现: + +## Artifact Dependency Graph + +```text +task-card -> syllabus-map -> concept-roadmap -> lesson-lab +lesson-lab -> mechanism-deep-dive -> practice-transfer +practice-transfer -> capstone/review -> knowledge-base entry +``` + +## Open Gaps + +- [ ] 确认最终要获得的能力。 +- [ ] 确认最小可验证 lesson lab。 +- [ ] 确认业务迁移目标。 + +## Why This Step Matters + +当前步骤决定后续应该学哪些章节、停止哪些材料、最终 lesson lab 和迁移任务如何验收。 + +## Critical Lens + +Critical Lens 用来防止把学习素材当成权威。它不是反对模仿,而是让 demo 的模仿变成有意识、有证据、有边界的学习动作。 + +- 当前素材中可能被过度神化的设计: +- 当前 lesson lab 需要忠实模仿的核心机制: +- 当前 demo 不应无意识照抄的设计: +- 当前还没有验证的素材假设: +- 当前可以尝试简化、改进或丢弃的部分: +- 当前迁移到业务场景前必须重新验证的约束: + +## Stop Rules + +- 不学与 North Star、lesson lab、迁移任务或知识归档无关的课程细节。 +- 不因为课程还有章节没覆盖完就继续学。 +- 不把 Agent-only 预读写成用户已经掌握的 notes。 diff --git a/system/templates/course-topic/.daedalus/reviews/README.md b/system/templates/course-topic/.daedalus/reviews/README.md new file mode 100644 index 0000000..bf616b2 --- /dev/null +++ b/system/templates/course-topic/.daedalus/reviews/README.md @@ -0,0 +1,17 @@ +# Topic Reviews + +Topic-level reviews are independent from the topic learning lifecycle. They do not reopen completed stages; they help the user rebuild, apply, and repair understanding from already created artifacts. + +Review questions must start from: + +```text +业务目标 / 现实任务 + -> 现实制约 + -> naive solution 为什么失败 + -> 核心抽象 / 不变量 + -> 实现机制 + -> trade-off + -> 对比最佳实践 + -> 可迁移模式 + -> 复习题 / 应用题 +``` diff --git a/system/templates/course-topic/.daedalus/state.md b/system/templates/course-topic/.daedalus/state.md new file mode 100644 index 0000000..bb5cade --- /dev/null +++ b/system/templates/course-topic/.daedalus/state.md @@ -0,0 +1,7 @@ +# 学习状态 + +> 从 [`.daedalus/state.toml`](state.toml) 生成。不要手动编辑。 + +该文件会由 `daedalus state render` 重新生成。 + +`state.toml` 是任务生命周期的唯一事实源;目录 bucket 是该状态的文件系统投影。`task.lifecycle` 与 `task.workspace_bucket` 必须保持一致。 diff --git a/system/templates/course-topic/.daedalus/state.toml b/system/templates/course-topic/.daedalus/state.toml new file mode 100644 index 0000000..1a8648c --- /dev/null +++ b/system/templates/course-topic/.daedalus/state.toml @@ -0,0 +1,90 @@ +# daedalus course learning topic state. 保留注释;确定性字段由 CLI 更新。 +# +# Topic state 承载 course-learning 专题学习进度。Project state 只负责 project lifecycle +# 和 active topic 指针。 +# +# 枚举约束: +# - topic.lifecycle 只能是:planned、active、blocked、awaiting-reflection、completed、abandoned、skipped。 +# - stage.status 只能是:pending、active、blocked、paused、done。 +# - transition.action 只能是:init、enter、complete、block、resume、rollback、topic-await-reflection、topic-complete、topic-abandon。 +# - transition.approval_source 只能是:user-confirmed、artifact-equivalent、stage-not-applicable。 +# Agent 不要发明新的枚举值;如需新增,先修改 Rust 领域模型、模板和测试。 +# required_artifacts 表示阶段完成所需文件。CLI 只校验文件存在; +# Agent 仍需确认 notes 是否包含用户确认、亲自实践或手写笔记后的证据。 + +[topic] +slug = "{{TOPIC_SLUG}}" +title = "{{TOPIC_TITLE}}" +kind = "course-learning-topic" +parent_project = "../.." +lifecycle = "active" +current_phase = "01-need-aligner" +next_action = "澄清课程学习诉求、目标能力和章节取舍,并填写 [`.daedalus/task-card.md`](task-card.md)。" + +[[stages]] +id = "01-need-aligner" +title = "对齐课程学习诉求" +status = "active" +required_artifacts = [".daedalus/task-card.md", ".daedalus/outcome-map.md"] + +[[stages]] +id = "02-syllabus-mapper" +title = "映射课程 syllabus" +status = "pending" +required_artifacts = ["guides/02-syllabus-mapper/README.md"] +completion_mode = "inherited" +inherited_from = "../../shared/syllabus-map.md" + +[[stages]] +id = "03-concept-roadmap" +title = "建立概念路线图" +status = "pending" +required_artifacts = ["guides/03-concept-roadmap/README.md", "../../shared/concept-map.md"] + +[[stages]] +id = "04-lesson-lab" +title = "把课程 lesson 变成可观察实验" +status = "pending" +required_artifacts = ["guides/04-lesson-lab/README.md", "notes/04-lesson-lab/README.md"] + +[[stages]] +id = "05-mechanism-deep-dive" +title = "拆解被 API 隐藏的机制" +status = "pending" +required_artifacts = ["guides/05-mechanism-deep-dive/README.md"] + +[[stages]] +id = "06-practice-transfer" +title = "迁移课程概念到真实任务" +status = "pending" +required_artifacts = ["guides/06-practice-transfer/README.md"] + +[[stages]] +id = "07-capstone-lab" +title = "完成课程驱动 mini project" +status = "pending" +required_artifacts = ["demo/design.md"] + +[[stages]] +id = "08-review-loop" +title = "复习与掌握度验证" +status = "pending" +required_artifacts = ["review/mastery-map.md", "review/question-bank.md"] + +[[stages]] +id = "09-closeout-archive" +title = "专题回顾与知识归档" +status = "pending" +required_artifacts = [ + ".daedalus/artifact-index.md", + ".daedalus/long-context.md", + "reflection/candidate-map.md", + "reflection/closeout.md", +] + +[[transitions]] +stage = "01-need-aligner" +action = "init" +timestamp = "{{CREATED_AT}}" +actor = "daedalus-cli" +reason = "初始化 course learning topic。" diff --git a/system/templates/course-topic/.daedalus/task-card.md b/system/templates/course-topic/.daedalus/task-card.md new file mode 100644 index 0000000..687b3e3 --- /dev/null +++ b/system/templates/course-topic/.daedalus/task-card.md @@ -0,0 +1,29 @@ +# 课程专题学习任务卡 + +- Project:`{{TASK_NAME}}` +- Topic:`{{TOPIC_SLUG}}` +- Topic 标题:{{TOPIC_TITLE}} +- 创建时间:`{{CREATED_AT}}` +- 学习材料类型:课程 / tutorial / official learning path +- 当前目标: +- 现实问题: +- 预期输出物: +- 验收标准: +- 约束条件: +- 暂不学习: +- 用户确认: + +## Topic 学习目标 + +- 希望获得的能力: +- 对应课程章节: +- 继承的 syllabus / concept context: +- 需要补充的 lesson 入口: +- 运行/观察要求: +- Practice transfer 方向: + +## 角色边界 + +- daedalus 应该做:拆解学习路径、生成行动指南、协助排障、验收阶段产物。 +- 用户必须亲自做:确认目标、运行关键命令、观察结果、回答核心问题、形成自己的笔记。 +- daedalus 可以协助但不能代替:课程取舍、环境启动、机制解释、实验观察、迁移设计。 diff --git a/system/templates/course-topic/.daedalus/todo.md b/system/templates/course-topic/.daedalus/todo.md new file mode 100644 index 0000000..0161699 --- /dev/null +++ b/system/templates/course-topic/.daedalus/todo.md @@ -0,0 +1,57 @@ +# Todo Path Board + +Todo 是动态路径看板。学习证据变化、阶段完成、学习路径需要收窄或扩展时,都要同步更新它和 [`outcome-map.md`](outcome-map.md)。 + +> Agent 负责拆解、指导、排障和验收;用户负责关键实践、观察和手写笔记。不要把 Agent 自动完成的事项伪装成用户已经掌握。 + +## North Star + +- Project:`{{TASK_NAME}}` +- Topic:`{{TOPIC_SLUG}}` - {{TOPIC_TITLE}} +- 最终产物: +- 最小 lesson lab: +- 业务迁移目标: + +## Current Path + +当前 active topic 正在从 `01-need-aligner` 走向 `.daedalus/task-card.md` 和 `.daedalus/outcome-map.md`。 + +## Now + +- 当前问题:澄清当前课程学习诉求、章节边界和验收标准。 +- 为什么现在做它:没有 North Star,后续 syllabus 取舍、概念路线、lesson lab 和迁移练习都会失去取舍标准。 +- 完成后解锁:进入 syllabus mapping,并能判断哪些章节必学、跳读或延后。 + +## Current Cursor + +Current Cursor 是恢复定位器,不是完成证明。恢复或判断阶段状态时,必须用当前代码、测试、运行输出或用户已验证观察重新校准。 + +- Course frontier:暂无,尚未进入具体 lesson、mechanism 或 transfer checkpoint。 +- Already wired:暂无。 +- Current open decision:暂无。 +- Do not suggest:不要跳过 need alignment 直接进入课程照抄或大段机制解释。 + +## Critical Checkpoint + +Critical Checkpoint 只记录会影响后续判断的关键取舍,不写成聊天日志。 + +- Course constraint:当前课程方案成立的现实约束是什么? +- Faithful imitation:lesson lab 中哪些机制需要先忠实模仿以获得实现手感? +- Simplified / improved / discarded:本轮哪些部分应该简化、改进或丢弃? +- Transfer risk:迁移到业务场景前必须重新验证什么? + +## Gaps Blocking Next Stage + +- [ ] 学习目标:阻塞 `.daedalus/task-card.md` 的当前目标和验收标准。 +- [ ] 最小 lesson lab:阻塞 `.daedalus/outcome-map.md` 的 North Star。 +- [ ] 业务迁移目标:阻塞后续 `notes/business-application.md` 的评价标准。 + +## Stage Exit Criteria + +- [ ] 可以解释本任务为什么值得学习这门课程。 +- [ ] 可以说清最终产物和最小 lesson lab。 +- [ ] 可以用验收标准判断是否进入 `02-syllabus-mapper`。 + +## Done + +## Canceled diff --git a/system/templates/course-topic/.daedalus/validation-log.md b/system/templates/course-topic/.daedalus/validation-log.md new file mode 100644 index 0000000..026b376 --- /dev/null +++ b/system/templates/course-topic/.daedalus/validation-log.md @@ -0,0 +1,18 @@ +# Daedalus 验证日志 + +记录本学习任务对 daedalus 教学引导能力的反向验证结果。 + +## 使用规则 + +- 不要把普通学习笔记写到这里;学习笔记写入 [`notes/`](../notes)。 +- 这里只记录 daedalus 本身的引导效果、缺口和改进建议。 +- 每完成一个阶段,至少复盘一次“Agent 是否代替用户做太多”。 + +## {{CREATED_AT}} + +- 阶段:`01-goal-aligner` +- 有效引导: +- 用户亲自完成的实践: +- Agent 代替用户过多的地方: +- Prompt/template/CLI/docs 改进建议: +- 是否足以进入下一阶段: diff --git a/system/templates/course-topic/demo/.gitkeep b/system/templates/course-topic/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/.gitkeep b/system/templates/course-topic/guides/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/01-need-aligner/.gitkeep b/system/templates/course-topic/guides/01-need-aligner/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/02-syllabus-mapper/.gitkeep b/system/templates/course-topic/guides/02-syllabus-mapper/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/03-concept-roadmap/.gitkeep b/system/templates/course-topic/guides/03-concept-roadmap/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/04-lesson-lab/.gitkeep b/system/templates/course-topic/guides/04-lesson-lab/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/05-mechanism-deep-dive/.gitkeep b/system/templates/course-topic/guides/05-mechanism-deep-dive/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/06-practice-transfer/.gitkeep b/system/templates/course-topic/guides/06-practice-transfer/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/07-capstone-lab/.gitkeep b/system/templates/course-topic/guides/07-capstone-lab/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/08-review-loop/.gitkeep b/system/templates/course-topic/guides/08-review-loop/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/guides/09-closeout-archive/.gitkeep b/system/templates/course-topic/guides/09-closeout-archive/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/notes/.gitkeep b/system/templates/course-topic/notes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/notes/04-lesson-lab/.gitkeep b/system/templates/course-topic/notes/04-lesson-lab/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course-topic/reflection/README.md b/system/templates/course-topic/reflection/README.md new file mode 100644 index 0000000..95662eb --- /dev/null +++ b/system/templates/course-topic/reflection/README.md @@ -0,0 +1,12 @@ +# Reflection + +`reflection/` 是 topic 的滚动知识候选和最终主动回顾区。 + +这里不是 Agent 总结区,也不是从 notes 自动提取知识的地方。`candidate-map.md` 从 01-09 阶段就开始滚动维护,记录学习过程中暴露出的高价值候选;用户需要在完成 topic 后,用自己的语言写出 closeout reflection;Agent 进入总结阶段后,应根据当前 topic 的具体内容重写反思提示,再进行提问、挑战、补外部参照、建议链接和一致性检查。 + +当 topic 主体学习已经完成但你还需要等周末或大块时间回顾时,topic 可以进入 `awaiting-reflection`。这是正常的 closeout pending 状态,不是失败;它不会占用日常 active learning slot,但仍然需要尽快完成。 + +## Files + +- [`candidate-map.md`](candidate-map.md):贯穿 01-09 滚动维护的候选知识表,10-reflection 只做查漏补缺、降噪和状态确认。 +- [`closeout.md`](closeout.md):用户主动回顾总结。进入 `knowledge-base/` 前必须先由用户完成它。 diff --git a/system/templates/course-topic/reflection/candidate-map.md b/system/templates/course-topic/reflection/candidate-map.md new file mode 100644 index 0000000..2e090df --- /dev/null +++ b/system/templates/course-topic/reflection/candidate-map.md @@ -0,0 +1,21 @@ +# 知识候选表 + +> 状态只能使用:`候选中`、`总结中`、`已归档`、`已忽略`。 + +## 主题核心 + +| 候选 | 为什么值得看 | 证据 | 状态 | +| --- | --- | --- | --- | +| | | | 候选中 | + +## 底层原理 + +| 候选 | 为什么值得看 | 证据 | 状态 | +| --- | --- | --- | --- | +| | | | 候选中 | + +## 工程模式 + +| 候选 | 为什么值得看 | 证据 | 状态 | +| --- | --- | --- | --- | +| | | | 候选中 | diff --git a/system/templates/course-topic/reflection/closeout.md b/system/templates/course-topic/reflection/closeout.md new file mode 100644 index 0000000..022db7b --- /dev/null +++ b/system/templates/course-topic/reflection/closeout.md @@ -0,0 +1,47 @@ +# Closeout Reflection + +> 这是 daedalus 留给你的 topic 收尾回顾。它不是固定问卷,也不是 daedalus 的总结区;它可以粗糙、不完整、有困惑,但一定要保留你自己的判断、变化和疑问。 + +当你准备结束一个 topic 时,先别急着让 daedalus 归档知识。请先在这里写下你真实想明白的东西:这次学习到底解决了什么问题,你的理解发生了什么变化,哪些判断有证据,哪些地方还只是模仿或猜测。 + +整个学习过程中,daedalus 会滚动维护一份知识候选表。进入 closeout 时,你不需要照单全收;你要用 closeout 判断哪些真的成为了你的理解,哪些只是路过,哪些应该忽略。 + +> 完整 topic 的回顾需要高度专注的注意力,你要把几周的源码、demo、争论、失败、设计取舍,压缩成自己的理解。所以推荐你在周末或者有大块连续时间段的时候专注做这件事哦 😄 + +如果这个 topic 已经进入 `awaiting-reflection`,说明主体学习已经完成,daedalus 只是把它从日常推进队列里移出来,等你有足够注意力时再认真收尾。它不是失败,也不是让你无限拖延;它是一张需要被看见、被安排、被完成的 closeout 小债条。 + +你不需要套用固定目录结构。不同 topic 的 closeout reflection 可以有不同标题、不同顺序、不同重点。进入 `10-reflection` 时,daedalus 会先阅读当前 topic 的目标、notes、demo、业务迁移、运行证据和遗留问题,然后在对话里提出贴合当前 topic 的回顾问题。 + +可以大致从这些方向开始想: + +- 你最初想解决的真实问题是什么。 +- 你从一开始的学习目标中真正学到了什么。 +- 这次学习让你改变了哪些看法。 +- 你现在能独立做出什么判断或设计。 +- demo 实现过程中做了哪些关键决策、trade-off 和简化。 +- demo 的核心架构图是什么;如果图还没想清楚,先手绘、人工调整,再让 daedalus 帮你落盘。 +- 学习和实现 demo 的过程中,暴露了哪些基础知识薄弱点,比如操作系统、网络、数据库、编程语言、编译原理、运行时、并发或安全模型。 +- 哪些结论有源码、实验、demo、业务迁移或外部资料支撑。 +- 哪些地方只是为了学习而忠实模仿,不能直接迁移。 +- 哪些经验可以举一反三。 +- 哪些地方仍然模糊、脆弱或需要复习。 +- 下一次遇到相似问题时,你会怎么判断。 +- 你希望 daedalus 重点 challenge 哪些地方。 + +如果你需要画图,不要试图用一张图讲完所有细节。图优先画清楚分层和主方向;关键决策点、异常分支、取舍原因,放在图后的文字里解释。daedalus 会帮你检查图和文字是否互相支撑,而不是让图变成不可读的全量流程。 + +closeout 不要求你把所有底层原理都讲透,但要把重要原理缺口和可迁移能力点出来。后续进入 `knowledge-base` 时,daedalus 会从 guides、notes、demo、测试和外部资料里查漏补缺、去重和降噪。 + +你负责写正文;daedalus 会负责提问、challenge、搜索外部最佳实践、补链接、检查一致性和组织结构。没有你的主动回顾,这个 topic 就不能进入 `knowledge-base/`。 + +你写完并确认后,daedalus 应该主动推进: + +```text +reflection done +-> challenge resolved +-> knowledge-base gate +-> archive confirmed +-> topic completed +``` + +如果某一步已经在对话中完成,daedalus 应该承认完成、同步地图并进入下一步,而不是让你重复提醒。 diff --git a/system/templates/course-topic/review/.gitkeep b/system/templates/course-topic/review/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/course/.daedalus/artifact-index.md b/system/templates/course/.daedalus/artifact-index.md new file mode 100644 index 0000000..b8925c1 --- /dev/null +++ b/system/templates/course/.daedalus/artifact-index.md @@ -0,0 +1,17 @@ +# Project 产物索引 + +记录 project 层产物,以及它们如何服务多个 topic。 + +> `状态` 列只能使用 `草稿`、`已验证`、`待补充`、`不适用`。专题阶段产物写入 `topics/<slug>/.daedalus/artifact-index.md`。 + +| 产物 | 层级 | 目的 | 状态 | +| --- | --- | --- | --- | +| [`.daedalus/project-map.md`](project-map.md) | project | 说明 North Star、topic 路线和跨专题停止规则 | 草稿 | +| [`.daedalus/topic-board.md`](topic-board.md) | project | 维护 topic 列表、状态、依赖和下一步 | 草稿 | +| [`.daedalus/state.md`](state.md) | project | 由 project `state.toml` 渲染的可读状态 | 已验证 | +| [`shared/README.md`](../shared/README.md) | shared | 跨 topic 共享上下文入口 | 草稿 | +| [`shared/syllabus-map.md`](../shared/syllabus-map.md) | shared | 课程 syllabus、章节依赖和取舍边界 | 草稿 | +| [`shared/course-progress.md`](../shared/course-progress.md) | shared | 课程章节进度和当前 checkpoint | 草稿 | +| [`shared/concept-map.md`](../shared/concept-map.md) | shared | 跨章节核心概念、机制问题和薄弱点 | 草稿 | +| [`shared/evidence-registry.md`](../shared/evidence-registry.md) | shared | 跨 topic 复用的证据登记表 | 草稿 | +| [`topics/`](../topics) | topic collection | 专题学习目录;每个 topic 自己维护 course-learning 阶段产物 | 草稿 | diff --git a/system/templates/course/.daedalus/decision-log.md b/system/templates/course/.daedalus/decision-log.md new file mode 100644 index 0000000..468aa51 --- /dev/null +++ b/system/templates/course/.daedalus/decision-log.md @@ -0,0 +1,9 @@ +# Project 决策记录 + +记录会影响 project 范围、topic 路线、shared context 或状态流转的关键决策。 + +## {{CREATED_AT}} + +- 决策:初始化 repo learning project `{{TASK_NAME}}`,并创建初始 topic `{{TOPIC_SLUG}}`。 +- 原因:开启一个可分专题推进、可共享上下文的 filesystem-first 学习闭环。 +- 影响:project active topic 为 `{{TOPIC_SLUG}}`;专题阶段进度写入 `topics/{{TOPIC_SLUG}}/.daedalus/state.toml`。 diff --git a/system/templates/course/.daedalus/long-context.md b/system/templates/course/.daedalus/long-context.md new file mode 100644 index 0000000..9f4e267 --- /dev/null +++ b/system/templates/course/.daedalus/long-context.md @@ -0,0 +1,26 @@ +# Project 长期上下文 + +只保留跨 topic 可复用的长期上下文,不要粘贴聊天记录。 + +## 角色边界 + +- Project root 保存 project 地图、topic board 和 shared context。 +- `topics/<slug>/guides/` 保存该专题的 Agent 行动指南。 +- `topics/<slug>/notes/` 保存该专题中用户亲自实践和思考后的学习笔记。 +- `topics/<slug>/demo/` 保存该专题的 mini demo。 +- `shared/` 保存跨专题可复用的源码索引、术语、证据和迁移模式。 +- `source/` 保存外部源码缓存,默认不提交到 daedalus 仓库。 + +## Project 目标 + +## Active Topic + +## Topic 路线 + +## Shared Decisions + +## 已验证的跨专题结论 + +## 未解决的跨专题问题 + +## 恢复上下文提示 diff --git a/system/templates/course/.daedalus/project-map.md b/system/templates/course/.daedalus/project-map.md new file mode 100644 index 0000000..5a84214 --- /dev/null +++ b/system/templates/course/.daedalus/project-map.md @@ -0,0 +1,35 @@ +# Project Map + +Project Map 是长期 course learning project 的导航仪表盘。它描述课程主线、章节依赖和跨 topic 学习路线,不替代任何 topic 的 outcome map。 + +## North Star + +- 长期学习素材:{{COURSE_URL}} +- 课程材料入口:[`source/`](../source) +- 当前 active topic:`{{TOPIC_SLUG}}` +- 最终希望沉淀的跨专题能力: + +## Shared Context + +| 产物 | 用途 | 状态 | +| --- | --- | --- | +| [`shared/syllabus-map.md`](../shared/syllabus-map.md) | 课程章节、概念依赖和必学/可跳过边界 | 草稿 | +| [`shared/course-progress.md`](../shared/course-progress.md) | 课程进度、章节状态和当前 checkpoint | 草稿 | +| [`shared/concept-map.md`](../shared/concept-map.md) | 跨章节概念图、机制问题和薄弱点 | 草稿 | +| [`shared/glossary.md`](../shared/glossary.md) | 跨 topic 术语表 | 草稿 | +| [`shared/evidence-registry.md`](../shared/evidence-registry.md) | 可继承的 verified evidence | 草稿 | +| [`shared/transfer-patterns.md`](../shared/transfer-patterns.md) | 跨 topic 可迁移模式 | 草稿 | + +## Active Topic + +- slug:`{{TOPIC_SLUG}}` +- title:{{TOPIC_TITLE}} +- topic workspace:[`topics/{{TOPIC_SLUG}}`](../topics/{{TOPIC_SLUG}}) +- topic outcome map:[`topics/{{TOPIC_SLUG}}/.daedalus/outcome-map.md`](../topics/{{TOPIC_SLUG}}/.daedalus/outcome-map.md) + +## Project Stop Rules + +- 不把 topic 草稿直接提升为 shared evidence。 +- 不在 project root 记录 topic stage 进度。 +- 不把多个 topic 同时设为 active。 +- 不把课程代码跑通等同于概念掌握;必须留下观察、解释或迁移证据。 diff --git a/system/templates/course/.daedalus/reviews/README.md b/system/templates/course/.daedalus/reviews/README.md new file mode 100644 index 0000000..6fcd371 --- /dev/null +++ b/system/templates/course/.daedalus/reviews/README.md @@ -0,0 +1,17 @@ +# Project Reviews + +Project-level reviews are independent from the learning project lifecycle. Use them to revisit shared context, cross-topic relations, and business transfer after one or more topics have been learned. + +Review questions must start from: + +```text +业务目标 / 现实任务 + -> 现实制约 + -> naive solution 为什么失败 + -> 核心抽象 / 不变量 + -> 实现机制 + -> trade-off + -> 对比最佳实践 + -> 可迁移模式 + -> 复习题 / 应用题 +``` diff --git a/system/templates/course/.daedalus/state.md b/system/templates/course/.daedalus/state.md new file mode 100644 index 0000000..bb5cade --- /dev/null +++ b/system/templates/course/.daedalus/state.md @@ -0,0 +1,7 @@ +# 学习状态 + +> 从 [`.daedalus/state.toml`](state.toml) 生成。不要手动编辑。 + +该文件会由 `daedalus state render` 重新生成。 + +`state.toml` 是任务生命周期的唯一事实源;目录 bucket 是该状态的文件系统投影。`task.lifecycle` 与 `task.workspace_bucket` 必须保持一致。 diff --git a/system/templates/course/.daedalus/state.toml b/system/templates/course/.daedalus/state.toml new file mode 100644 index 0000000..1cc5524 --- /dev/null +++ b/system/templates/course/.daedalus/state.toml @@ -0,0 +1,41 @@ +# daedalus course learning project state. 保留注释;确定性字段由 CLI 更新。 +# +# Project state 只描述长期课程学习项目本身,不承载专题阶段进度。 +# 专题进度写在 `topics/<slug>/.daedalus/state.toml`。 +# +# 枚举约束: +# - task.lifecycle 只能是:active、idle、abandoned。 +# - task.workspace_bucket 固定为:projects。 +# - project.active_topic 必须指向 `[[topics]]` 中存在的 topic slug。 +# - topics.lifecycle 只能是:planned、active、blocked、awaiting-reflection、completed、abandoned、skipped。 +# - transition.action 只能是:init、topic-new、topic-activate、topic-await-reflection、topic-complete、topic-abandon、migrate、task-complete、abandon。 +# Agent 不要发明新的枚举值;如需新增,先修改 Rust 领域模型、模板和测试。 + +[task] +name = "{{TASK_NAME}}" +kind = "course-learning" +created_at = "{{CREATED_AT}}" +lifecycle = "active" +workspace_bucket = "projects" +next_action = "激活初始课程专题 `{{TOPIC_SLUG}}`,澄清课程学习目标,并填写 topic 的 `.daedalus/task-card.md`。" + +[project] +mode = "multi-topic" +active_topic = "{{TOPIC_SLUG}}" +source_kind = "course" +source_name = "{{TASK_NAME}}" +course_url = "{{COURSE_URL}}" + +[[topics]] +slug = "{{TOPIC_SLUG}}" +title = "{{TOPIC_TITLE}}" +lifecycle = "active" +path = "topics/{{TOPIC_SLUG}}" +inherits = [] + +[[transitions]] +stage = "project" +action = "init" +timestamp = "{{CREATED_AT}}" +actor = "daedalus-cli" +reason = "初始化 course learning project,并创建初始课程专题 `{{TOPIC_SLUG}}`。" diff --git a/system/templates/course/.daedalus/topic-board.md b/system/templates/course/.daedalus/topic-board.md new file mode 100644 index 0000000..82063e6 --- /dev/null +++ b/system/templates/course/.daedalus/topic-board.md @@ -0,0 +1,24 @@ +# Topic Board + +Topic Board 记录当前 course learning project 的专题列表、课程章节映射、状态和继承关系。一次只能有一个 active topic。 + +## Active Topic + +- `{{TOPIC_SLUG}}`:{{TOPIC_TITLE}} + +## Topics + +| Topic | Title | Lifecycle | Path | Inherits | +| --- | --- | --- | --- | --- | +| `{{TOPIC_SLUG}}` | {{TOPIC_TITLE}} | active | [`topics/{{TOPIC_SLUG}}`](../topics/{{TOPIC_SLUG}}) | - | + +## Parking Lot + +- 待补充后续 topic。 + +## Rules + +- 新增专题必须使用 `daedalus topic new <slug> --title <title>`。 +- 切换专题必须使用 `daedalus topic activate <slug>`。 +- 专题完成后必须反向更新 [`shared/evidence-registry.md`](../shared/evidence-registry.md)。 +- 专题进入 transfer/capstone 前,必须能指向已学习的课程概念和验证证据。 diff --git a/system/templates/course/.daedalus/validation-log.md b/system/templates/course/.daedalus/validation-log.md new file mode 100644 index 0000000..2e66b26 --- /dev/null +++ b/system/templates/course/.daedalus/validation-log.md @@ -0,0 +1,17 @@ +# Project 验证日志 + +记录 daedalus 在 project/topic 编排层面的引导效果。 + +## 使用规则 + +- 普通学习笔记写入 active topic 的 `notes/`。 +- 专题阶段复盘写入 active topic 的 `.daedalus/validation-log.md`。 +- 这里只记录 project 层问题:topic 切换、shared context 复用、WIP 管理、迁移和归档。 + +## {{CREATED_AT}} + +- 事件:初始化 repo learning project。 +- 有效引导: +- 用户亲自完成的实践: +- Agent 代替用户过多的地方: +- Project/topic/template/CLI 改进建议: diff --git a/system/templates/course/CLAUDE.md b/system/templates/course/CLAUDE.md new file mode 100644 index 0000000..f56ad57 --- /dev/null +++ b/system/templates/course/CLAUDE.md @@ -0,0 +1,43 @@ +# Course Learning Project Context + +本目录是长期 course learning project `{{TASK_NAME}}` 的工作区。 + +处理当前 project 前,先阅读 project 层上下文: + +@.daedalus/state.md +@.daedalus/project-map.md +@.daedalus/topic-board.md +@shared/syllabus-map.md +@shared/course-progress.md +@shared/concept-map.md +@shared/evidence-registry.md + +然后读取 active topic: + +@topics/{{TOPIC_SLUG}}/.daedalus/state.md +@topics/{{TOPIC_SLUG}}/.daedalus/task-card.md +@topics/{{TOPIC_SLUG}}/.daedalus/outcome-map.md +@topics/{{TOPIC_SLUG}}/.daedalus/todo.md +@topics/{{TOPIC_SLUG}}/.daedalus/long-context.md + +## 操作规则 + +- Project root 的 `.daedalus/state.toml` 只描述 project lifecycle、workspace bucket、active topic 和 topic 列表。 +- Topic 的 `topics/<slug>/.daedalus/state.toml` 才描述 course-learning 阶段进度。 +- 创建新的 course learning project 必须使用 `daedalus init course-learning <project-name> --topic <topic-slug> --title <topic-title> --course-url <url>`。 +- 创建新专题必须使用 `daedalus topic new <topic-slug> --title <topic-title>`。 +- 切换专题必须使用 `daedalus topic activate <topic-slug>`。 +- 推进阶段时,`daedalus state enter/complete/block/resume <stage-id>` 默认操作 active topic,不操作 project root。 +- 如需指定专题,使用 `daedalus state complete <stage-id> --topic <topic-slug>`。 +- 关闭专题使用 `daedalus topic complete <topic-slug> --reason <reason>`;关闭整个 project 才使用 `daedalus task complete --reason <reason>`。 +- 不要手动编辑 `state.md`;需要更新时运行 `daedalus state render` 渲染 active topic,或 `daedalus state render --project` 渲染 project。 +- `daedalus validate` 校验 project + active topic;`daedalus validate --all-topics` 校验 project + 所有 topics。 +- 不要在 project root 手写 topic stage 状态,不要把 topic notes 写到 project root。 +- `shared/` 只保存跨 topic 可复用的 verified knowledge;topic 草稿和用户回答留在 `topics/<slug>/notes/`。 +- 每次继续学习前,先说明 project、active topic、topic stage、current gap 和完成后解锁什么。 +- 使用 `system/prompts/course` 下的分阶段 course 学习提示词来引导 active topic 的学习过程。 +- 除非用户明确批准,或已有可追溯的等价证据,否则不要使用 `--force` 这类强制绕过选项。 +- 不要默认代替用户完成关键学习实践。Agent 应该说明要做什么、为什么做、用户等待时可以思考什么,并在用户完成实践后协助验收和排障。 +- Notes Ownership Rule:用户没有回答、观察或实践前,不要把结论写入 topic `notes/`。Agent 可以写 topic `guides/`,也可以在 topic `notes/` 中创建待用户填写的轻量模板。 +- `source/` 用来记录课程主页、章节入口、notebook、参考文档和可选源码链接,不默认假设要拉取外部 repo。 +- 跑通课程代码只是 lesson lab 的一部分;完成阶段前还要留下 input/output/shape/loss/artifact 观察或用户解释。 diff --git a/system/templates/course/shared/README.md b/system/templates/course/shared/README.md new file mode 100644 index 0000000..aaef67a --- /dev/null +++ b/system/templates/course/shared/README.md @@ -0,0 +1,12 @@ +# Shared Context + +`shared/` 只保存跨 topic 可复用的已验证课程学习上下文。探索草稿、用户假设和专题内 lesson 观察应留在对应 `topics/<slug>/notes/`。 + +## Files + +- [`syllabus-map.md`](syllabus-map.md):课程章节、概念依赖和取舍边界。 +- [`course-progress.md`](course-progress.md):章节进度、当前 checkpoint 和复习状态。 +- [`concept-map.md`](concept-map.md):跨章节核心概念、机制问题和薄弱点。 +- [`glossary.md`](glossary.md):术语表。 +- [`evidence-registry.md`](evidence-registry.md):可继承证据。 +- [`transfer-patterns.md`](transfer-patterns.md):可迁移模式。 diff --git a/system/templates/course/shared/concept-map.md b/system/templates/course/shared/concept-map.md new file mode 100644 index 0000000..a5dcd97 --- /dev/null +++ b/system/templates/course/shared/concept-map.md @@ -0,0 +1,17 @@ +# Concept Map + +记录跨章节概念、机制问题和薄弱点。每个概念都应该能回到课程章节、实验观察或迁移任务。 + +## Core Concepts + +| Concept | 学习来源 | 机制问题 | 已验证证据 | 薄弱点 | +| --- | --- | --- | --- | --- | +| 待补充 | 待补充 | 待补充 | - | 待补充 | + +## Mechanism Questions + +- 待补充。 + +## Transfer Links + +- 待补充。 diff --git a/system/templates/course/shared/course-progress.md b/system/templates/course/shared/course-progress.md new file mode 100644 index 0000000..d2c0832 --- /dev/null +++ b/system/templates/course/shared/course-progress.md @@ -0,0 +1,21 @@ +# Course Progress + +记录课程进度、当前 checkpoint 和复习状态。这里是恢复学习现场的入口,不替代 topic todo。 + +## Current Checkpoint + +- Active topic:`{{TOPIC_SLUG}}` +- 当前课程章节: +- 当前 lesson / mechanism: +- 当前 open question: +- 下一步: + +## Progress Board + +| Chapter / Section | Status | Evidence | Review | +| --- | --- | --- | --- | +| 待补充 | planned | - | - | + +## Review Queue + +- 待补充。 diff --git a/system/templates/course/shared/evidence-registry.md b/system/templates/course/shared/evidence-registry.md new file mode 100644 index 0000000..1ce138d --- /dev/null +++ b/system/templates/course/shared/evidence-registry.md @@ -0,0 +1,18 @@ +# Evidence Registry + +这里保存跨 topic 可复用的 verified evidence。每条 evidence 必须有来源、边界和迁移限制。 + +## Template + +```markdown +## E-001: 标题 + +- 状态:verified / stale / superseded +- 来源 topic: +- 课程章节: +- 验证方式: +- 结论: +- 适用边界: +- 迁移限制: +- 被引用: +``` diff --git a/system/templates/course/shared/glossary.md b/system/templates/course/shared/glossary.md new file mode 100644 index 0000000..741fd62 --- /dev/null +++ b/system/templates/course/shared/glossary.md @@ -0,0 +1,7 @@ +# Glossary + +跨 topic 术语表。 + +## Terms + +- 待补充。 diff --git a/system/templates/course/shared/syllabus-map.md b/system/templates/course/shared/syllabus-map.md new file mode 100644 index 0000000..db9622c --- /dev/null +++ b/system/templates/course/shared/syllabus-map.md @@ -0,0 +1,23 @@ +# Syllabus Map + +把课程 syllabus 转换为 daedalus 学习路线图。不要盲目照搬课程目录;要标明必学、可跳过、延后和迁移关系。 + +## Course + +- URL:{{COURSE_URL}} +- 学习策略: +- 主线章节: +- 旁路章节: +- 暂不学习: + +## Chapter Map + +| Chapter / Section | 概念目标 | 依赖 | 必做练习 | 状态 | 证据 | +| --- | --- | --- | --- | --- | --- | +| 待补充 | 待补充 | 待补充 | 待补充 | planned | - | + +## Stop Rules + +- 不因为课程有章节就默认全量学习。 +- 不把看完文字等同于掌握概念。 +- 不把跑通示例代码等同于能迁移到真实任务。 diff --git a/system/templates/course/shared/transfer-patterns.md b/system/templates/course/shared/transfer-patterns.md new file mode 100644 index 0000000..8f65a4b --- /dev/null +++ b/system/templates/course/shared/transfer-patterns.md @@ -0,0 +1,15 @@ +# Transfer Patterns + +跨 topic 可迁移模式。只归档已通过 lesson lab、practice transfer、capstone 或用户解释验证的模式。 + +记录模式时必须保留: + +- 它解决的现实问题。 +- 它成立的约束。 +- topic lab 中为了学习而忠实模仿的部分。 +- 它的局限、failure mode 和不应照抄的部分。 +- 迁移到新业务场景前必须重新验证的约束。 + +## Patterns + +- 待补充。 diff --git a/system/templates/course/source/.gitignore b/system/templates/course/source/.gitignore new file mode 100644 index 0000000..f249c31 --- /dev/null +++ b/system/templates/course/source/.gitignore @@ -0,0 +1,6 @@ +# 外部源码默认不进入 daedalus 仓库。 +* +!.gitignore +!.gitkeep +!README.md +!pull_source.sh diff --git a/system/templates/course/source/README.md b/system/templates/course/source/README.md new file mode 100644 index 0000000..315bc1c --- /dev/null +++ b/system/templates/course/source/README.md @@ -0,0 +1,16 @@ +# Course Source + +这里记录当前课程学习任务的材料入口。 + +- 课程主页:{{COURSE_URL}} +- 官方文档: +- 章节入口: +- 练习 / notebook: +- 可选源码链接: +- 本地实验目录: + +## Rules + +- `source/` 是课程材料索引,不默认假设要拉取外部 repo。 +- 课程中的代码应先进入 topic `demo/` 或 `guides/04-lesson-lab/`,再由用户运行和观察。 +- 如果某节课确实需要阅读库源码,应先说明机制问题,再把它登记到 `guides/05-mechanism-deep-dive/`。 diff --git a/system/templates/course/topics/.gitkeep b/system/templates/course/topics/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/system/templates/discovery/item.md b/system/templates/discovery/item.md new file mode 100644 index 0000000..4e4a181 --- /dev/null +++ b/system/templates/discovery/item.md @@ -0,0 +1,33 @@ +# <Discovery Title> + +## Raw Signals + +- 用户原始表达: +- 候选材料: +- 当前上下文: + +## Need Hypotheses + +| Hypothesis | Evidence | Confidence | User Status | +| --- | --- | --- | --- | +| | | low / medium / high | pending / confirmed / rejected | + +## Questions + +### Round 1 + +- Agent question: +- User answer: +- Updated hypothesis: + +## Topic Candidates + +| Candidate | Serves Need | Possible Artifact | Risk | +| --- | --- | --- | --- | +| | | | | + +## Decision Readiness + +- 是否可以进入 clarify-goal: +- 仍需澄清: +- 暂不学习: diff --git a/workspaces/.daedalus/current.toml b/workspaces/.daedalus/current.toml index 23b0835..fe2b152 100644 --- a/workspaces/.daedalus/current.toml +++ b/workspaces/.daedalus/current.toml @@ -1,5 +1,5 @@ schema_version = 1 -current_project = "projects/openai-codex-cli-deep-learning" -current_topic = "" +current_project = "projects/hugging-face-llm-course" +current_topic = "projects/hugging-face-llm-course/topics/lora-feedback-loop" pending_closeout_project = "" pending_closeout_topic = "" diff --git a/workspaces/.daedalus/project-index.toml b/workspaces/.daedalus/project-index.toml index 9bb20f9..ac2e98f 100644 --- a/workspaces/.daedalus/project-index.toml +++ b/workspaces/.daedalus/project-index.toml @@ -6,3 +6,10 @@ name = "openai-codex-cli-deep-learning" lifecycle = "idle" path = "projects/openai-codex-cli-deep-learning" active_topic = "" + +[[projects]] +slug = "hugging-face-llm-course" +name = "hugging-face-llm-course" +lifecycle = "active" +path = "projects/hugging-face-llm-course" +active_topic = "lora-feedback-loop" diff --git a/workspaces/current-project b/workspaces/current-project new file mode 120000 index 0000000..4464ef3 --- /dev/null +++ b/workspaces/current-project @@ -0,0 +1 @@ +projects/hugging-face-llm-course \ No newline at end of file diff --git a/workspaces/current-topic b/workspaces/current-topic new file mode 120000 index 0000000..65721e6 --- /dev/null +++ b/workspaces/current-topic @@ -0,0 +1 @@ +projects/hugging-face-llm-course/topics/lora-feedback-loop \ No newline at end of file diff --git a/workspaces/discovery/README.md b/workspaces/discovery/README.md new file mode 100644 index 0000000..774bd49 --- /dev/null +++ b/workspaces/discovery/README.md @@ -0,0 +1,23 @@ +# Discovery Inbox + +这里保存尚未进入 active learning 的选题探索记录。 + +## Role + +Discovery 是 pre-topic exploration space。它记录“用户为什么被某些方向吸引、真实诉求假设是什么、哪些问题仍需澄清”,而不是记录已经决定要学的 backlog candidate 或 active topic 进度。 + +## Rules + +- 一个 discovery 使用一个独立 Markdown 文件,例如 `ai-stack-vs-ddia.md`。 +- 默认使用模板:[`../../system/templates/discovery/item.md`](../../system/templates/discovery/item.md)。 +- discovery 文件不创建 `.daedalus/state.toml`。 +- discovery 文件不写 `notes/`、`guides/`、`demo/` 或 active `todo.md`。 +- 当诉求假设能导向可验证产物后,再进入 `clarify-goal` 和 `gatekeeper`。 +- 如果只是想保存未来可能学习的方向,而不是探索真实诉求,使用 [`../backlog`](../backlog)。 + +## States + +- `exploring`:还在澄清真实诉求。 +- `shaped`:已经形成可进入任务卡的诉求假设。 +- `promoted`:已经进入 backlog、project 或 topic。 +- `parked`:暂时搁置,不占 active WIP。 diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/artifact-index.md b/workspaces/projects/hugging-face-llm-course/.daedalus/artifact-index.md new file mode 100644 index 0000000..b8925c1 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/artifact-index.md @@ -0,0 +1,17 @@ +# Project 产物索引 + +记录 project 层产物,以及它们如何服务多个 topic。 + +> `状态` 列只能使用 `草稿`、`已验证`、`待补充`、`不适用`。专题阶段产物写入 `topics/<slug>/.daedalus/artifact-index.md`。 + +| 产物 | 层级 | 目的 | 状态 | +| --- | --- | --- | --- | +| [`.daedalus/project-map.md`](project-map.md) | project | 说明 North Star、topic 路线和跨专题停止规则 | 草稿 | +| [`.daedalus/topic-board.md`](topic-board.md) | project | 维护 topic 列表、状态、依赖和下一步 | 草稿 | +| [`.daedalus/state.md`](state.md) | project | 由 project `state.toml` 渲染的可读状态 | 已验证 | +| [`shared/README.md`](../shared/README.md) | shared | 跨 topic 共享上下文入口 | 草稿 | +| [`shared/syllabus-map.md`](../shared/syllabus-map.md) | shared | 课程 syllabus、章节依赖和取舍边界 | 草稿 | +| [`shared/course-progress.md`](../shared/course-progress.md) | shared | 课程章节进度和当前 checkpoint | 草稿 | +| [`shared/concept-map.md`](../shared/concept-map.md) | shared | 跨章节核心概念、机制问题和薄弱点 | 草稿 | +| [`shared/evidence-registry.md`](../shared/evidence-registry.md) | shared | 跨 topic 复用的证据登记表 | 草稿 | +| [`topics/`](../topics) | topic collection | 专题学习目录;每个 topic 自己维护 course-learning 阶段产物 | 草稿 | diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/decision-log.md b/workspaces/projects/hugging-face-llm-course/.daedalus/decision-log.md new file mode 100644 index 0000000..223747a --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/decision-log.md @@ -0,0 +1,9 @@ +# Project 决策记录 + +记录会影响 project 范围、topic 路线、shared context 或状态流转的关键决策。 + +## 2026-06-14 14:42:45 + +- 决策:初始化 course learning project `hugging-face-llm-course`,并创建初始 topic `lora-feedback-loop`。 +- 原因:开启一个可分专题推进、可共享上下文的 filesystem-first 学习闭环。 +- 影响:project active topic 为 `lora-feedback-loop`;专题阶段进度写入 `topics/lora-feedback-loop/.daedalus/state.toml`。 diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/long-context.md b/workspaces/projects/hugging-face-llm-course/.daedalus/long-context.md new file mode 100644 index 0000000..9f4e267 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/long-context.md @@ -0,0 +1,26 @@ +# Project 长期上下文 + +只保留跨 topic 可复用的长期上下文,不要粘贴聊天记录。 + +## 角色边界 + +- Project root 保存 project 地图、topic board 和 shared context。 +- `topics/<slug>/guides/` 保存该专题的 Agent 行动指南。 +- `topics/<slug>/notes/` 保存该专题中用户亲自实践和思考后的学习笔记。 +- `topics/<slug>/demo/` 保存该专题的 mini demo。 +- `shared/` 保存跨专题可复用的源码索引、术语、证据和迁移模式。 +- `source/` 保存外部源码缓存,默认不提交到 daedalus 仓库。 + +## Project 目标 + +## Active Topic + +## Topic 路线 + +## Shared Decisions + +## 已验证的跨专题结论 + +## 未解决的跨专题问题 + +## 恢复上下文提示 diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/project-map.md b/workspaces/projects/hugging-face-llm-course/.daedalus/project-map.md new file mode 100644 index 0000000..675adf4 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/project-map.md @@ -0,0 +1,41 @@ +# Project Map + +Project Map 是长期 course learning project 的导航仪表盘。它描述课程主线、章节依赖和跨 topic 学习路线,不替代任何 topic 的 outcome map。 + +## North Star + +- 长期学习素材:Hugging Face LLM Course 及其相关官方文档、notebooks 和开源示例 +- 课程材料入口:[`source/`](../source) +- 当前 active topic:`lora-feedback-loop` +- 最终希望沉淀的跨专题能力:以 Hugging Face LLM Course 为主学习材料,用系统工程第一性原理理解 AI 模型、训练、评测、反馈和部署链路,避免只停留在应用层 glue code。 + +## Shared Context + +| 产物 | 用途 | 状态 | +| --- | --- | --- | +| [`shared/syllabus-map.md`](../shared/syllabus-map.md) | 课程章节、概念依赖和必学/可跳过边界 | 草稿 | +| [`shared/course-progress.md`](../shared/course-progress.md) | 课程进度、章节状态和当前 checkpoint | 草稿 | +| [`shared/concept-map.md`](../shared/concept-map.md) | 跨章节概念图、机制问题和薄弱点 | 草稿 | +| [`shared/glossary.md`](../shared/glossary.md) | 跨 topic 术语表 | 草稿 | +| [`shared/evidence-registry.md`](../shared/evidence-registry.md) | 可继承的 verified evidence | 草稿 | +| [`shared/transfer-patterns.md`](../shared/transfer-patterns.md) | 跨 topic 可迁移模式 | 草稿 | + +## Active Topic + +- slug:`lora-feedback-loop` +- title:LoRA 微调与数据反馈闭环 +- topic workspace:[`topics/lora-feedback-loop`](../topics/lora-feedback-loop) +- topic outcome map:[`topics/lora-feedback-loop/.daedalus/outcome-map.md`](../topics/lora-feedback-loop/.daedalus/outcome-map.md) + +## Project Stop Rules + +- 不把 topic 草稿直接提升为 shared evidence。 +- 不在 project root 记录 topic stage 进度。 +- 不把多个 topic 同时设为 active。 +- 不把课程代码跑通等同于概念掌握;必须留下观察、解释或迁移证据。 + +## Course Learning Migration + +- 课程主页:https://huggingface.co/learn/llm-course/en +- 新 shared 入口:[`shared/syllabus-map.md`](../shared/syllabus-map.md)、[`shared/course-progress.md`](../shared/course-progress.md)、[`shared/concept-map.md`](../shared/concept-map.md) +- 课程实验现场已迁入 `guides/04-lesson-lab`;后续新 guide 使用 course-learning 阶段名。 diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/reviews/README.md b/workspaces/projects/hugging-face-llm-course/.daedalus/reviews/README.md new file mode 100644 index 0000000..6fcd371 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/reviews/README.md @@ -0,0 +1,17 @@ +# Project Reviews + +Project-level reviews are independent from the learning project lifecycle. Use them to revisit shared context, cross-topic relations, and business transfer after one or more topics have been learned. + +Review questions must start from: + +```text +业务目标 / 现实任务 + -> 现实制约 + -> naive solution 为什么失败 + -> 核心抽象 / 不变量 + -> 实现机制 + -> trade-off + -> 对比最佳实践 + -> 可迁移模式 + -> 复习题 / 应用题 +``` diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/state.md b/workspaces/projects/hugging-face-llm-course/.daedalus/state.md new file mode 100644 index 0000000..9aaaed8 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/state.md @@ -0,0 +1,39 @@ +# Project 状态 + +> 从 [`.daedalus/state.toml`](state.toml) 生成。不要手动编辑。 + +## 当前状态 + +- Project:`hugging-face-llm-course` +- 类型:`course-learning` +- 生命周期:`active` +- Workspace Bucket:`projects` +- Active Topic:`lora-feedback-loop` +- Pending Closeout:`none` +- Course URL:https://huggingface.co/learn/llm-course/en +- 下一步:继续 course-learning active topic `lora-feedback-loop`,按课程阶段推进下一份 guide。 + +## Topics + +- `lora-feedback-loop`: LoRA 微调与数据反馈闭环 (active) -> [`topics/lora-feedback-loop`](../topics/lora-feedback-loop) + +## Project Files + +- `.daedalus/project-map.md`: present +- `.daedalus/topic-board.md`: present +- `shared/syllabus-map.md`: present +- `shared/course-progress.md`: present +- `shared/concept-map.md`: present +- `shared/evidence-registry.md`: present + +## 最近状态流转 + +> 共 2 条状态流转;下面显示最近 2 条,完整历史见 [`.daedalus/state.toml`](state.toml) 的 `[[transitions]]`。 + +- `2026-06-14 14:42:45` 由 `daedalus-cli` 对 `project` 执行 `init`:初始化 course learning project,并创建初始专题 `lora-feedback-loop`。 +- `2026-06-23 00:53:42` 由 `daedalus-cli` 对 `project` 执行 `migrate`:确认项目使用 course-learning 语义。 + +## 下一步 CLI 建议 + +- `daedalus state render --project` +- `daedalus validate` diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/state.toml b/workspaces/projects/hugging-face-llm-course/.daedalus/state.toml new file mode 100644 index 0000000..17d6b3c --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/state.toml @@ -0,0 +1,48 @@ +# daedalus course learning project state. 保留注释;确定性字段由 CLI 更新。 +# +# Project state 只描述长期课程学习项目本身,不承载专题阶段进度。 +# 专题进度写在 `topics/<slug>/.daedalus/state.toml`。 +# +# 枚举约束: +# - task.lifecycle 只能是:active、idle、abandoned。 +# - task.workspace_bucket 固定为:projects。 +# - project.active_topic 必须指向 `[[topics]]` 中存在的 topic slug。 +# - topics.lifecycle 只能是:planned、active、blocked、awaiting-reflection、completed、abandoned、skipped。 +# - transition.action 只能是:init、topic-new、topic-activate、topic-await-reflection、topic-complete、topic-abandon、migrate、task-complete、abandon。 +# Agent 不要发明新的枚举值;如需新增,先修改 Rust 领域模型、模板和测试。 + +[task] +name = "hugging-face-llm-course" +kind = "course-learning" +created_at = "2026-06-14 14:42:45" +lifecycle = "active" +workspace_bucket = "projects" +next_action = "继续 course-learning active topic `lora-feedback-loop`,按课程阶段推进下一份 guide。" + +[project] +mode = "multi-topic" +active_topic = "lora-feedback-loop" +source_kind = "course" +source_name = "hugging-face-llm-course" +course_url = "https://huggingface.co/learn/llm-course/en" + +[[topics]] +slug = "lora-feedback-loop" +title = "LoRA 微调与数据反馈闭环" +lifecycle = "active" +path = "topics/lora-feedback-loop" +inherits = [] + +[[transitions]] +stage = "project" +action = "init" +timestamp = "2026-06-14 14:42:45" +actor = "daedalus-cli" +reason = "初始化 course learning project,并创建初始专题 `lora-feedback-loop`。" + +[[transitions]] +stage = "project" +action = "migrate" +timestamp = "2026-06-23 00:53:42" +actor = "daedalus-cli" +reason = "确认项目使用 course-learning 语义。" diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/topic-board.md b/workspaces/projects/hugging-face-llm-course/.daedalus/topic-board.md new file mode 100644 index 0000000..8f80aa0 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/topic-board.md @@ -0,0 +1,23 @@ +# Topic Board + +Topic Board 记录当前 course learning project 的专题列表、状态和继承关系。一次只能有一个 active topic。 + +## Active Topic + +- `lora-feedback-loop`:LoRA 微调与数据反馈闭环 + +## Topics + +| Topic | Title | Lifecycle | Path | Inherits | +| --- | --- | --- | --- | --- | +| `lora-feedback-loop` | LoRA 微调与数据反馈闭环 | active | [`topics/lora-feedback-loop`](../topics/lora-feedback-loop) | - | + +## Parking Lot + +- 待补充后续 topic。 + +## Rules + +- 新增专题必须使用 `daedalus topic new <slug> --title <title>`。 +- 切换专题必须使用 `daedalus topic activate <slug>`。 +- 专题完成后必须反向更新 [`shared/evidence-registry.md`](../shared/evidence-registry.md)。 diff --git a/workspaces/projects/hugging-face-llm-course/.daedalus/validation-log.md b/workspaces/projects/hugging-face-llm-course/.daedalus/validation-log.md new file mode 100644 index 0000000..ada216b --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/.daedalus/validation-log.md @@ -0,0 +1,17 @@ +# Project 验证日志 + +记录 daedalus 在 project/topic 编排层面的引导效果。 + +## 使用规则 + +- 普通学习笔记写入 active topic 的 `notes/`。 +- 专题阶段复盘写入 active topic 的 `.daedalus/validation-log.md`。 +- 这里只记录 project 层问题:topic 切换、shared context 复用、WIP 管理、迁移和归档。 + +## 2026-06-14 14:42:45 + +- 事件:初始化 course learning project。 +- 有效引导: +- 用户亲自完成的实践: +- Agent 代替用户过多的地方: +- Project/topic/template/CLI 改进建议: diff --git a/workspaces/projects/hugging-face-llm-course/CLAUDE.md b/workspaces/projects/hugging-face-llm-course/CLAUDE.md new file mode 100644 index 0000000..86bf049 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/CLAUDE.md @@ -0,0 +1,45 @@ +# Course Learning Project Context + +本目录是长期 course learning project `hugging-face-llm-course` 的工作区。 + +处理当前 project 前,先阅读 project 层上下文: + +@.daedalus/state.md +@.daedalus/project-map.md +@.daedalus/topic-board.md +@shared/syllabus-map.md +@shared/course-progress.md +@shared/concept-map.md +@shared/evidence-registry.md + +然后读取 active topic: + +@topics/lora-feedback-loop/.daedalus/state.md +@topics/lora-feedback-loop/.daedalus/task-card.md +@topics/lora-feedback-loop/.daedalus/outcome-map.md +@topics/lora-feedback-loop/.daedalus/todo.md +@topics/lora-feedback-loop/.daedalus/long-context.md + +## 操作规则 + +- Project root 的 `.daedalus/state.toml` 只描述 project lifecycle、workspace bucket、active topic 和 topic 列表。 +- Topic 的 `topics/<slug>/.daedalus/state.toml` 才描述 course-learning 阶段进度。 +- 创建新的 course learning project 必须使用 `daedalus init course-learning <project-name> --topic <topic-slug> --title <topic-title> --course-url <url>`。 +- 创建新专题必须使用 `daedalus topic new <topic-slug> --title <topic-title>`。 +- 切换专题必须使用 `daedalus topic activate <topic-slug>`。 +- 推进阶段时,`daedalus state enter/complete/block/resume <stage-id>` 默认操作 active topic,不操作 project root。 +- 如需指定专题,使用 `daedalus state complete <stage-id> --topic <topic-slug>`。 +- 关闭专题使用 `daedalus topic complete <topic-slug> --reason <reason>`;关闭整个 project 才使用 `daedalus task complete --reason <reason>`。 +- 不要手动编辑 `state.md`;需要更新时运行 `daedalus state render` 渲染 active topic,或 `daedalus state render --project` 渲染 project。 +- `daedalus validate` 校验 project + active topic;`daedalus validate --all-topics` 校验 project + 所有 topics。 +- 不要在 project root 手写 topic stage 状态,不要把 topic notes 写到 project root。 +- `shared/` 只保存跨 topic 可复用的 verified knowledge;topic 草稿和用户回答留在 `topics/<slug>/notes/`。 +- 每次继续学习前,先说明 project、active topic、topic stage、current gap 和完成后解锁什么。 +- 使用 `system/prompts/course` 下的分阶段 course 学习提示词来引导 active topic 的学习过程。 +- 不要恢复旧仓库学习阶段命名;课程学习新增内容必须使用 `system/prompts/course` 对应的 course-learning 阶段名。 +- 除非用户明确批准,或已有可追溯的等价证据,否则不要使用 `--force` 这类强制绕过选项。 +- 不要默认代替用户完成关键学习实践。Agent 应该说明要做什么、为什么做、用户等待时可以思考什么,并在用户完成实践后协助验收和排障。 +- Notes Ownership Rule:用户没有回答、观察或实践前,不要把结论写入 topic `notes/`。Agent 可以写 topic `guides/`,也可以在 topic `notes/` 中创建待用户填写的轻量模板。 +- Artifact Granularity Rule:topic `guides/**/README.md` 和 `notes/**/README.md` 只做阶段索引、时间线和恢复入口;具体 guide、机制解释、运行证据、用户复述、排障复盘都单独成篇,并从 README 链接。 +- `source/` 用来记录课程主页、章节入口、notebook、参考文档和可选源码链接,不默认假设要拉取外部 repo。 +- 跑通课程代码只是 lesson lab 的一部分;完成阶段前还要留下 input/output/shape/loss/artifact 观察或用户解释。 diff --git a/workspaces/projects/hugging-face-llm-course/shared/README.md b/workspaces/projects/hugging-face-llm-course/shared/README.md new file mode 100644 index 0000000..aaef67a --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/README.md @@ -0,0 +1,12 @@ +# Shared Context + +`shared/` 只保存跨 topic 可复用的已验证课程学习上下文。探索草稿、用户假设和专题内 lesson 观察应留在对应 `topics/<slug>/notes/`。 + +## Files + +- [`syllabus-map.md`](syllabus-map.md):课程章节、概念依赖和取舍边界。 +- [`course-progress.md`](course-progress.md):章节进度、当前 checkpoint 和复习状态。 +- [`concept-map.md`](concept-map.md):跨章节核心概念、机制问题和薄弱点。 +- [`glossary.md`](glossary.md):术语表。 +- [`evidence-registry.md`](evidence-registry.md):可继承证据。 +- [`transfer-patterns.md`](transfer-patterns.md):可迁移模式。 diff --git a/workspaces/projects/hugging-face-llm-course/shared/architecture-map.md b/workspaces/projects/hugging-face-llm-course/shared/architecture-map.md new file mode 100644 index 0000000..ef1309a --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/architecture-map.md @@ -0,0 +1,7 @@ +# Shared Architecture Map + +跨 topic 稳定架构边界。只放多个 topic 都会复用的结构,不放单个 topic 的局部阅读细节。 + +## Status + +- 状态:草稿 diff --git a/workspaces/projects/hugging-face-llm-course/shared/concept-map.md b/workspaces/projects/hugging-face-llm-course/shared/concept-map.md new file mode 100644 index 0000000..59a0021 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/concept-map.md @@ -0,0 +1,34 @@ +# Concept Map + +记录跨章节概念、机制问题和薄弱点。每个概念都应该能回到课程章节、实验观察或迁移任务。 + +## Core Concepts + +| Concept | 学习来源 | 机制问题 | 已验证证据 | 薄弱点 | +| --- | --- | --- | --- | --- | +| pipeline | task string 如何展开成 tokenizer、model、config 和 postprocess? | 已跑通 sentiment pipeline;用户已完成 Chapter 2 | 默认模型选择仍需固定 | 按需复习,不阻塞 Chapter 3 | +| tokenizer | 文本如何变成 `input_ids`、`attention_mask` 和可 batch 的张量?BPE / WordPiece 的切词差异是什么? | 已观察 tokenizer 输出和 decode;已记录 [`bpe-vs-wordpiece.md`](../topics/lora-feedback-loop/notes/04-lesson-lab/bpe-vs-wordpiece.md) | padding/truncation 与 batch 关系仍需练习 | 待补 lesson lab | +| sequence classification | 文本分类任务如何从 `text + label` 反推到 tokenizer、collator、model head、metric 和 Trainer? | 用户已完成 `sequence_classification.ipynb`;已观察 batch keys、loss、logits shape、argmax、id2label;已闭卷重写最小 flow;已生成 [`04-sequence-classification-derivation.md`](../topics/lora-feedback-loop/guides/04-lesson-lab/04-sequence-classification-derivation.md) | 小样本训练需避免未 shuffle 的标签偏斜;需确认 `id2label` 与数据集 label 语义一致 | 已通过,待迁移 | +| task heads | 不同任务如何改变 head、logits shape、loss 和 postprocess? | Chapter 1/5 已完成 Causal LM、sequence classification、token classification、question answering、summarization;已记录 [`06-token-classification-derivation.md`](../topics/lora-feedback-loop/guides/04-lesson-lab/06-token-classification-derivation.md)、[`07-question-answering-derivation.md`](../topics/lora-feedback-loop/guides/04-lesson-lab/07-question-answering-derivation.md)、[`08-summarization-derivation.md`](../topics/lora-feedback-loop/guides/04-lesson-lab/08-summarization-derivation.md) 及 summarization 运行 note | QA 严肃评估需补完整 span postprocess 与 EM/F1;Translation / ASR / Image classification 已跳过或延后 | 已够进入架构归纳 | +| architecture families | encoder-only、decoder-only、encoder-decoder 分别适合什么任务? | 已完成 Causal LM、sequence classification、token classification、question answering、summarization,可分别映射到 decoder-only、encoder-only、encoder-decoder;用户已复述 encoder-only 依赖完整上下文理解,见 [`2026-07-07-chapter1-6-encoder-only-recap.md`](../topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-encoder-only-recap.md);用户已复述 decoder-only 是续写当前 input 而不是从 input 摘取,见 [`2026-07-07-chapter1-6-decoder-only-recap.md`](../topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-decoder-only-recap.md);用户已辨析 summarization 可由多类架构实现,但 encoder-decoder 最贴合 source -> target 任务形态,见 [`2026-07-07-chapter1-6-encoder-decoder-recap.md`](../topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-encoder-decoder-recap.md);最终总结见 [`2026-07-07-chapter1-6-architecture-selection-summary.md`](../topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-architecture-selection-summary.md) | 已通过,待迁移到 pipeline / model class 选择 | 已通过 | +| attention mechanisms | full attention、LSH attention、local attention、axial positional encodings 分别解决什么长序列问题? | Chapter 1/6 已进入 attention mechanisms;官方指出标准 attention 是 O(n^2),长文本会成为计算瓶颈 | 需要用户复述 full attention 的成本,以及 LSH / local / axial positional encodings 各自牺牲和保留了什么 | 当前 lesson | +| LLM inference | LLM 如何从 prompt 逐 token 生成?推理成本、延迟和显存主要花在哪里? | 已有 Chapter 1/8 inference guide;用户确认 Chapter 1 已完成 | prefill/decode 与 KV cache 可在性能实践时复习 | 已越过,不阻塞 Chapter 3 | +| seq2seq generation | Encoder-decoder 任务如何从 source text 生成 target text? | 用户已完成 `summarization.ipynb`:BillSum `ca_test` 加载、T5 `summarize:` prefix、seq2seq labels、ROUGE 评估、`model.generate` 推理和 checkpoint 加载;已记录 [`2026-07-03-summarization-lab-recap.md`](../topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-03-summarization-lab-recap.md) | Translation 已按用户决定跳过/延后;如后续需要补做,只用来对比同构 seq2seq 流程下的 source/target 与指标差异 | 已够进入架构归纳 | +| pipeline internals | `pipeline("text-classification")` 如何展开成 tokenizer、model forward、softmax/argmax、id2label? | 已跑通过 pipeline smoke test 和 sequence classification inference | 先完成 Chapter 1/5 task labs,再手写 pipeline 等价流程 | 延后 | +| causal language modeling | GPT-2 为什么训练 next token prediction?`labels = input_ids.copy()` 为什么不是让模型复制输入? | 已完成 DistilGPT2 quick training run | shifted loss 机制仍需可视化 | 候选 mechanism deep dive | +| Trainer / full loop | Trainer 如何封装 dataloader、forward、loss、backward、optimizer、scheduler 与 evaluation?手写循环又暴露什么? | 用户已完成 3/4 机制学习,并拆解 shuffle、`**batch`、forward/backward/optimizer/scheduler/zero-grad 顺序;详见 [`17-chapter3-finetuning-pretrained-model.md`](../topics/lora-feedback-loop/guides/04-lesson-lab/17-chapter3-finetuning-pretrained-model.md) | 完整训练运行结果、evaluation 与 Accelerate 证据按需补齐 | 已够推进,待运行复习 | +| learning curves | train/validation loss 与 accuracy 的不同组合如何暴露收敛、过拟合、欠拟合或训练震荡? | 用户确认 Chapter 3 已读完;全章 guide 已提供诊断矩阵与单变量实验纪律 | 需要用户能从真实曲线提出原因假设、预期信号与下一次单变量实验 | 已读,待实验 | +| Hub model sharing | Hub Git 仓库、`from_pretrained()`、`push_to_hub()` 与 model card 如何共同支持版本化和复用? | 用户确认 Chapter 4 已读完 | 尚未验收实际上传、版本选择与 model card 实践 | 已读,待实践 | +| local / remote dataset loading | 非 Hub 数据如何由文件格式、`data_files`、split mapping 与 `field` 转成 DatasetDict? | 用户已进入 Chapter 5/2;Agent 已在同一 `.venv` 验证 GitHub URL、最终 raw URL、本地 `.json.gz` 均得到 train 442 / test 48,排障见 [`18-chapter5-2-local-remote-dataset-loading.md`](../topics/lora-feedback-loop/guides/04-lesson-lab/18-chapter5-2-local-remote-dataset-loading.md) | 需要用户亲自检查 loader name、source、split、rows/columns 和 nested field 的对应关系 | 当前 lesson | + +## Mechanism Questions + +- `ForCausalLMLoss` 如何 shift logits / labels? +- BPE / WordPiece 的切词策略差异如何影响 tokenizer 与 model 的匹配? +- `pipeline("text-generation")` 如何调用 tokenizer、model.generate 和 decode? +- `Trainer` 如何处理 MPS、dataloader、optimizer 和 scheduler? +- Chapter 1/6 中,架构选择到底由任务输出形态决定,还是由 attention 可见范围和生成方式共同决定? + +## Transfer Links + +- 将 Causal LM / Trainer 训练链路迁移到闲鱼买家 Agent suggestion next action SFT / LoRA feedback loop。 diff --git a/workspaces/projects/hugging-face-llm-course/shared/course-progress.md b/workspaces/projects/hugging-face-llm-course/shared/course-progress.md new file mode 100644 index 0000000..8aa199b --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/course-progress.md @@ -0,0 +1,35 @@ +# Course Progress + +记录课程进度、当前 checkpoint 和复习状态。这里是恢复学习现场的入口,不替代 topic todo。 + +## Current Checkpoint + +- Active topic:`lora-feedback-loop` +- 当前课程章节:Hugging Face LLM Course Chapter 5/2 `What if my dataset isn't on the Hub?` +- 当前 lesson / mechanism:用 `load_dataset(format, data_files=..., field=...)` 从本地或远程文件构造 DatasetDict,并显式映射 train/test split +- 当前 open question:Hub dataset repository ID 与 CSV/text/JSON/pandas 等通用 loader 名称分别在什么场景使用,`data_files` 如何决定 split 与数据来源 +- 下一步:阅读并观察 Chapter 5/2,先区分 local path、remote URL、压缩文件与 `data_files` split mapping,再检查加载后的 rows、columns 和嵌套结构 + +## Progress Board + +| Chapter / Section | Status | Evidence | Review | +| --- | --- | --- | --- | +| Chapter 1/5 How Transformers solve tasks | done-with-scope-cut | 5/8 labs complete:Causal LM / sequence classification / token classification / question answering / summarization;Translation / ASR / Image classification 按用户决定跳过或延后 | 足够进入架构归纳 | +| Chapter 1/6 Transformer Architectures | done-enough-to-advance | 架构家族已归纳;attention mechanisms 已完成 Agent-guided walkthrough,LSH/local/axial 可回看 guide | 若后续长上下文机制薄弱再回看 | +| Chapter 1/7 Ungraded quiz | passed-through | 用户已进入 Chapter 1/8 | 不单独归档 quiz 答案 | +| Chapter 1/8 Deep dive into Text Generation Inference with LLMs | done-by-user-confirmation | 已有 inference 主线 guide;用户确认 Chapter 1 已完成并已越过 Chapter 2 | 机制弱点按需回看 | +| Chapter 2 Using Transformers | done-by-user-confirmation | 已有 tokenizer、model、pipeline 与 forward 观察;用户于 2026-07-13 确认完成整章 | 深挖项不再阻塞前进 | +| Chapter 3/1 Introduction | done | 官方页与本章路线已确认;全章 guide 已生成 | 进入 3/2 | +| Chapter 3/2 Processing the data | read-complete | 用户确认已读完;API 漂移已记录,数据处理实验仍待单独验收 | 实验观察按需补齐 | +| Chapter 3/3 Fine-tuning with the Trainer API | read-complete | 用户确认已读完;Trainer 运行产物与指标尚未作为掌握证据验收 | 与 full loop 对照时复查 | +| Chapter 3/4 A full training loop | done-by-user-confirmation | 用户已完成 shuffle、`**batch`、forward/backward/optimizer/scheduler/zero-grad 顺序等机制学习;未虚构完整训练运行结果 | 运行证据按需补齐 | +| Chapter 3/5-3/7 Learning Curves / Check / Quiz | read-complete | 用户确认进入 Chapter 5/2 前的内容均已读完 | 曲线诊断实验仍待验收 | +| Chapter 4 Sharing models and tokenizers | read-complete | 用户确认整章已读完 | Hub 上传与 model card 实践未验收 | +| Chapter 5/1 Introduction | read-complete | 用户确认已读完;Datasets 深入学习路线已建立 | 进入 5/2 | +| Chapter 5/2 What if my dataset isn't on the Hub? | active | 官方页标题与 local/remote loading 主线已核对 | 当前 lesson | + +## Review Queue + +- Chapter 1/8 inference 与 Chapter 2 的深挖项保留为按需复习,不再作为 Chapter 3 的前置阻塞。 +- Chapter 3 的训练/曲线运行证据、Chapter 4 的 Hub 分享实践尚未验收,但不阻塞阅读游标。 +- Chapter 5/2:观察 `load_dataset("json", data_files=..., field=...)` 中 loader、source、split 与 nested field 的职责;延续完整 Hub ID 与通用文件 loader 的区分。 diff --git a/workspaces/projects/hugging-face-llm-course/shared/evidence-registry.md b/workspaces/projects/hugging-face-llm-course/shared/evidence-registry.md new file mode 100644 index 0000000..f873954 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/evidence-registry.md @@ -0,0 +1,18 @@ +# Evidence Registry + +这里保存跨 topic 可复用的 verified evidence。每条 evidence 必须有来源、边界和迁移限制。 + +## Template + +```markdown +## E-001: 标题 + +- 状态:verified / stale / superseded +- 来源 topic: +- 源码证据: +- 验证方式: +- 结论: +- 适用边界: +- 迁移限制: +- 被引用: +``` diff --git a/workspaces/projects/hugging-face-llm-course/shared/glossary.md b/workspaces/projects/hugging-face-llm-course/shared/glossary.md new file mode 100644 index 0000000..741fd62 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/glossary.md @@ -0,0 +1,7 @@ +# Glossary + +跨 topic 术语表。 + +## Terms + +- 待补充。 diff --git a/workspaces/projects/hugging-face-llm-course/shared/runbook.md b/workspaces/projects/hugging-face-llm-course/shared/runbook.md new file mode 100644 index 0000000..61a7afa --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/runbook.md @@ -0,0 +1,11 @@ +# Shared Runbook + +跨 topic 可复用的运行、构建、测试和调试入口。 + +## Status + +- 状态:草稿 + +## Commands + +- 待补充。 diff --git a/workspaces/projects/hugging-face-llm-course/shared/source-index.md b/workspaces/projects/hugging-face-llm-course/shared/source-index.md new file mode 100644 index 0000000..82dd3f4 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/source-index.md @@ -0,0 +1,7 @@ +# Source Index + +源码模块索引,帮助 topic 快速定位入口。 + +## Status + +- 状态:草稿 diff --git a/workspaces/projects/hugging-face-llm-course/shared/syllabus-map.md b/workspaces/projects/hugging-face-llm-course/shared/syllabus-map.md new file mode 100644 index 0000000..77a2c25 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/syllabus-map.md @@ -0,0 +1,27 @@ +# Syllabus Map + +把课程 syllabus 转换为 daedalus 学习路线图。不要盲目照搬课程目录;要标明必学、可跳过、延后和迁移关系。 + +## Course + +- URL:https://huggingface.co/learn/llm-course/en +- 学习策略:Chapter 1-4 已完成阅读;当前进入 Chapter 5 的 Datasets 深入学习,把数据加载、变换与规模化机制连接到后续 LoRA/SFT feedback loop。 +- 主线章节:1. Transformer Models;2. Using Transformers;3. Fine-tuning a pretrained model;4. Sharing models and tokenizers;5. The Datasets library。 +- 旁路章节:部署优化、认证考试、非当前任务必要的章节先延后。 +- 暂不学习:完整 RLHF / DPO / 大规模分布式训练,等第一轮 SFT/LoRA 闭环跑通后再进入。 + +## Chapter Map + +| Chapter / Section | 概念目标 | 依赖 | 必做练习 | 状态 | 证据 | +| --- | --- | --- | --- | --- | --- | +| Chapter 1 Transformer Models | 建立 pipeline、Transformer、Causal LM 和 LLM inference 直觉 | 无 | Text generation / Causal LM quick run;Chapter 1/5 文本任务观察;Chapter 1/6 架构归纳;Chapter 1/8 inference 主线 | done | 已有 task labs、架构归纳与 inference guide;用户确认已学完并进入 Chapter 3 | +| Chapter 2 Using Transformers | 理解 tokenizer、model、batch 和 forward 的基本组合 | Chapter 1 | tokenizer / model lesson lab | done | 已有 pipeline、tokenizer、model forward 与 task-head 观察;用户于 2026-07-13 确认完成 Chapter 2 | +| Chapter 3 Fine-tuning a pretrained model | 掌握数据预处理、Trainer API、完整训练循环与 Accelerate 的职责边界 | Chapter 1/2 | 依次观察 processing、Trainer、full loop 与 learning curves | read-complete | 用户确认整章已读完;机制 guide 已生成,部分运行与曲线诊断证据待验收 | +| Chapter 4 Sharing models and tokenizers | 理解 Hub 仓库、加载/分享模型与 tokenizer、model card 和版本化 | Chapter 1-3 | 使用预训练模型;分享模型;构建 model card | read-complete | 用户确认整章已读完;未声称完成实际上传或 model card 实践 | +| Chapter 5 The Datasets library | 掌握非 Hub 数据加载、数据变换、大数据/Arrow、创建数据集与语义搜索 | Chapter 3/4 | 从本地与远程文件加载 dataset;slice/dice;大数据;创建与检索 | active | Chapter 5/1 已读完;当前进入 Chapter 5/2 `What if my dataset isn't on the Hub?` | + +## Stop Rules + +- 不因为课程有章节就默认全量学习。 +- 不把看完文字等同于掌握概念。 +- 不把跑通示例代码等同于能迁移到真实任务。 diff --git a/workspaces/projects/hugging-face-llm-course/shared/transfer-patterns.md b/workspaces/projects/hugging-face-llm-course/shared/transfer-patterns.md new file mode 100644 index 0000000..143c829 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/shared/transfer-patterns.md @@ -0,0 +1,15 @@ +# Transfer Patterns + +跨 topic 可迁移模式。只归档已通过 topic demo、业务迁移或源码证据验证的模式。 + +记录模式时必须保留: + +- 它解决的现实问题。 +- 它成立的约束。 +- topic demo 中为了学习而忠实模仿的部分。 +- 它的局限、failure mode 和不应照抄的部分。 +- 迁移到新业务场景前必须重新验证的约束。 + +## Patterns + +- 待补充。 diff --git a/workspaces/projects/hugging-face-llm-course/source/.gitignore b/workspaces/projects/hugging-face-llm-course/source/.gitignore new file mode 100644 index 0000000..f249c31 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/source/.gitignore @@ -0,0 +1,6 @@ +# 外部源码默认不进入 daedalus 仓库。 +* +!.gitignore +!.gitkeep +!README.md +!pull_source.sh diff --git a/workspaces/projects/hugging-face-llm-course/source/README.md b/workspaces/projects/hugging-face-llm-course/source/README.md new file mode 100644 index 0000000..dded0fb --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/source/README.md @@ -0,0 +1,12 @@ +# Source + +这里用于放置当前学习任务的外部源码材料。 + +默认规则: + +- 外部源码目录不会进入 daedalus 仓库版本控制。 +- 优先由用户执行 [`pull_source.sh`](pull_source.sh) 拉取源码,并记录自己的运行结果。 +- Agent 可以生成或调整拉取脚本、解释错误、给出排障建议,但不要默认代替用户完成源码准备。 +- 如果用户明确要求 Agent 代为拉取,完成后应删除外部仓库自带的 `.git` 目录,避免嵌套仓库污染当前知识库。 +- 实际运行和调试外部 repo 时,推荐在该 repo 根目录单独打开 Cursor 窗口;`.vscode/launch.json`、断点路径和启动命令都应以被学习 repo 的 `${workspaceFolder}` 为基准。 +- daedalus 任务目录只保存拉取脚本、学习指南、用户笔记和复盘,不假设自己就是外部 repo 的运行 workspace。 diff --git a/workspaces/projects/hugging-face-llm-course/source/pull_source.sh b/workspaces/projects/hugging-face-llm-course/source/pull_source.sh new file mode 100644 index 0000000..57032ef --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/source/pull_source.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 用法: +# REPO_URL="https://github.com/example/repo.git" \ +# REPO_DIR="repo" \ +# PINNED_REF="main" \ +# ./pull_source.sh +# +# 说明: +# - 该脚本只负责可复现地拉取学习原材料。 +# - 外部源码默认被 source/.gitignore 忽略,不应提交到 daedalus 仓库。 +# - 拉取后会删除外部仓库自带的 .git 目录,避免嵌套 git 仓库污染当前项目。 + +REPO_URL="${REPO_URL:-}" +REPO_DIR="${REPO_DIR:-}" +PINNED_REF="${PINNED_REF:-}" + +if [[ -z "$REPO_URL" || -z "$REPO_DIR" || -z "$PINNED_REF" ]]; then + echo "error: REPO_URL, REPO_DIR and PINNED_REF are required" >&2 + exit 1 +fi + +if [[ -e "$REPO_DIR" ]]; then + echo "skip: $REPO_DIR already exists" + exit 0 +fi + +git clone --depth 1 "$REPO_URL" "$REPO_DIR" +git -C "$REPO_DIR" fetch --depth 1 origin "$PINNED_REF" || true +git -C "$REPO_DIR" checkout "$PINNED_REF" +rm -rf "$REPO_DIR/.git" + +echo "ok: source pulled into $REPO_DIR at $PINNED_REF" diff --git a/workspaces/projects/hugging-face-llm-course/topics/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/artifact-index.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/artifact-index.md new file mode 100644 index 0000000..4ff1d76 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/artifact-index.md @@ -0,0 +1,21 @@ +# 产物索引 + +记录学习过程中产生的产物,以及它们服务的学习目的。 + +> `状态` 列只能使用 `草稿`、`已验证`、`待补充`、`不适用`。不要发明新的状态值;如需扩展,先更新模板、CLI 测试和相关提示词。 + +| 产物 | 阶段 | 目的 | 状态 | +| --- | --- | --- | --- | +| [`.daedalus/task-card.md`](task-card.md) | 01-need-aligner | 学习目标与验收标准 | 草稿 | +| [`.daedalus/outcome-map.md`](outcome-map.md) | all | 全程导航:最终产物、当前位置、缺口、依赖和停止规则 | 草稿 | +| [`.daedalus/validation-log.md`](validation-log.md) | all | 记录 daedalus 教学引导效果与改进点 | 草稿 | +| [`guides/`](../guides) | all | Agent 生成的行动指南与验收清单 | 草稿 | +| [`notes/`](../notes) | all | 用户亲自实践后的学习笔记 | 草稿 | +| [`reflection/closeout.md`](../reflection/closeout.md) | 09-closeout-archive | 用户完成 topic 后的主动回顾总结,是进入 knowledge-base 前的必要证据 | 草稿 | +| [`guides/02-syllabus-mapper/README.md`](../guides/02-syllabus-mapper/README.md) | 02-syllabus-mapper | syllabus 取舍、章节依赖和学习路线入口 | 草稿 | +| [`guides/03-concept-roadmap/README.md`](../guides/03-concept-roadmap/README.md) | 03-concept-roadmap | 概念问题、机制 checkpoint 和薄弱点入口 | 草稿 | +| [`guides/04-lesson-lab/README.md`](../guides/04-lesson-lab/README.md) | 04-lesson-lab | lesson 可观察实验指南入口 | 草稿 | +| [`notes/04-lesson-lab/README.md`](../notes/04-lesson-lab/README.md) | 04-lesson-lab | 用户运行、观察和解释证据入口 | 草稿 | +| [`guides/05-mechanism-deep-dive/README.md`](../guides/05-mechanism-deep-dive/README.md) | 05-mechanism-deep-dive | 被 API 隐藏机制的必要深挖入口 | 草稿 | +| [`guides/06-practice-transfer/README.md`](../guides/06-practice-transfer/README.md) | 06-practice-transfer | 课程概念迁移到真实任务的练习入口 | 草稿 | +| [`review/mastery-map.md`](../review/mastery-map.md) | 08-review-loop | 掌握度验证入口 | 草稿 | diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/decision-log.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/decision-log.md new file mode 100644 index 0000000..423b133 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/decision-log.md @@ -0,0 +1,9 @@ +# 决策记录 + +记录会影响学习方向、任务范围或状态流转的关键决策。 + +## 2026-06-14 14:42:45 + +- 决策:初始化 course learning 任务 `hugging-face-llm-course`。 +- 原因:开启一次 filesystem-first 的 daedalus 学习闭环。 +- 影响:当前阶段为 `01-need-aligner`。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/long-context.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/long-context.md new file mode 100644 index 0000000..e929a65 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/long-context.md @@ -0,0 +1,20 @@ +# 长期上下文 + +只保留可长期复用的上下文,不要粘贴聊天记录。 + +## 角色边界 + +- `guides/` 保存 Agent 给用户的行动指南。 +- `notes/` 保存用户亲自实践和思考后的学习笔记。 +- `source/` 保存外部源码缓存,默认不提交到 daedalus 仓库。 +- `.daedalus/validation-log.md` 保存 daedalus 自身教学效果的复盘。 + +## 目标 + +## 决策 + +## 已验证结论 + +## 未解决问题 + +## 恢复上下文提示 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/outcome-map.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/outcome-map.md new file mode 100644 index 0000000..bc264f4 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/outcome-map.md @@ -0,0 +1,141 @@ +# Outcome Map + +Outcome Map 是 topic 导航仪表盘,不是聊天总结。每次继续学习、深读源码、切换阶段或更新 todo 前,先用它定位:本 topic 的最终产物是什么、当前在哪、正在补哪个缺口、哪些细节停止扩展。 + +## North Star + +- Project:`hugging-face-llm-course` +- Topic:`lora-feedback-loop` - LoRA 微调与数据反馈闭环 +- 最终要获得的能力:用系统化训练闭环理解 AI fine-tuning,而不是停留在 API glue code;能判断数据、训练、评测、反馈和迁移边界。 +- 最小可验证 lesson lab:先用 Hugging Face Course 1/2 建立 Transformers 基础,再迁移到闲鱼二手买家 Agent 的 suggestion next action LoRA feedback loop lab。 +- 现实问题中的迁移目标:为 AI Agent 开发补齐模型层和训练层判断力,形成下一份 AI 开发岗位可展示、可解释的技术作品。 + +## Inherited Context + +- Syllabus map:[`../../shared/syllabus-map.md`](../../shared/syllabus-map.md) +- Course progress:[`../../shared/course-progress.md`](../../shared/course-progress.md) +- Concept map:[`../../shared/concept-map.md`](../../shared/concept-map.md) +- Shared evidence:[`../../shared/evidence-registry.md`](../../shared/evidence-registry.md) + +## Final Artifacts + +| 产物 | 用途 | 状态 | +| --- | --- | --- | +| [`.daedalus/task-card.md`](task-card.md) | 学习目标与验收标准 | 已确认草案 | +| [`guides/01-need-aligner/02-secondhand-buyer-agent-research.md`](../guides/01-need-aligner/02-secondhand-buyer-agent-research.md) | 二手买家 Agent 业务痛点与 fine-tuning suitability gate | Agent 调研完成,待用户确认 | +| [`guides/02-syllabus-mapper/02-schema-and-engineering-shape.md`](../guides/02-syllabus-mapper/02-schema-and-engineering-shape.md) | 输入输出 schema、rubric 与 production-shaped 工程目录 | 草案完成,待用户 review | +| [`guides/02-syllabus-mapper/03-first-lab-default-plan.md`](../guides/02-syllabus-mapper/03-first-lab-default-plan.md) | 第一轮模型、训练环境、数据规模与材料入口默认方案 | 草案完成,待用户 review | +| [`guides/06-practice-transfer/README.md`](../guides/06-practice-transfer/README.md) | 递进问题路线图与第一批数据审核标准 | 已生成,待用户用样本校准 | +| [`guides/06-practice-transfer/01-candidate-samples-v0.1.md`](../guides/06-practice-transfer/01-candidate-samples-v0.1.md) | 第一批 20 条候选样本 | 已生成,待用户审核 | +| [`guides/06-practice-transfer/02-candidate-samples-v0.2.md`](../guides/06-practice-transfer/02-candidate-samples-v0.2.md) | 第一批 20 条短句数组候选样本 | 已生成,待用户审核 | +| [`guides/06-practice-transfer/03-candidate-samples-v0.3.md`](../guides/06-practice-transfer/03-candidate-samples-v0.3.md) | `{s,r}` schema 候选样本 | 已生成,待用户审核 | +| [`guides/06-practice-transfer/04-candidate-samples-v0.4.md`](../guides/06-practice-transfer/04-candidate-samples-v0.4.md) | 用户口吻 Agent 指令样本 | 已生成,待用户审核 | +| [`guides/03-concept-roadmap/README.md`](../guides/03-concept-roadmap/README.md) | HF Course 1/2 概念路线图 | 已生成,当前执行 | +| [`guides/04-lesson-lab/README.md`](../guides/04-lesson-lab/README.md) | HF lesson guide 索引 | 已索引化,当前执行 | +| [`guides/04-lesson-lab/17-chapter3-finetuning-pretrained-model.md`](../guides/04-lesson-lab/17-chapter3-finetuning-pretrained-model.md) | Chapter 3 数据处理、Trainer、full loop、Accelerate、learning curves 与 LoRA 迁移 guide | Chapter 3 已读完;保留未验收运行证据 | +| [`guides/04-lesson-lab/18-chapter5-2-local-remote-dataset-loading.md`](../guides/04-lesson-lab/18-chapter5-2-local-remote-dataset-loading.md) | Chapter 5/2 本地/远程文件加载与 URL 探测排障 guide | Agent 已验证三种来源;待用户观察并复述数据契约 | +| [`guides/04-lesson-lab/06-token-classification-derivation.md`](../guides/04-lesson-lab/06-token-classification-derivation.md) | Token classification 任务契约与 label alignment guide | 已完成 | +| [`guides/04-lesson-lab/07-question-answering-derivation.md`](../guides/04-lesson-lab/07-question-answering-derivation.md) | Question answering 字符答案到 token span 的任务契约与排障 guide | 已完成基础观察 | +| [`guides/04-lesson-lab/08-summarization-derivation.md`](../guides/04-lesson-lab/08-summarization-derivation.md) | Summarization encoder-decoder 任务形状 guide | 已完成 | +| [`guides/04-lesson-lab/09-t5-task-prefix.md`](../guides/04-lesson-lab/09-t5-task-prefix.md) | T5 `summarize:` task prefix guide | 已完成 | +| [`guides/04-lesson-lab/10-billsum-dataset-loading.md`](../guides/04-lesson-lab/10-billsum-dataset-loading.md) | BillSum dataset / split / notebook 变量排障 guide | 已完成 | +| [`guides/04-lesson-lab/11-rouge-metric.md`](../guides/04-lesson-lab/11-rouge-metric.md) | ROUGE metric guide | 已完成 | +| [`guides/04-lesson-lab/12-mixed-precision-fp16-bf16.md`](../guides/04-lesson-lab/12-mixed-precision-fp16-bf16.md) | Mixed precision guide | 已完成 | +| [`guides/04-lesson-lab/13-summarization-compute-metrics-overflow.md`](../guides/04-lesson-lab/13-summarization-compute-metrics-overflow.md) | Summarization metric decode overflow debug guide | 已完成 | +| [`guides/04-lesson-lab/14-summarization-inference-generate.md`](../guides/04-lesson-lab/14-summarization-inference-generate.md) | Summarization inference / `generate` guide | 已完成 | +| [`notes/04-lesson-lab/README.md`](../notes/04-lesson-lab/README.md) | Lesson lab notes 索引 | 已索引化 | +| [`notes/04-lesson-lab/2026-06-30-token-classification-lab-recap.md`](../notes/04-lesson-lab/2026-06-30-token-classification-lab-recap.md) | Token classification 用户复述与完成证据 | 已完成 | +| [`notes/04-lesson-lab/2026-07-03-summarization-lab-recap.md`](../notes/04-lesson-lab/2026-07-03-summarization-lab-recap.md) | Summarization notebook 运行、排障和推理证据 | 已完成 | +| [`demo/design.md`](../demo/design.md) | mini demo 设计草案与定稿 | 待填 | +| [`demo/README.md`](../demo/README.md) | mini demo 实现、运行和验收说明 | 待填 | +| [`guides/06-practice-transfer/README.md`](../guides/06-practice-transfer/README.md) | 业务迁移练习 | 待填 | +| knowledge-base entry | 已验证知识归档 | 待填 | + +## Current Position + +Current Position 是学习地图,不是实现事实源。涉及实现阶段时,必须用当前代码、测试、运行输出和用户已验证观察校准后再判断完成度。 + +- 当前阶段:04-lesson-lab / Chapter 5/2 What if my dataset isn't on the Hub?。 +- 当前目标:从只会加载 Hub dataset 推进到能从本地或远程 CSV/text/JSON/pandas 文件构造结构正确的 DatasetDict。 +- 当前障碍:不要把一次 URL 探测失败等同于文件不存在;还需显式理解 `data_files` 如何绑定 source 与 split,以及 JSON `field` 如何选择嵌套数据入口。 +- 当前动作服务的产物:`shared/course-progress.md`、后续 Chapter 5 focused guide 与用户加载观察。 +- 当前光标:Chapter 5/2 `What if my dataset isn't on the Hub?`(https://huggingface.co/learn/llm-course/en/chapter5/2)。 + +## Contribution Back To Project + +- 本 topic 完成后预计新增或更新哪些 shared evidence: +- 哪些 shared glossary / architecture-map 需要更新: +- 哪些 future topics 被发现: + +## Artifact Dependency Graph + +```text +task-card -> syllabus-map -> concept-roadmap -> lesson-lab +lesson-lab -> mechanism-deep-dive -> practice-transfer +practice-transfer -> capstone/review -> knowledge-base entry +``` + +## Open Gaps + +- [x] 确认最终要获得的能力:AI Agent 工程师的 fine-tuning / training loop 能力补齐。 +- [x] 确认最小可验证 demo:闲鱼二手买家 Agent 的 suggestion next action LoRA fine-tuning feedback loop lab。 +- [x] 确认业务迁移目标:提升短期 AI 岗位竞争力,同时恢复长期底层能力信心。 +- [x] 确认数据集任务类型:贴近 AI Agent 的 suggestion next action 生成。 +- [x] 完成 Agent 调研版 fine-tuning suitability:生产先 prompt/context/tooling,学习 topic 中用 LoRA 做对照实验。 +- [x] 确认第一轮品类:手机。 +- [x] 确认核心质量标准:推动购买任务往前、多帮用户想一步,并满足安全性、语义完整性和相关性。 +- [x] 确认手机品类初始样例集结构、suggestion 输出 schema、购买阶段枚举和 eval rubric 草案。 +- [x] 确认具体小模型、训练环境和第一批样例数据规模。 +- [x] 确认材料入口:围绕 lab 反向读取 Hugging Face LLM Course + PEFT/TRL/Transformers/Datasets 官方文档。 +- [x] 生成 `06-practice-transfer` 问题路线图和第一批数据审核标准。 +- [x] 生成第一批 20 条候选样本并等待用户审核。 +- [x] 跑通第一个 Hugging Face Transformers pipeline smoke test。 +- [x] 跑通 Causal LM quick training run。 +- [x] 跑通 sequence classification lab。 +- [x] 跑通 sequence classification pipeline load / inference。 +- [x] 完成 sequence classification derivation review。 +- [x] 完成 sequence classification closed-book rewrite。 +- [x] 完成 Chapter 1/5 task lab sweep 的文本主线;剩余 Translation / ASR / Image classification 已按用户决定跳过/延后,不再阻塞 Chapter 1/6。 +- [x] Lab 1/8 Text generation / Causal LM。 +- [x] Lab 2/8 Text classification。 +- [x] Lab 3/8 Token classification。 +- [x] Lab 4/8 Question answering。 +- [x] Lab 5/8 Summarization。 +- [x] Lab 6/8 Translation:跳过/延后。 +- [x] Lab 7/8 Automatic speech recognition:跳过/延后。 +- [x] Lab 8/8 Image classification:跳过/延后。 +- [x] Chapter 1/6 Transformer Architectures:已完成 encoder-only、decoder-only、encoder-decoder 的任务适配归纳;attention mechanisms 已完成到可推进。 +- [x] Chapter 1/7 Ungraded quiz:用户已越过并进入 Chapter 1/8。 +- [x] Chapter 1/8 Deep dive into Text Generation Inference with LLMs:用户确认 Chapter 1 已完成。 +- [x] 完成 HF Course 第 1、2 章 foundation sprint:用户于 2026-07-13 确认完成 Chapter 2。 +- [x] Chapter 3/1 Introduction:全章路线与产出预期已建立。 +- [x] Chapter 3/2 Processing the data:用户确认已读完;实验观察仍在待验收队列。 +- [x] Chapter 3/3 Fine-tuning with Trainer API:用户确认已读完;运行产物与指标仍在待验收队列。 +- [x] Chapter 3/4 A full training loop:用户已完成机制学习;完整训练运行证据按需补齐。 +- [x] Chapter 3/5-3/7:用户确认已读完;learning curves 观察与单变量诊断实验仍待验收。 +- [x] Chapter 4 Sharing models and tokenizers:用户确认已读完;Hub 上传与 model card 实践仍待验收。 +- [x] Chapter 5/1 Introduction:用户确认已读完。 +- [ ] Chapter 5/2 What if my dataset isn't on the Hub?:当前阅读与加载观察 lesson。 +- [ ] 解释并记录 pipeline 的 task、默认 model、tokenizer、config、postprocess。 +- [ ] 用户审核至少 5-10 条候选样本。 + +## Why This Step Matters + +当前步骤决定后续应该学哪些章节、停止哪些材料、最终 lesson lab 和迁移任务如何验收。 + +## Critical Lens + +Critical Lens 用来防止把学习素材当成权威。它不是反对模仿,而是让 demo 的模仿变成有意识、有证据、有边界的学习动作。 + +- 当前素材中可能被过度神化的设计:Hugging Face 工具链和热门 fine-tuning recipe 可能把工具使用伪装成能力掌握。 +- 当前 demo 需要忠实模仿的核心机制:面向真实 Agent 子任务的数据集构造、adapter 训练、独立 eval、错误样本反馈和再训练对比。 +- 当前 demo 不应无意识照抄的设计:大规模训练工程、复杂 RLHF pipeline、榜单导向调参。 +- 当前还没有验证的素材假设:小模型和小数据集能否足够展示 suggestion next action 质量的可评测变化;模糊、边界和 negative/unsafe case 是否能被 eval 稳定区分。 +- 当前可以尝试简化、改进或丢弃的部分:先用小任务和轻量模型证明闭环,不追生产级平台。 +- 当前迁移到业务场景前必须重新验证的约束:真实任务数据质量、评测一致性、隐私/合规、训练成本和线上回归风险。 + +## Stop Rules + +- 不学与 North Star、lesson lab、迁移任务或知识归档无关的课程细节。 +- 不因为课程还有章节没覆盖完就继续学。 +- 不把 Agent-only 预读写成用户已经掌握的 notes。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/reviews/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/reviews/README.md new file mode 100644 index 0000000..bf616b2 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/reviews/README.md @@ -0,0 +1,17 @@ +# Topic Reviews + +Topic-level reviews are independent from the topic learning lifecycle. They do not reopen completed stages; they help the user rebuild, apply, and repair understanding from already created artifacts. + +Review questions must start from: + +```text +业务目标 / 现实任务 + -> 现实制约 + -> naive solution 为什么失败 + -> 核心抽象 / 不变量 + -> 实现机制 + -> trade-off + -> 对比最佳实践 + -> 可迁移模式 + -> 复习题 / 应用题 +``` diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/state.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/state.md new file mode 100644 index 0000000..5955341 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/state.md @@ -0,0 +1,60 @@ +# Topic 学习状态 + +> 从 [`.daedalus/state.toml`](state.toml) 生成。不要手动编辑。 + +## 当前状态 + +- Topic:`lora-feedback-loop` - LoRA 微调与数据反馈闭环 +- 生命周期:`active` +- 当前阶段:`04-lesson-lab` +- 状态:`active` +- 下一步:继续 `04-lesson-lab`:只推进 Chapter 5/2 `What if my dataset isn't on the Hub?`,先区分通用文件 loader、local/remote source、`data_files` split mapping 与 JSON `field`,并检查加载后的 DatasetDict 结构。 + +## 枚举约束 + +- `topic.lifecycle` 只能是:`planned`、`active`、`blocked`、`awaiting-reflection`、`completed`、`abandoned`、`skipped`。 +- `stage.status` 只能是:`pending`、`active`、`blocked`、`paused`、`done`。 +- `transition.action` 只能是:`init`、`enter`、`complete`、`block`、`resume`、`rollback`、`topic-await-reflection`、`topic-complete`、`topic-abandon`。 +- `transition.approval_source` 只能是:`user-confirmed`、`artifact-equivalent`、`stage-not-applicable`。 +- Agent 不要发明新的枚举值;如需新增,先修改 Rust 领域模型、模板和测试。 + +## 阶段进度 + +- `01-need-aligner`: 对齐课程学习诉求 (done) +- `02-syllabus-mapper`: 映射课程 syllabus (done) +- `03-concept-roadmap`: 建立概念路线图 (done) +- `04-lesson-lab`: 把课程 lesson 变成可观察实验 (active) +- `05-mechanism-deep-dive`: 拆解被 API 隐藏的机制 (pending) +- `06-practice-transfer`: 迁移课程概念到真实任务 (pending) +- `07-capstone-lab`: 完成课程驱动 mini project (pending) +- `08-review-loop`: 复习与掌握度验证 (pending) +- `09-closeout-archive`: 专题回顾与知识归档 (pending) + +## 缺失产物 + +- [`guides/05-mechanism-deep-dive/README.md`](../guides/05-mechanism-deep-dive/README.md) 属于 `05-mechanism-deep-dive` +- [`demo/design.md`](../demo/design.md) 属于 `07-capstone-lab` +- [`review/mastery-map.md`](../review/mastery-map.md) 属于 `08-review-loop` +- [`review/question-bank.md`](../review/question-bank.md) 属于 `08-review-loop` + +## 阻塞项 + +- 无 + +## 最近状态流转 + +> 共 8 条状态流转;下面显示最近 8 条,完整历史见 [`.daedalus/state.toml`](state.toml) 的 `[[transitions]]`。 + +- `2026-06-14 14:42:45` 由 `daedalus-cli` 对 `01-need-aligner` 执行 `init`:初始化 course learning topic。 +- `2026-06-14 20:44:11` 由 `daedalus-cli` 对 `01-need-aligner` 执行 `complete`:用户确认 fine-tuning suitability、第一轮手机品类和 suggestion next action 质量标准,01 目标对齐完成。 +- `2026-06-14 20:44:11` 由 `daedalus-cli` 对 `02-syllabus-mapper` 执行 `enter`:进入 Hugging Face LLM Course source/material scout,选择支持 baseline + LoRA 对照实验的最小材料集合。 +- `2026-06-14 21:32:51` 由 `daedalus-cli` 对 `02-syllabus-mapper` 执行 `complete`:完成材料入口、schema、工程形态、默认模型、训练环境和数据规模收敛。 +- `2026-06-14 21:32:51` 由 `daedalus-cli` 对 `06-practice-transfer` 执行 `enter`:进入问题路线图阶段,将默认方案转成数据生成标准、审核问题和 demo 设计前置问题。 +- `2026-06-23 00:53:42` 由 `daedalus-cli` 对 `03-concept-roadmap` 执行 `resume`:将 topic 阶段映射为 course-learning 阶段,并迁移 guide/notes 到 course-learning 目录。 +- `2026-06-23 01:03:06` 由 `daedalus-cli` 对 `03-concept-roadmap` 执行 `complete`:完成 HF Course 1/2 concept roadmap,并同步 shared concept/progress/syllabus maps。 +- `2026-06-23 01:03:06` 由 `daedalus-cli` 对 `04-lesson-lab` 执行 `enter`:进入 course-learning lesson lab,执行 Trainer 拆解 guide 并沉淀用户观察证据。 + +## 下一步 CLI 建议 + +- `daedalus state render --topic-dir /Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop` +- 完成阶段前先运行 `daedalus validate`。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/state.toml b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/state.toml new file mode 100644 index 0000000..f2f07c5 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/state.toml @@ -0,0 +1,132 @@ +# daedalus course learning topic state. 保留注释;确定性字段由 CLI 更新。 +# +# Topic state 承载 course-learning 专题学习进度。Project state 只负责 project lifecycle +# 和 active topic 指针。 +# +# 枚举约束: +# - topic.lifecycle 只能是:planned、active、blocked、awaiting-reflection、completed、abandoned、skipped。 +# - stage.status 只能是:pending、active、blocked、paused、done。 +# - transition.action 只能是:init、enter、complete、block、resume、rollback、topic-await-reflection、topic-complete、topic-abandon。 +# - transition.approval_source 只能是:user-confirmed、artifact-equivalent、stage-not-applicable。 +# Agent 不要发明新的枚举值;如需新增,先修改 Rust 领域模型、模板和测试。 +# required_artifacts 表示阶段完成所需文件。CLI 只校验文件存在; +# Agent 仍需确认 notes 是否包含用户确认、亲自实践或手写笔记后的证据。 + +[topic] +slug = "lora-feedback-loop" +title = "LoRA 微调与数据反馈闭环" +kind = "course-learning-topic" +parent_project = "../.." +lifecycle = "active" +current_phase = "04-lesson-lab" +next_action = "继续 `04-lesson-lab`:只推进 Chapter 5/2 `What if my dataset isn't on the Hub?`,先区分通用文件 loader、local/remote source、`data_files` split mapping 与 JSON `field`,并检查加载后的 DatasetDict 结构。" + +[[stages]] +id = "01-need-aligner" +title = "对齐课程学习诉求" +status = "done" +required_artifacts = [".daedalus/task-card.md", ".daedalus/outcome-map.md"] + +[[stages]] +id = "02-syllabus-mapper" +title = "映射课程 syllabus" +status = "done" +required_artifacts = ["guides/02-syllabus-mapper/README.md"] + +[[stages]] +id = "03-concept-roadmap" +title = "建立概念路线图" +status = "done" +required_artifacts = ["guides/03-concept-roadmap/README.md", "../../shared/concept-map.md"] + +[[stages]] +id = "04-lesson-lab" +title = "把课程 lesson 变成可观察实验" +status = "active" +required_artifacts = ["guides/04-lesson-lab/README.md", "notes/04-lesson-lab/README.md"] + +[[stages]] +id = "05-mechanism-deep-dive" +title = "拆解被 API 隐藏的机制" +status = "pending" +required_artifacts = ["guides/05-mechanism-deep-dive/README.md"] + +[[stages]] +id = "06-practice-transfer" +title = "迁移课程概念到真实任务" +status = "pending" +required_artifacts = ["guides/06-practice-transfer/README.md"] + +[[stages]] +id = "07-capstone-lab" +title = "完成课程驱动 mini project" +status = "pending" +required_artifacts = ["demo/design.md"] + +[[stages]] +id = "08-review-loop" +title = "复习与掌握度验证" +status = "pending" +required_artifacts = ["review/mastery-map.md", "review/question-bank.md"] + +[[stages]] +id = "09-closeout-archive" +title = "专题回顾与知识归档" +status = "pending" +required_artifacts = [".daedalus/artifact-index.md", ".daedalus/long-context.md", "reflection/candidate-map.md", "reflection/closeout.md"] + +[[transitions]] +stage = "01-need-aligner" +action = "init" +timestamp = "2026-06-14 14:42:45" +actor = "daedalus-cli" +reason = "初始化 course learning topic。" + +[[transitions]] +stage = "01-need-aligner" +action = "complete" +timestamp = "2026-06-14 20:44:11" +actor = "daedalus-cli" +reason = "用户确认 fine-tuning suitability、第一轮手机品类和 suggestion next action 质量标准,01 目标对齐完成。" + +[[transitions]] +stage = "02-syllabus-mapper" +action = "enter" +timestamp = "2026-06-14 20:44:11" +actor = "daedalus-cli" +reason = "进入 Hugging Face LLM Course source/material scout,选择支持 baseline + LoRA 对照实验的最小材料集合。" + +[[transitions]] +stage = "02-syllabus-mapper" +action = "complete" +timestamp = "2026-06-14 21:32:51" +actor = "daedalus-cli" +reason = "完成材料入口、schema、工程形态、默认模型、训练环境和数据规模收敛。" + +[[transitions]] +stage = "06-practice-transfer" +action = "enter" +timestamp = "2026-06-14 21:32:51" +actor = "daedalus-cli" +reason = "进入问题路线图阶段,将默认方案转成数据生成标准、审核问题和 demo 设计前置问题。" + +[[transitions]] +stage = "03-concept-roadmap" +action = "resume" +timestamp = "2026-06-23 00:53:42" +actor = "daedalus-cli" +reason = "将 topic 阶段映射为 course-learning 阶段,并迁移 guide/notes 到 course-learning 目录。" + +[[transitions]] +stage = "03-concept-roadmap" +action = "complete" +timestamp = "2026-06-23 01:03:06" +actor = "daedalus-cli" +reason = "完成 HF Course 1/2 concept roadmap,并同步 shared concept/progress/syllabus maps。" + +[[transitions]] +stage = "04-lesson-lab" +action = "enter" +timestamp = "2026-06-23 01:03:06" +actor = "daedalus-cli" +reason = "进入 course-learning lesson lab,执行 Trainer 拆解 guide 并沉淀用户观察证据。" diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/task-card.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/task-card.md new file mode 100644 index 0000000..11ef233 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/task-card.md @@ -0,0 +1,43 @@ +# 专题学习任务卡 + +- Project:`hugging-face-llm-course` +- Topic:`lora-feedback-loop` +- Topic 标题:LoRA 微调与数据反馈闭环 +- 创建时间:`2026-06-14 14:42:45` +- 学习材料类型:课程 + 官方文档 + 开源代码示例 +- 当前目标:以 Hugging Face / PEFT / TRL 等材料为入口,围绕“闲鱼二手买家 Agent 的 suggestion next action 生成”完成一个 LoRA fine-tuning feedback loop lab,掌握从数据构造、LoRA 训练、评测、错误分析、数据反哺到再训练对比的最小闭环。 +- 现实问题:用户当前做 AI Agent 开发,但主要停留在应用层;短期需要补齐 AI 技术栈以应对岗位风险,长期希望用系统底层能力驾驭 AI,而不是疲惫追热点。 +- 预期输出物:production-shaped mini LoRA fine-tuning lab、训练闭环 runbook、初训 vs 反哺后再训练的实验对比报告、闲鱼二手买家 Agent suggestion next action 任务定义与样例集、fine-tuning 适用边界和业务迁移笔记。 +- 验收标准:能加载小型开源模型或教学模型;能构造初始训练集和独立测试集;能完成一次 LoRA 训练;能运行固定 eval;能基于错误样本构造反馈数据;能完成第二轮训练并比较差异;能记录改善、失败和未改善案例;能说明 non-goals 和迁移边界;工程形态必须满足 script-first / config-first / artifact-first,不能依赖 notebook cell 顺序、手工复制文件、绝对路径或隐式环境状态。 +- 约束条件:active WIP 只承认本 topic;DDIA 只作为 ambient reading,不占 active topic;优先低成本可跑通的小模型;先做 LoRA/fine-tuning,后训练和推理机制暂不提前展开;允许分阶段,但阶段只服务同一个闭环,不拆成多条长期战线;第一阶段可以是小规模实验,但从一开始就按可迁移、可复现、可审计的生产工程标准组织。 +- 暂不学习:从零训练大模型、大规模分布式训练、完整 RLHF pipeline、追最新模型榜单、生产级训练平台、DDIA 独立 active topic。 +- 用户确认:已确认主线为 Hugging Face / AI Stack 学习并完成 LoRA feedback loop lab;DDIA 作为公司碎片时间的低摩擦旁路阅读。 + +## Topic 学习目标 + +- 希望获得的能力:从 AI Agent 应用层进入模型训练闭环层,理解 fine-tuning 的数据、训练、评测、反馈和版本化边界,并能判断什么时候该 fine-tune、什么时候该用 prompt/RAG/tooling。 +- 继承的 shared context:暂无;本 topic 将沉淀 Hugging Face LLM Course project 的第一批 shared context。 +- 需要补充的源码入口:Hugging Face LLM Course、Transformers、Datasets、PEFT、TRL 官方文档和最小 LoRA 示例;具体材料在 `02-syllabus-mapper` 阶段确认。 +- 运行/调试要求:在用户可控的非公司环境中跑通训练;优先选择本地或低成本云环境能承受的小模型和小数据集;所有训练命令、环境约束和失败案例写入 runbook;所有入口都应是脚本/配置驱动,所有关键中间产物都要落盘并可追溯。 +- Mini demo 方向:闲鱼二手买家 Agent 在每次回复后生成 2-4 个高质量 suggestion next actions;闭环为 `initial dataset -> LoRA fine-tuning -> test / online-like dataset -> eval -> error analysis -> feedback data construction -> retrain -> compare`。 + +## Demo 业务任务 + +- 场景:toC 二手购物买家 Agent,帮助用户在闲鱼式二手交易平台进行商品搜索、比价、询问卖家、判断风险和推进购买决策。 +- 模型输出:每次 Agent 回复后面的 suggestion next actions,例如“继续追问成色细节”“让卖家补充实拍图”“比较同款历史价格”“询问是否支持当面验货”。 +- 第一阶段目标:用小数据和轻量模型快速证明完整 fine-tuning feedback loop。 +- 第二阶段目标:在不拉长战线的前提下,把数据、评测和任务定义迁移到更贴近真实岗位作品的程度。 +- 学习节奏:实践和理论交替推进;每个可运行实验都要回到 LoRA、数据、评测和反馈链路的机制理解。 +- 工程原则:从第一阶段开始就采用 script-first / config-first / artifact-first,而不是 notebook-first;实验小,但目录结构、配置边界、产物命名、评测报告和迁移路径要按生产工程标准设计。 + +## Fine-tuning Suitability 用户确认 + +- 结论确认:认可生产上应先做 prompt / context / tooling baseline,fine-tuning 由于成本、耗时、工程难度和上手速度问题,应作为最后选择,而不是默认主方案。 +- 第一轮品类:手机。 +- 核心质量标准:suggestion next action 应推动购买任务往前进,帮助用户多想一步,尤其是发现用户不知道但应该知道的问题;同时满足安全性、语义完整性和相关性等基础标准。 + +## 角色边界 + +- daedalus 应该做:拆解学习路径、生成行动指南、协助排障、验收阶段产物。 +- 用户必须亲自做:确认目标、运行关键命令、观察结果、回答核心问题、形成自己的笔记。 +- daedalus 可以协助但不能代替:源码拉取、环境启动、问题回答、架构总结、demo 设计。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/todo.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/todo.md new file mode 100644 index 0000000..99bd963 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/todo.md @@ -0,0 +1,155 @@ +# Todo Path Board + +Todo 是动态路径看板。学习证据变化、阶段完成、学习路径需要收窄或扩展时,都要同步更新它和 [`outcome-map.md`](outcome-map.md)。 + +> Agent 负责拆解、指导、排障和验收;用户负责关键实践、观察和手写笔记。不要把 Agent 自动完成的事项伪装成用户已经掌握。 + +## North Star + +- Project:`hugging-face-llm-course` +- Topic:`lora-feedback-loop` - LoRA 微调与数据反馈闭环 +- 最终产物:闲鱼二手买家 Agent suggestion next action production-shaped mini LoRA lab、训练闭环 runbook、实验对比报告、业务迁移笔记。 +- 最小 lesson lab:HF Course 1/2 基础机制已完成;当前用 Chapter 3 微调训练链路连接 `suggestion dataset -> train -> eval -> error analysis -> feedback data -> retrain -> compare`。 +- 业务迁移目标:让用户从 AI Agent 应用层进入模型训练闭环层,提升下一份 AI 开发岗位竞争力。 + +## Current Path + +当前 active topic 已迁移为 course-learning:`01-need-aligner`、`02-syllabus-mapper`、`03-concept-roadmap` 已完成,当前处于 `04-lesson-lab`。Chapter 1/5 文本主线任务观察已完成到 Summarization;剩余 Translation / ASR / Image classification 按用户决定跳过或延后,不再阻塞 Chapter 1/6。 + +## Now + +- 当前问题:用户已读完 Chapter 3、Chapter 4 和 Chapter 5/1,当前进入 5/2,需要掌握非 Hub 数据的本地/远程加载契约。 +- 为什么现在做它:LoRA/SFT 业务数据通常来自本地文件、对象存储或远程服务,而不是现成 Hub dataset;需要先把 source、format、split 与 schema 显式化。 +- 完成后解锁:能用统一的 `load_dataset()` 入口加载本地/远程 CSV、text、JSON/pandas 数据,并验证 DatasetDict 的 split、columns、rows 与嵌套结构。 + +## Current Cursor + +Current Cursor 是恢复定位器,不是完成证明。恢复或判断阶段状态时,必须用当前代码、测试、运行输出或用户已验证观察重新校准。 + +- Course frontier:已进入 `demo/hugging-face-course-learning`;用户完成 `transformer-work/casual_language_model.ipynb` 的 `max_steps=200` Causal LM quick training run。 +- Latest lesson evidence:用户已完成 `transformer-work/summarization.ipynb`,跑通 BillSum `ca_test` 数据加载、T5 summarization preprocessing、ROUGE 评估修复、Seq2SeqTrainer 训练和 `checkpoint-248` 推理;训练输出 `epoch=4.0`、`train_loss≈3.02`,推理样例已生成摘要。 +- Already wired:project/topic 已通过 daedalus lifecycle 初始化;task-card / outcome-map 已填入确认过的主线目标。 +- Current open decision:用户确认 Chapter 5/2 之前的内容均已读完;当前正式进入 Chapter 5/2 `What if my dataset isn't on the Hub?`。 +- Current QA boundary:QA lab 已完成基础观察;若后续追求可靠指标,需要补完整合法 span postprocess 与 SQuAD EM/F1,而不是复现 task guide 的单次示例输出。 +- Do not suggest:不要恢复旧仓库学习阶段命名;不要把 DDIA 升级为第二个 active topic。 + +## Reminder Queue + +这些事项是后续必须提醒用户亲自做的观察,不要因为 Agent 已解释过就标成完成。 + +- [x] Chapter 1/6 架构映射:用户已能用“理解 encoder、生成 decoder;extract 是 encoder,generate new token 是 decoder;强依赖完整 input 的生成可用 decoder 续写或 encoder + decoder 先理解后生成”归纳三类架构。 +- [x] Chapter 1/6 attention mechanisms:完成 Agent-guided walkthrough;LSH / local / axial positional encodings guide 已留作回看。 +- [x] Chapter 1/8 与 Chapter 2:用户确认已学完并正式进入 Chapter 3;未深挖项转为按需复习,不阻塞课程游标。 +- [x] Chapter 3/1 Introduction:全章路线已预读,并生成独立 Chapter 3 guide。 +- [x] Chapter 3/2、3/3 阅读进度:用户已读完并推进到 3/4。 +- [ ] Chapter 3/2、3/3 实验回看:区分 preprocessing/training batch,保留 Trainer 的 loss、metric 与 checkpoint 证据;不阻塞当前阅读游标。 +- [x] Chapter 3/4 full loop 机制:已拆解 dataset/DataLoader、shuffle、`**batch`、device、forward/loss、backward、optimizer、scheduler 与 zero-grad 顺序。 +- [ ] Chapter 3/4 运行证据:完整训练结果、evaluation 与 Accelerate 观察按需补齐,不阻塞当前游标。 +- [x] Chapter 3 剩余阅读:用户确认 3/5-3/7 已读完;曲线诊断实验保留待验收。 +- [x] Chapter 4 阅读:用户确认整章已读完;Hub 上传/model card 实践保留待验收。 +- [x] Chapter 5/1 Introduction:用户确认已读完。 +- [ ] Chapter 5/2:Agent 已验证课程 GitHub URL、最终 raw URL与本地压缩文件均可加载;待用户观察并复述 file loader、source、split mapping、自动解压与 JSON `field` 的职责。 + +## Critical Checkpoint + +Critical Checkpoint 只记录会影响后续判断的关键取舍,不写成聊天日志。 + +- Source constraint:Hugging Face/PEFT 示例通常默认数据、任务和评测已清楚;本 topic 必须把闲鱼买家 Agent 的输入、输出、偏好标准和评测边界显式化。 +- Faithful imitation:必须忠实保留训练闭环的因果链,而不是只跑一次 fine-tune。 +- Engineering standard:第一阶段可以低成本、小数据、小模型,但不能用 demo 标准放过工程结构;训练、评测、反馈构造和报告生成必须脚本化、配置化、产物化。 +- Dataset difficulty:数据集不能只覆盖典型清晰 case;必须加入 ambiguous、boundary、negative/unsafe 样本,让模型学会补证据、暂停推进和处理风险冲突。 +- Product shape:suggestion next action 是用户点击后直接发给买家 Agent 的自然指令,不是菜单标签;target 为最多 3 条 `{s, r}`,其中 `s` 是用户口吻指令,`r` 是短原因。 +- Simplified / improved / discarded:简化模型规模和数据规模;暂时丢弃生产级平台和完整 RLHF。 +- Transfer risk:迁移到真实业务前必须重新验证数据质量、评测一致性、成本、隐私和线上回归风险。 + +## Gaps Blocking Next Stage + +- [x] 学习目标:LoRA / fine-tuning feedback loop 能力补齐。 +- [x] 最小 demo:闲鱼二手买家 Agent suggestion next action 训练反馈闭环 lab。 +- [x] 业务迁移目标:AI Agent 工程师模型层和训练层判断力。 +- [x] 数据集任务:贴近 AI Agent 的 suggestion next action 生成。 +- [x] Fine-tuning suitability:已形成 Agent 调研版判断,结论是先做 prompt/context baseline,再用 LoRA 判断稳定策略和格式偏好是否改善。 +- [x] 第一轮品类:手机。 +- [x] 核心质量标准:推动购买任务往前、多帮用户想一步,同时满足安全性、语义完整性和相关性。 +- [x] 材料入口:确认课程/文档/示例组合。 +- [x] 实验优先级:第一阶段先证明完整 pipeline。 +- [x] 训练环境约束:可接受 Colab / 云 GPU,但需要成本可控。 +- [x] 数据策略:Agent 生成候选样本,用户审核修改。 +- [x] 企业平台候选:阿里云百炼可作为第二阶段迁移目标。 +- [x] 工程标准:从第一阶段开始采用 script-first / config-first / artifact-first,不用 demo 标准放过自己。 +- [x] Schema 草案:买家上下文、suggestion 输出结构、购买阶段枚举、rubric 和工程目录已形成草案。 +- [x] 实验边界:默认模型、训练环境和第一批样例数据规模已确认。 +- [x] 下一阶段入口:生成 `06-practice-transfer` 问题路线图、数据生成标准和 demo 设计前置问题。 +- [x] 第一批样本:生成 20 条候选数据,供用户校准审核口径。 +- [x] 难度覆盖:确认数据集需要覆盖 typical / ambiguous / boundary / negative-unsafe case。 +- [x] 输出形态修正:确认 target 为 `{ "suggestions": [{ "s": "建议", "r": "原因" }] }`,最多 3 条。 +- [x] v0.2 样本:按短句数组重生成 20 条候选样本。 +- [x] v0.3 样本:按 `{s,r}` schema 生成候选样本。 +- [x] v0.4 样本:按“用户口吻发给买家 Agent 的指令”重生成候选样本。 +- [x] 运行证据:用户已跑通 Transformers `pipeline` smoke test。 +- [x] 数据观察:用户已跑通 ELI5 `inspect_dataset.py`,确认原始数据结构。 +- [x] Tokenizer 观察:用户已观察 `input_ids` / `attention_mask` / decode,并发现长序列 warning。 +- [x] LM block 观察:用户已观察 `group_texts` 后 `input_ids` / `labels` 均为 128,且 `labels == input_ids`。 +- [x] Collator / Trainer warning 观察:用户已观察 special token config 自动对齐 warning 与 MPS `pin_memory` warning,并确认它们不是训练失败。 +- [x] Causal LM quick training run:用户已完成 `max_steps=200` 的 DistilGPT2 fine-tuning,得到 `training_loss≈3.98`、`perplexity≈47.81`,并上传到 Hugging Face Hub。 +- [x] Trainer 拆解 guide:已迁入 `guides/04-lesson-lab/03-trainer-train-under-the-hood.md`。 +- [x] Sequence classification lab:用户已完成 `transformer-work/sequence_classification.ipynb`,但需要做 derivation review。 +- [x] Sequence classification pipeline reload:用户报告已跑通保存/加载或内存模型推理链路。 +- [x] course-learning 迁移:project/topic state 已切换为 `course-learning` / `course-learning-topic`,并补齐 course shared maps 与新阶段目录。 +- [x] Concept roadmap:已生成 `guides/03-concept-roadmap/README.md`,把 HF Course 1/2 的关键问题收束为课程概念路线。 +- [x] Lesson lab:已生成 `guides/04-lesson-lab/README.md`,并迁移 Trainer batch / forward / loss / backward 观察入口。 +- [x] Sequence classification derivation review:已补 raw/tokenized/collated/forward/logits 观察,并理解 loss/logits/argmax/id2label 链路。 +- [x] Sequence classification closed-book rewrite:盖住教程,按任务契约重写最小 flow,并验收通过。 +- [x] Chapter 1/5 task lab sweep:完成文本主线 5/8;剩余 Translation / ASR / Image classification 已按用户决定跳过/延后,不再阻塞 Chapter 1/6。 +- [x] Lab 1/8 Text generation / Causal LM:已完成 DistilGPT2 quick training run、perplexity、Hub 上传。 +- [x] Lab 2/8 Text classification:已完成 DistilBERT sequence classification、pipeline load、forward/logits probe、闭卷复现。 +- [x] Lab 3/8 Token classification:已观察 label alignment、token-level logits 任务契约、模型保存和 `pipeline("ner")` postprocess;用户已完成复述。 +- [x] Lab 4/8 Question answering:已观察 `offset_mapping`、`start_positions/end_positions`、`start_logits/end_logits`、空 span 后处理问题和官网示例输出不可作为 golden output 的边界。 +- [x] Lab 5/8 Summarization:已观察 encoder-decoder generation、T5 task prefix、seq2seq labels、ROUGE、`compute_metrics` decode 边界和 `model.generate` 推理链路。 +- [x] Lab 6/8 Translation:按用户决定跳过/延后;如后续补做,只作为 summarization 的 seq2seq 对照。 +- [x] Lab 7/8 Automatic speech recognition:按用户决定跳过/延后;不作为当前 Transformers language foundation 阻塞项。 +- [x] Lab 8/8 Image classification:按用户决定跳过/延后;不作为当前 Transformers language foundation 阻塞项。 +- [x] Chapter 1/6 Transformer Architectures:三类架构映射和 attention mechanisms 已完成到可推进,后续弱点可回看 guide。 +- [x] Chapter 1/7 Ungraded quiz:用户已越过并进入 Chapter 1/8。 +- [x] Chapter 1/8 Deep dive into Text Generation Inference with LLMs:用户确认 Chapter 1 已完成。 +- [x] HF Course 1/2:用户于 2026-07-13 确认完成 Transformer Models 与 Using Transformers。 +- [x] Chapter 3/1 Introduction:已生成全章机制与实验路线 guide。 +- [x] Chapter 3/2 Processing the data:用户确认已读完;观察证据待回看验收。 +- [x] Chapter 3/3 Trainer API:用户确认已读完;训练与评估产物待回看验收。 +- [x] Chapter 3/4 Full training loop:用户确认完成机制学习;运行与 Accelerate 证据按需补齐。 +- [x] Chapter 3/5-3/7:用户确认已读完;learning curves 实验待回看。 +- [x] Chapter 4 Sharing models and tokenizers:用户确认已读完;Hub 实践待回看。 +- [x] Chapter 5/1 Introduction:用户确认已读完。 +- [ ] Chapter 5/2 What if my dataset isn't on the Hub?:加载本地与远程文件并检查 DatasetDict 契约。 +- [ ] Causal LM recipe:解释并观察 dataset -> tokenizer -> blocks -> labels -> Trainer。 +- [ ] Pipeline 机制:解释 task -> model/tokenizer/config -> inference -> postprocess。 +- [ ] Baseline 固化:固定 model name,不依赖默认 pipeline model。 +- [ ] 用户审核:用户至少审核 5-10 条样本,校准 keep/revise/weak/reject 口径。 + +## Stage Exit Criteria + +- [x] 可以解释本任务为什么值得进入 active learning。 +- [x] 可以说清最终产物和最小 demo。 +- [x] 可以用验收标准判断是否进入 `02-syllabus-mapper`。 +- [x] 用户 review 本次初始化内容,并确认 fine-tuning suitability、第一轮品类和质量标准。 + +## Done + +- [x] 01-need-aligner:完成 topic 目标、业务任务、fine-tuning suitability、第一轮品类和核心质量标准确认。 +- [x] 02-syllabus-mapper:完成材料入口、schema、工程形态、默认模型、训练环境和数据规模收敛。 +- [x] 06-practice-transfer input:数据集与问题路线产物已迁入 course-learning 阶段目录。 +- [x] 第一批候选样本:生成 20 条手机品类 suggestion next action 候选样本。 +- [x] 第一批候选样本 v0.2:根据产品形态反馈,重生成短句数组版本。 +- [x] 第一批候选样本 v0.3:根据 `{s,r}` target schema 生成当前有效版本。 +- [x] 第一批候选样本 v0.4:根据“suggestion 是发给买家 Agent 的用户指令”修正当前有效版本。 +- [x] Transformers pipeline smoke test:用户已跑通最小分类 pipeline。 +- [x] 学习路径调整:先完成 HF Course 1/2,建立 Transformers 基础,再进入 LoRA。 +- [x] Chapter 1/5 scope cut:完成 5 个文本主线任务实验后,按用户决定跳过/延后 Translation / ASR / Image classification,进入 Chapter 1/6。 +- [x] Chapter 1/6 architecture families summary:完成 encoder-only、decoder-only、encoder-decoder 的任务形态归纳。 +- [x] Chapter 1/6 attention mechanisms walkthrough:完成 full attention / LSH attention / local attention / axial positional encodings 的慢动作解释。 + +## Canceled + +- Chapter 1/5 Translation lab:2026-07-07 用户决定当前不做;后续如需要补 seq2seq 对照再恢复。 +- Chapter 1/5 Automatic speech recognition lab:2026-07-07 用户决定当前不做;非文本多模态实验不阻塞本 topic。 +- Chapter 1/5 Image classification lab:2026-07-07 用户决定当前不做;非文本多模态实验不阻塞本 topic。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/validation-log.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/validation-log.md new file mode 100644 index 0000000..13577b9 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/.daedalus/validation-log.md @@ -0,0 +1,81 @@ +# Daedalus 验证日志 + +记录本学习任务对 daedalus 教学引导能力的反向验证结果。 + +## 使用规则 + +- 不要把普通学习笔记写到这里;学习笔记写入 [`notes/`](../notes)。 +- 这里只记录 daedalus 本身的引导效果、缺口和改进建议。 +- 每完成一个阶段,至少复盘一次“Agent 是否代替用户做太多”。 + +## 2026-06-14 14:42:45 + +- 阶段:`01-need-aligner` +- 有效引导: +- 用户亲自完成的实践: +- Agent 代替用户过多的地方: +- Prompt/template/CLI/docs 改进建议: +- 是否足以进入下一阶段: + +## 2026-06-14 初始化补充 + +- 阶段:`01-need-aligner` +- 有效引导:根据 topic discovery 对话初始化 `hugging-face-llm-course` / `lora-feedback-loop`,并将已确认的目标、边界和验收标准写入 task card、outcome map、todo 和 goal guide。 +- 用户亲自完成的实践:用户确认主线为 Hugging Face / AI Stack 学习并完成 LoRA feedback loop lab;DDIA 作为 ambient reading。 +- Agent 代替用户过多的地方:尚未进入 notes 或 demo;本次只整理用户已确认的目标和待 review 的初始化产物。 +- Prompt/template/CLI/docs 改进建议:课程/官方文档型项目应使用 course-learning 项目类型,避免混用仓库学习阶段语义。 +- 是否足以进入下一阶段:等待用户 review task card 后再进入材料选择。 + +## 2026-06-14 业务任务收敛 + +- 阶段:`01-need-aligner` +- 有效引导:用户将 demo 任务收敛为“闲鱼二手买家 Agent 的 suggestion next action 生成”,并确认学习策略为实践 + 理论闭环。 +- 用户亲自完成的实践:用户确认最终目标贴近真实岗位,但允许先快速形成闭环,再迁移到真实业务需求程度。 +- Agent 代替用户过多的地方:未写用户 notes,当前只同步 task-card / outcome-map / todo / guide。 +- Prompt/template/CLI/docs 改进建议:后续 syllabus mapper 需要支持课程 + 官方文档 + 示例代码的混合材料,不应强行只按仓库选择。 +- 是否足以进入下一阶段:仍需确认 suggestion next action schema、质量标准、模型/训练环境和材料读取策略。 + +## 2026-06-14 01-need-aligner 用户确认 + +- 阶段:`01-need-aligner` +- 有效引导:用户确认 fine-tuning 是最后选择,生产上应先做 prompt/context/tooling baseline;第一轮品类选择手机;核心质量标准是推动购买任务往前、多帮用户想一步,并满足安全性、语义完整性和相关性。 +- 用户亲自完成的实践:完成 fine-tuning suitability gate 的关键判断。 +- Agent 代替用户过多的地方:无;Agent 只同步学习地图。 +- Prompt/template/CLI/docs 改进建议:课程型项目应使用 `02-syllabus-mapper` 来承接 source/material 选择。 +- 是否足以进入下一阶段:是,可以进入材料选择。 + +## 2026-06-14 02-syllabus-mapper 背景选择 + +- 阶段:`02-syllabus-mapper` +- 有效引导:用户确认第一阶段先证明完整 pipeline,可接受 Colab/云 GPU,数据由 Agent 生成候选样本后用户审核。 +- 用户亲自完成的实践:完成训练环境和数据生成方式的方向选择。 +- Agent 代替用户过多的地方:无;Agent 只补充基础知识 primer 和成本判断。 +- Prompt/template/CLI/docs 改进建议:后续 guide 应继续降低小白选型门槛,先解释选项含义再要求用户选择。 +- 是否足以进入下一阶段:还需确认具体材料清单和第一轮 schema/rubric。 + +## 2026-06-15 suggestion 产品形态修正 + +- 阶段:`06-practice-transfer` +- 有效引导:用户 review 第一批候选样本后指出 v0.1 的 suggestion 太长,无法直接点击发送给 Agent;Agent 将默认模型输出 schema 修正为最多 3 条可点击短句数组,并生成 v0.2 样本。 +- 用户亲自完成的实践:完成关键产品形态判断:suggestion next action 不是解释型建议,而是用户可点击的下一步指令。 +- Agent 代替用户过多的地方:v0.1 样本生成时过度偏解释型 coach 文案,没有先贴近产品交互形态。 +- Prompt/template/CLI/docs 改进建议:后续样本生成 prompt 应显式区分“模型输出字段”和“标注/eval 字段”,要求 suggestion 是短句、可点击、最多 3 条且互补;标注侧必须保留逐条 reason,模型输出 reason 则作为小规模 ablation。 +- 是否足以进入下一阶段:等待用户审核 v0.2 的 5-10 条样本后再扩展到 120 条。 + +## 2026-06-15 target schema 收敛 + +- 阶段:`06-practice-transfer` +- 有效引导:用户进一步确认 target 应直接采用 `{ "suggestions": [{ "s": "建议", "r": "原因" }] }`,而不是 action-only 或 action/reason 长字段。 +- 用户亲自完成的实践:完成产品输出 schema 的关键收敛:`s` 保持可点击短建议,`r` 保持短原因,最多 3 条且互补。 +- Agent 代替用户过多的地方:Agent 曾将 reason 拆成标注侧或 ablation,导致 schema 分叉;用户纠正后已收敛到单一 target。 +- Prompt/template/CLI/docs 改进建议:后续样本生成 prompt 应直接以 `{s,r}` 为目标 schema,避免再次发散到 action-only / action-with-reason。 +- 是否足以进入下一阶段:等待用户审核 v0.3 的 5-10 条样本后再扩展到 120 条。 + +## 2026-06-15 suggestion 说话对象修正 + +- 阶段:`06-practice-transfer` +- 有效引导:用户指出 v0.3 的 `s` 仍像菜单标签,而不是用户点击后直接发送给买家 Agent 的自然指令;Agent 将 `s` 的定义修正为“用户口吻发给买家 Agent 的下一步请求”,并生成 v0.4 样本。 +- 用户亲自完成的实践:完成交互语义判断:suggestion next action 不是给用户阅读的建议,而是降低用户发号施令摩擦的可点击用户消息。 +- Agent 代替用户过多的地方:Agent 之前只关注“短”和“可点击”,忽略点击后的消息接收者和说话口吻。 +- Prompt/template/CLI/docs 改进建议:后续样本生成必须先写清 `s` 的 recipient 是买家 Agent,speaker 是用户;禁止生成内部动作标签式文案。 +- 是否足以进入下一阶段:等待用户审核 v0.4 的 5-10 条样本后再扩展到 120 条。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.gitignore b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.gitignore new file mode 100644 index 0000000..357629e --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.gitignore @@ -0,0 +1,6 @@ +my_awesome_**model/ +**/.ipynb_checkpoints/ +ch3/test-trainer +**.json.gz +**-test.json +**-train.json diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.python-version b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.python-version new file mode 100644 index 0000000..2c07333 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.python-version @@ -0,0 +1 @@ +3.11 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.vscode/settings.json b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.vscode/settings.json new file mode 100644 index 0000000..fb7a36b --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python", + "python.analysis.extraPaths": [ + "${workspaceFolder}" + ], + "python.terminal.activateEnvironment": true, + "cursorpyright.analysis.extraPaths": [ + "${workspaceFolder}" + ] +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/README.md new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch3/01_processing_the_data.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch3/01_processing_the_data.ipynb new file mode 100644 index 0000000..80f2493 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch3/01_processing_the_data.ipynb @@ -0,0 +1,1830 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "07cf0313", + "metadata": {}, + "source": [ + "### Load Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9203f620", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "91f303a9-77cc-413c-935c-b901923881ed", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DatasetDict({\n", + " train: Dataset({\n", + " features: ['sentence1', 'sentence2', 'label', 'idx'],\n", + " num_rows: 3668\n", + " })\n", + " validation: Dataset({\n", + " features: ['sentence1', 'sentence2', 'label', 'idx'],\n", + " num_rows: 408\n", + " })\n", + " test: Dataset({\n", + " features: ['sentence1', 'sentence2', 'label', 'idx'],\n", + " num_rows: 1725\n", + " })\n", + "})" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "raw_datasets = load_dataset(\"nyu-mll/glue\", \"mrpc\")\n", + "raw_datasets" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "ab248fe0-6c90-4eae-ad81-64a5b1afe9e7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'sentence1': 'Amrozi accused his brother , whom he called \" the witness \" , of deliberately distorting his evidence .',\n", + " 'sentence2': 'Referring to him as only \" the witness \" , Amrozi accused his brother of deliberately distorting his evidence .',\n", + " 'label': 1,\n", + " 'idx': 0}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "raw_train_dataset = raw_datasets[\"train\"]\n", + "raw_train_dataset[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9f5192f8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'sentence1': Value('string'),\n", + " 'sentence2': Value('string'),\n", + " 'label': ClassLabel(names=['not_equivalent', 'equivalent']),\n", + " 'idx': Value('int32')}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "raw_train_dataset.features" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "33f98069", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'sentence1': 'Gyorgy Heizler , head of the local disaster unit , said the coach was carrying 38 passengers .',\n", + " 'sentence2': 'The head of the local disaster unit , Gyorgy Heizler , said the coach driver had failed to heed red stop lights .',\n", + " 'label': 0,\n", + " 'idx': 15}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "raw_train_dataset[14]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6f1186b4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'sentence1': 'He was arrested Friday night at an Alpharetta seafood restaurant while dining with his wife , singer Whitney Houston .',\n", + " 'sentence2': 'He was arrested again Friday night at an Alpharetta restaurant where he was having dinner with his wife .',\n", + " 'label': 1,\n", + " 'idx': 796}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "raw_validate_dataset = raw_datasets[\"validation\"]\n", + "raw_validate_dataset[86]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "14c11387", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Amrozi accused his brother , whom he called \" the witness \" , of deliberately distorting his evidence .',\n", + " \"Yucaipa owned Dominick 's before selling the chain to Safeway in 1998 for $ 2.5 billion .\",\n", + " 'They had published an advertisement on the Internet on June 10 , offering the cargo for sale , he added .',\n", + " 'Around 0335 GMT , Tab shares were up 19 cents , or 4.4 % , at A $ 4.56 , having earlier set a record high of A $ 4.57 .',\n", + " 'The stock rose $ 2.11 , or about 11 percent , to close Friday at $ 21.51 on the New York Stock Exchange .',\n", + " 'Revenue in the first quarter of the year dropped 15 percent from the same period a year earlier .',\n", + " 'The Nasdaq had a weekly gain of 17.27 , or 1.2 percent , closing at 1,520.15 on Friday .',\n", + " 'The DVD-CCA then appealed to the state Supreme Court .',\n", + " 'That compared with $ 35.18 million , or 24 cents per share , in the year-ago period .',\n", + " 'Shares of Genentech , a much larger company with several products on the market , rose more than 2 percent .',\n", + " 'Legislation making it harder for consumers to erase their debts in bankruptcy court won overwhelming House approval in March .',\n", + " 'The Nasdaq composite index increased 10.73 , or 0.7 percent , to 1,514.77 .',\n", + " 'But he added group performance would improve in the second half of the year and beyond .',\n", + " \"He told The Sun newspaper that Mr. Hussein 's daughters had British schools and hospitals in mind when they decided to ask for asylum .\",\n", + " 'Gyorgy Heizler , head of the local disaster unit , said the coach was carrying 38 passengers .',\n", + " 'Rudder was most recently senior vice president for the Developer & Platform Evangelism Business .',\n", + " 'As well as the dolphin scheme , the chaos has allowed foreign companies to engage in damaging logging and fishing operations without proper monitoring or export controls .',\n", + " 'Sheena Young of Child , the national infertility support network , hoped the guidelines would lead to a more \" fair and equitable \" service for infertility sufferers .',\n", + " '\" I think you \\'ll see a lot of job growth in the next two years , \" he said , adding the growth could replace jobs lost .',\n", + " \"The new Finder puts a user 's folders , hard drive , network servers , iDisk and removable media in one location , providing one-click access .\",\n", + " 'But tropical storm warnings and watches were posted today for Haiti , western portions of the Dominican Republic , the southeastern Bahamas and the Turk and Caicos islands .',\n", + " 'A federal magistrate in Fort Lauderdale ordered him held without bail .',\n", + " 'A BMI of 25 or above is considered overweight ; 30 or above is considered obese .',\n", + " 'Six months ago , the IMF and Argentina struck a bare-minimum $ 6.8-billion debt rollover deal that expires in August .',\n", + " 'Inhibited children tend to be timid with new people , objects , and situations , while uninhibited children spontaneously approach them .',\n", + " 'I wanted to bring the most beautiful people into the most beautiful building , he said Sunday inside the Grand Central concourse .',\n", + " \"The broad Standard & Poor 's 500 < .SPX > fell 10.75 points , or 1.02 percent , to 1,039.32 .\",\n", + " \"Duque will return to Earth Oct. 27 with the station 's current crew , U.S. astronaut Ed Lu and Russian cosmonaut Yuri Malenchenko .\",\n", + " \"Singapore is already the United States ' 12th-largest trading partner , with two-way trade totaling more than $ 34 billion .\",\n", + " 'The largest gains were seen in prices , new orders , inventories and exports .',\n", + " 'Trading in Loral was halted yesterday ; the shares closed on Monday at $ 3.01 .',\n", + " 'Earnings per share from recurring operations will be 13 cents to 14 cents .',\n", + " \"He plans to have dinner with troops at Kosovo 's U.S. military headquarters , Camp Bondsteel .\",\n", + " 'Retailers J.C. Penney Co . Inc . ( JCP ) and Walgreen Co . ( WAG ) kick things off on Monday .',\n", + " 'Prosecutors filed a motion informing Lee they intend to seek the death penalty .',\n", + " \"Last year the court upheld Cleveland 's school voucher program , ruling 5-4 that vouchers are constitutional if they provide parents a choice of religious and secular schools .\",\n", + " 'He beat testicular cancer that had spread to his lungs and brain .',\n", + " 'Sorkin , who faces charges of conspiracy to obstruct justice and lying to a grand jury , was to have been tried separately .',\n", + " 'Graves reported from Albuquerque , Villafranca from Austin and Ratcliffe from Laredo .',\n", + " 'The US chip market is expected to decline 2.1 percent this year , then grow 15.7 percent in 2004 .',\n", + " 'The group will be headed by State Department official John S. Wolf , who has served in Australia , Vietnam , Greece and Pakistan .',\n", + " \"The commission must work out the plan 's details , but the average residential customer paying $ 840 a year would get a savings of about $ 30 annually .\",\n", + " 'The company has said it plans to restate its earnings for 2000 through 2002 .',\n", + " 'Results from No. 2 U.S. soft drink maker PepsiCo Inc . PEP.N were likely to be in the spotlight .',\n", + " '\" The result is an overall package that will provide significant economic growth for our employees over the next four years . \"',\n", + " 'The songs are on offer for 99 cents each , or $ 9.99 for an album .',\n", + " 'However , the talk was downplayed by PBL which said it would focus only on smaller purchases that were immediately earnings and cash flow accretive .',\n", + " 'Comcast Class A shares were up 8 cents at $ 30.50 in morning trading on the Nasdaq Stock Market .',\n", + " 'The Washington Post said Airlite would shut down its first shift and parts of the second shift Monday to accommodate the president ’ s appearance .',\n", + " 'A former teammate , Carlton Dotson , has been charged with the murder .',\n", + " 'Several of the questions asked by the audience in the fast-paced forum were new to the candidates .',\n", + " 'Meanwhile , the global death toll approached 770 with more than 8,300 people sickened since the severe acute respiratory syndrome virus first appeared in southern China in November .',\n", + " \"The battles marked day four of a U.S. sweep to hunt down supporters of Saddam Hussein 's fallen regime .\",\n", + " 'The women then had follow-up examinations after five , 12 and 24 years .',\n", + " 'The Embraer jets are scheduled to be delivered by September 2006 .',\n", + " 'Contrary to what PeopleSoft management would have you believe , Oracle intends to fully support PeopleSoft customers and products for many years to come . \"',\n", + " \"Application Intelligence will be included as part of the company 's SmartDefense application , which is included with Firewall-1 .\",\n", + " 'American Masters : Arthur Miller , Elia Kazan and the Blacklist : None Without Sin ( Wed .',\n", + " 'The downtime , to take place in May and June , is expected to cut production by 60 million to 70 million board feet .',\n", + " 'On July 3 , Troy is expected to be sentenced to life in prison without parole .',\n", + " 'The University of Michigan released a new undergraduate admission process Thursday , dropping a point system the U.S. Supreme Court found unconstitutional in June .',\n", + " 'The processors were announced in San Jose at the Intel Developer Forum .',\n", + " 'The Justice Department filed suit Thursday against the state of Mississippi for failing to end what federal officials call \" disturbing \" abuse of juveniles and \" unconscionable \" conditions at two state-run facilities .',\n", + " \"It said the damage to the wing provided a pathway for hot gasses to penetrate the ship 's thermal armor during Columbia 's ill-fated reentry .\",\n", + " 'Also demonstrating box-office strength _ and getting seven Tony nominations _ was a potent revival of Eugene O \\'Neill \\'s family drama , \" Long Day \\'s Journey Into Night . \"',\n", + " 'But Secretary of State Colin Powell brushed off this possibility Wednesday .',\n", + " 'Thomas and Tauzin say , as do many doctors , that the Bush administration has the power to correct some of those flaws .',\n", + " 'Based on experience elsewhere , it could take up to two years before regular elections are held , he added .',\n", + " 'The first biotechnology treatment for asthma , the constriction of the airways that affects millions around the world , received approval from the US Food and Drug Administration yesterday .',\n", + " 'FBI agents arrested a former partner of Big Four accounting firm Ernst & Young ERNY.UL on criminal charges of obstructing federal investigations , U.S. officials said on Thursday .',\n", + " 'Kelly will begin meetings with Russian Deputy Foreign Minister Alexander Losyukov in Washington on Monday .',\n", + " 'The latest shooting linked to the spree was a November 11 shooting at Hamilton Central Elementary School in Obetz , about 3km from the freeway .',\n", + " 'The Dow Jones Industrial Average ended down 128 points , or 1.4 % , at 9073 , while the Nasdaq fell 34 points , or 2.1 % , to 1610 .',\n", + " \"PDC will also almost certainly fan the flames of speculation about Longhorn 's release .\",\n", + " 'Sales - a figure watched closely as a barometer of its health - rose 5 percent instead of falling as many industry experts had predicted .',\n", + " 'NEC is pitching its wireless gear and management software to a variety of industries , including health care and hospitality .',\n", + " \"Elena Slough , considered to be the nation 's oldest person and the third oldest person in the world , died early Sunday morning .\",\n", + " '\" We are declaring war on sexual harassment and sexual assault .',\n", + " 'The technology-laced Nasdaq Composite Index < .IXIC > added 1.92 points , or 0.12 percent , at 1,647.94 .',\n", + " 'The dollar was at 117.85 yen against the Japanese currency , up 0.1 percent .',\n", + " 'El Watan , an Algerian newspaper , reported that the kidnappers fiercely resisted the army assault this morning , firing Kalashnikov rifles .',\n", + " 'But Mitsubishi Tokyo Financial ( JP : 8306 : news , chart , profile ) declined 3,000 yen , or 0.65 percent , to 456,000 yen .',\n", + " '\" We \\'re just dealing with bragging rights here , who wins and who loses . \"',\n", + " 'Shares of Hartford rose $ 2.88 to $ 46.50 in New York Stock Exchange composite trading .',\n", + " 'This Palm OS smart phone is the last product the company will release before it becomes a part of palmOne .',\n", + " 'And they think the protein probably is involved in the spread of other forms of cancer .',\n", + " \"Tokyo Electric Power Co . , Asia 's largest power company , won approval to restart the first of 17 nuclear reactors it shut down after it admitted falsifying inspection reports .\",\n", + " 'Tuition at four-year private colleges averaged $ 19,710 this year , up 6 percent from 2002 .',\n", + " 'Security lights have also been installed and police have swept the grounds for booby traps .',\n", + " 'He said that with the U.S.-backed peace plan , or road map , “ in a coma , ” the attack could easily widen conflict through the region .',\n", + " 'A successful attack could be launched within any type of document that supports VBA , including Microsoft Word , Excel or PowerPoint .',\n", + " 'Officials at Brandeis said this was an \" extremely heartrending \" time for the campus .',\n", + " '\" If that ain \\'t a Democrat , I must be at the wrong meeting , \" he said .',\n", + " 'Fewer than a dozen FBI agents were dispatched to secure and analyze evidence .',\n", + " 'Those who only had surgery lived an average of 46 months .',\n", + " \"Tonight a spokesman for Russia 's foreign ministry said the ministry may issue a statement on Thursday clarifying Russia 's position on cooperation with Iran 's nuclear-energy efforts .\",\n", + " \"A day earlier , a committee appointed by reformist President Mohammad Khatami called for an independent judicial inquiry into Kazemi 's death .\",\n", + " 'The suite comes complete with a word processor , spreadsheet , presentation software and other components , while continuing its tradition of utilizing an XML-based file format .',\n", + " 'Downstream at Mount Vernon , the Skagit River was expected to crest at 36 feet -- 8 feet above flood stage -- tonight , Burke said .',\n", + " 'The blue-chip Dow Jones industrial average eased 44 points , or 0.47 percent , to 9,543 , after scoring five consecutive up sessions .',\n", + " 'The Nasdaq composite index inched up 1.28 , or 0.1 percent , to 1,766.60 , following a weekly win of 3.7 percent .',\n", + " 'Physicians who violate the ban would be subject to fines and up to two years in prison .',\n", + " \"Standard & Poor 's 500 stock index futures declined 4.40 points to 983.50 , while Nasdaq futures fell 6.5 points to 1,206.50 .\",\n", + " 'For example , Anhalt said , children typically treat a 20-ounce soda bottle as one serving , although it actually contains 2 ½ 8-ounce servings .',\n", + " 'The yield on the 3 percent note maturing in 2008 fell 22 basis points to 2.61 percent .',\n", + " \"Kline 's opinion dealt specifically with abortion providers , but it also would apply to doctors providing other services , such as prenatal care , to a pregnant child under 16 .\",\n", + " \"The United Nations ' International Atomic Energy Agency asked for information on the allegation after the dossier was published , but Britain did not provide any , U.N. officials said .\",\n", + " 'Sixteen days later , as superheated air from the shuttle \\'s reentry rushed into the damaged wing , \" there was no possibility for crew survival , \" the board said .',\n", + " '\" The free world and those who love freedom and peace must deal harshly with Hamas and the killers , \" he told reporters as he emerged from a church service .',\n", + " 'The pressure will intensify today , with national coverage of the final round planned by ESPN and words that are even more difficult .',\n", + " 'Powell fired back : \" He \\'s accusing the president of a ludicrous act , \" he said .',\n", + " 'The memo on protecting sales of Windows and other desktop software mentioned Linux , a still small but emerging software competitor that is not owned by any specific company .',\n", + " 'Also Tuesday , the United States also released more Iraqi prisoners of war , and officials announced that all would soon be let go .',\n", + " \"The resulting lists led toa CBS special , AFI 's 100 Years . . . 100 Heroes & Villains , hosted by Arnold Schwarzenegger .\",\n", + " 'Police warned residents on Friday not to travel alone and to avoid convenience stores and service stations stores at night .',\n", + " '\" We had nothing to do with @ Stake \\'s internal personnel decision , \" Microsoft spokesman Sean Sundwell said .',\n", + " 'Prosecutors said there is no question Waagner was behind the letters , which were sent at the same time of the anthrax attacks .',\n", + " \"His plan would cost more than $ 200 billion annually , which Gephardt would pay for by repealing Bush 's tax cuts .\",\n", + " \"There had been fears that the 35 year-old wouldn 't be able to conceive after suffering two ectopic pregnancies and cancer of the uterus .\",\n", + " 'On Friday , the Food and Drug Administration approved the use of silicone breast implants with conditions .',\n", + " 'Prosecutors said Paracha , if convicted , could face at least 17 years in prison .',\n", + " 'More than half of the songs were purchased as albums , Apple said .',\n", + " 'The ECB has cut interest rates six times over that period , from 4.75 percent in October 2000 to 2.5 percent .',\n", + " '\" This deal makes sense for both companies , \" Halla said in a prepared statement .',\n", + " 'Smugglers in a van chased down a pickup and SUV carrying other smugglers and illegal immigrants , said Pinal County Sheriff Roger Vanderpool .',\n", + " 'Tyco later said the loan had not been forgiven , and Swartz repaid it in full , with interest , according to his lawyer , Charles Stillman .',\n", + " 'While Bolton apparently fell and was immobilized , Selenski used the mattress to scale a 10-foot , razor-wire fence , Fischi said .',\n", + " 'The loonie , meanwhile , continued to slip in early trading Friday .',\n", + " 'Ms. Haque , meanwhile , was on her turquoise cellphone with the smiley faces organizing the prom .',\n", + " 'But he added that the New York-based actor and producer is \" a private person and doesn \\'t care to have his treatment out for public consumption . \"',\n", + " \"While a month 's supply of aspirin can cost less than $ 10 , the equivalent amount of ticlopidine can run significantly more than $ 100 .\",\n", + " 'She estimated it would take three months and would require cancellation of a sub-audit of the Department of Environmental Quality .',\n", + " 'Arison said Mann may have been one of the pioneers of the world music movement and he had a deep love of Brazilian music .',\n", + " 'The virus kills roughly 36,000 people in an average year , according to the U.S. Centers for Disease Control and Prevention .',\n", + " 'In two new schemes , people posing as IRS representatives target families of armed forces members and e-mail users .',\n", + " 'Wim Wenders directed a widely praised film of the same name , based on the sessions .',\n", + " \"Miss Novikova said while there is no standard weight for ballerinas , Miss Volochkova is ' bigger than others ' .\",\n", + " 'Both have aired videotapes of ex-president Saddam Hussein encouraging Iraqis to fight the U.S. occupation of Iraq .',\n", + " 'Under the program , U.S. officials work with foreign port authorities to identify , target and search high-risk cargo .',\n", + " 'And if we don \\'t do this , innocent people on the ground are going to die , too . \"',\n", + " '\" It \\'s probably not the easiest time to take over the shuttle program , \" he added , \" but I look forward to the challenge .',\n", + " \"As a result , Nelson now faces up to 10 years ' jail instead of life .\",\n", + " 'With all precincts reporting , Fletcher — a three-term congressman from Lexington — had an overwhelming 57 percent of the vote .',\n", + " '\" His progress is steady , he \\'s stable , he \\'s comfortable , \" Jack said Tuesday afternoon .',\n", + " \"Shadrin said Khodorkovsky 's aircraft was approached by officers identifying themselves as members of the FSB intelligence service , the domestic successor to the Soviet KGB .\",\n", + " 'She claimed all the babies were born full-term and died within hours , he said .',\n", + " 'French Foreign Minister Dominique de Villepin was scheduled to arrive Sharm el-Sheikh on Wednesday .',\n", + " 'They believe the same is true in humans , giving a 30 per cent higher risk of the disease .',\n", + " 'Total sales for the period declined 8.0 percent to $ 1.99 billion from a year earlier .',\n", + " \"U.S. law enforcement officials are sneering at Dar Heatherington 's version of the events which thrust her into the public spotlight .\",\n", + " \"The Saudi newspaper Okaz reported Monday that suspects who escaped Saturday 's raid fled in a car that broke down on the outskirts of Mecca Sunday afternoon .\",\n", + " 'Michael Mitchell , the chief public defender in Baton Rouge who is representing Lee , did not answer his phone Wednesday afternoon .',\n", + " 'In Canada , the booming dollar will be in focus again as it tries to stay above the 75 cent ( U.S. ) mark .',\n", + " 'They are tired of paying the high cost of CDs and DVDs and prefer more flexible forms of on-demand media delivery . \"',\n", + " '\" The bolt catcher is not as robust as it is supposed to be , \" the board \\'s chairman , retired Adm. Hal Gehman , said .',\n", + " 'The Interior Minister , Al Mustapha Sahel , said the investigation \" points to a group that has been arrested recently \" , an apparent reference to Djihad Salafist .',\n", + " 'The report was found Oct. 23 , tucked inside an old three-ring binder not related to the investigation .',\n", + " 'Among Fox viewers , 41 percent describe themselves as Republicans , 24 percent as Democrats and 30 percent as Independents .',\n", + " 'Muslim immigrants have used the networks - which rely on wire transfers , couriers and overnight mail - to send cash to their families overseas .',\n", + " 'Also arrested Friday was Claudia Carrizales de Villa , 34 , a Mexican citizen who lives in Harlingen .',\n", + " '\" We condemn the Governing Council headed by the United States , \" Muqtada al Sadr said in a sermon at a mosque .',\n", + " \"Mrs Boncyk 's husband , Wayne , had taken the family car to work , so his wife and children had to ride with neighbours .\",\n", + " 'Ukrainian President Leonid Kuchma today cut short a visit to Latin America as a bitter border wrangle between Ukraine and Russia deteriorates further .',\n", + " 'For the weekend , the top 12 movies grossed $ 157.1 million , up 52 percent from the same weekend a year earlier .',\n", + " 'Platinum prices soared to 23-year highs earlier this year after President Bush ( news - web sites ) proposed investing $ 1.2 billion for research on fuel cell-powered vehicles .',\n", + " '\" Nobody really knows what happened except me and that guard , \" Kaye said , \" and I can assure you that what he said happened didn \\'t happen . \"',\n", + " 'Shares of Corixa fell 12 cents to $ 6.88 as of 3 : 59 p.m.',\n", + " 'At the time federal investigators were looking into how CFSB allocated shares of initial public stock offerings .',\n", + " 'Qanbar said the council members would possibly elect a chairman later Sunday .',\n", + " 'October heating oil futures settled .85 cent lower at 69.89 cents a gallon .',\n", + " 'By 2007 , antivirus solutions will carry a worldwide price tag of $ 4.4 billion--double that of five years earlier .',\n", + " 'The Securities and Exchange Commission brought a related civil case on Thursday .',\n", + " 'ConAgra stock closed Monday on the New York Stock Exchange at $ 21.63 a share , down 11 cents .',\n", + " 'One of the features is the ability to delete data on a handheld device or lock down the device should a user lose it .',\n", + " '\" I just got carried away and started making stuff , \" Byrne said .',\n", + " 'Georgia cannot afford to not get funding , \" said Dr. Melinda Rowe , Chatham County \\'s health director .',\n", + " 'Handspring shareholders will get 0.09 shares of the parent company , Palm , Inc . , for each share of Handspring common stock they currently own .',\n", + " 'Shares of Microsoft rose 50 cents Friday to close at $ 28.34 a share on the Nasdaq Stock Market .',\n", + " 'Based on having at least one of the symptoms , most students had been hung over between three and 11 times in the previous year .',\n", + " 'Tibco has used the Rendezvous name since 1994 for several of its technology products , according to the Palo Alto , California company .',\n", + " \"A few thousand troops , most from the division 's 3rd Brigade Combat Team based at Fort Benning in Columbus , began returning last week , with flights continuing through Friday .\",\n", + " 'In a televised interview on Wednesday , ECB President Wim Duisenberg said it was too soon to discuss further interest rate cuts in the 12-nation euro zone .',\n", + " 'Teenagers at high schools where condoms were available were no more likely to have sex than other teens , a study published Wednesday finds .',\n", + " 'Later in the day , however , a former Intel executive turned the tables in a speech where he blasted wireless as being too complicated and too difficult to install .',\n", + " 'He is charged in three bombings in Atlanta _ including a blast at the 1996 Olympics _ along with the bombing in Alabama .',\n", + " 'They were found in a stolen van , said James Flateau , a spokesman for the state Department of Correctional Services .',\n", + " 'On Thursday , a Post article argued that a 50 basis point cut from the Fed was more likely .',\n", + " 'Sun was the lone major vendor to see its shipments decline , falling 2.9 percent to 59,692 units .',\n", + " 'He is one of two Democrats on the five-member FCC , and he is a strong advocate of harsher penalties against radio and television stations that violate indecency laws .',\n", + " 'The city index outperformed the Dow Jones industrial average , which fell 0.9 percent for the week .',\n", + " 'State media said there were at least 770 dead and over 5,600 injured .',\n", + " 'The March downturn was the only break in what has been broad growth in services for the past 15 months .',\n", + " 'In the first three months of 2003 alone , weekly earnings adjusted for inflation fell 1.5 % -- the biggest drop in more than a decade , \" Lieberman said .',\n", + " 'The statistical analysis was published Tuesday in Circulation , a journal of the American Heart Association ( news - web sites ) .',\n", + " \"President George W. Bush and top administration officials cited the threat from Iraq 's banned weapons programs as the main justification for going to war .\",\n", + " \"The epidemic began in November in the mainland 's Guangdong province , but the People 's Republic of China refused to report truthfully about its spread for four months .\",\n", + " '\" This man gambled with our lives and we , the passengers , lost , \" said Andres Rivera Jr . , who was also on the bus .',\n", + " \"IG Farben 's 500 properties were valued at around 38-million euros ( about R300-million ) and the firm had debts totalling about 28-million .\",\n", + " \"The company 's operating loss rose 59 percent to $ 73 million , from $ 46 million a year earlier .\",\n", + " 'The agent , Bassem Youssef , filed the lawsuit on Friday in Federal District Court for the District of Columbia .',\n", + " '\" Smallpox is not the only threat to the public \\'s health , and vaccination is not the only tool for smallpox preparedness , \" Strom said .',\n", + " 'Mr Abbas said : \" Every day without an agreement is an opportunity that was lost .',\n", + " 'We remain hopeful that the city will agree to work with us and engage in good-faith discussions on this issue . \"',\n", + " 'Later in the day , a standoff developed between French soldiers and a Hema battlewagon that attempted to pass the UN compound .',\n", + " '\" By its actions , the Bush administration threatens to give a bad name to a just war , \" Lieberman said .',\n", + " 'Gillette shares rose $ 1.45 , or 4.5 percent , to $ 33.95 in afternoon New York Stock Exchange trading .',\n", + " 'Even without call center requests , companies using the handset option must have 95 percent of their customers using the technology by the end of 2005 .',\n", + " 'About 120 potential jurors were being asked to complete a lengthy questionnaire .',\n", + " \"Microsoft fell 5 percent before the open to $ 27.45 from Thursday 's close of $ 28.91 .\",\n", + " 'U.S. Airways ordered 85 Bombardier jets with 50 seats and 75 seats and 85 Embraer jets with 70 seats , it said in a release .',\n", + " \"The acquisition has been approved by both companies ' board of directors and is expected to close in the third quarter this year .\",\n", + " 'The 39-year-old Luster initially gave police a false name , but later revealed his true identity .',\n", + " 'In a statement distributed Friday , 28 Chechen non-governmental organizations said they would boycott the vote .',\n", + " \"If the companies won 't , their drugs could be prescribed to Medicaid patients only with the state 's say-so .\",\n", + " '\" It \\'s a huge black eye , \" said New York Times Company chairman Arthur Sulzberger .',\n", + " 'Singer Brandy and her husband , Robert Smith , have called it quits .',\n", + " 'She told Murray , \" We ... we have ... the fresh air is going down fast ! \"',\n", + " '\" The president \\'s campaign in 2000 set a standard for disclosure in political fund raising , and the campaign will again in 2004 , \" Bush campaign spokesman Dan Ronayne said .',\n", + " 'On April 11 , Mayor Bart Peterson said it was \" inconceivable \" that the airline would resume operations at the base that had employed 1,500 workers , including 1,100 mechanics .',\n", + " 'Dotson was arrested Monday in his native Maryland and charged with murder .',\n", + " 'Another was in serious condition at Northwest Medical Center in Springdale .',\n", + " 'With the exception of dancing , physical activity did not decrease the risk .',\n", + " 'The 1 5 / 8 percent note maturing in April 2005 gained 1 / 16 to 100 13 / 32 , lowering its yield 1 basis points to 1.41 percent .',\n", + " \"Mr. Bush had sought to store his papers in his father 's presidential library , where they would have stayed secret for a half-century .\",\n", + " 'Williams was White , and four of his victims were Black ; the fifth was White .',\n", + " 'A hearing on the matter was held Thursday morning in Fulton County Superior Court , marking one of the early steps in deciding the case of Matthew Whitley .',\n", + " 'Arts helped coach the youth on an eighth-grade football team at Lombardi Middle School in Green Bay .',\n", + " 'Atlantic Coast will continue its operations as a Delta Connections carrier .',\n", + " '\" It \\'s amazing to be part of an industry that rewards its young , \" said Hernandez , who is a recent graduate of Parsons School of Design .',\n", + " 'The flamboyant entrepreneur flagged the plan after a meeting in London with Australian Tourism Minister Joe Hockey .',\n", + " 'Ryan Harvey , an outfielder from Dunedin High School in Florida , was selected with the sixth pick by the Chicago Cubs .',\n", + " \"At 8 a.m. Friday , San Joaquin County Sheriff 's deputies found the body of Adan Avalos , 37 .\",\n", + " 'Technology stocks make up 42 percent of the Nasdaq , which fell 49.95 points , or 2.9 percent , to 1,698.02 .',\n", + " \"Thomas was joined in full by Rehnquist , and in parts by O 'Connor and Scalia .\",\n", + " 'The bulk of the funds , some $ 65 billion , will go for military operations in Iraq and Afghanistan .',\n", + " 'The new capabilities will provide IBM customers a way to create , publish , manage and archive Web-based content within a corporate intranet , extranet and Internet environment .',\n", + " 'When you crossed the line , you violated the constitutional right , \" said Charles Weisselberg , a UC Berkeley law professor .',\n", + " 'A $ 500 million natural-gas-fired power plant , for example , could replace up to $ 100 million in boilers yearly without adding new smog controls .',\n", + " 'On Sunday , the experts will perform the first simultaneous release of five whales from a single stranding incident in the United States .',\n", + " \"Shares of AstraZeneca AZN.N , Europe 's second biggest drugmaker , rose 3.71 percent to close at $ 43.05 on the New York Stock Exchange .\",\n", + " 'Gov. Rick Perry has said that while he opposes gambling expansion , he would be reluctant to veto continuation of the Lottery Commission .',\n", + " '\" This blackout was largely preventable , \" Energy Secretary Spencer Abraham said .',\n", + " 'Typhoon Maemi later moved out over the Sea of Japan , where it weakened considerably , the meteorology department said .',\n", + " 'Along with chip.m.aker Intel , the companies include Sony Corp. , Microsoft Corp. , Hewlett-Packard Co . , International Business Machines Corp. , Gateway Inc . , Nokia Corp. and others .',\n", + " \"Officials developed plans to burn about 2,000 acres of dense forest near the park 's southwest border by dropping incendiary devices , hoping to burn off fuel from the wildfire 's path .\",\n", + " 'At last nights protest , demonstratorschanted \" tapping our phones , reading our mail , the LEIU should go to jail . \"',\n", + " '\" It still remains to be seen whether the revenue recovery will be short-lived or long-lived , \" Mr. Sprayregen said .',\n", + " 'Some 14,000 customers were without power in the area , Oklahoma Gas and Electric said .',\n", + " '\" The SIA forecast reflects the new realities of the semiconductor industry of an 8-10 percent \" growth rate , George Scalise , the trade association \\'s president , said in a statement .',\n", + " 'Eighty-six seriously wounded U.N. workers were airlifted out for medical care .',\n", + " 'Burns believed that confessing a crime he did not commit was the only way out , Richardson said .',\n", + " \"Phone calls to Spitzer 's office , Citigroup and Goldman Sachs were not immediately returned .\",\n", + " 'Instead , the high court said that drug makers did not adequately show why the plan should be prevented from taking effect .',\n", + " 'Medical experts said the condition was mildly worrying but easily-manageable .',\n", + " \"Joan B. Kroc , the billionaire widow of McDonald 's Corp. founder Ray Kroc , died Sunday after a brief bout with brain cancer .\",\n", + " 'The company has expanded into providing other services for buyers , including payment services .',\n", + " 'A Lamar mother arrested Saturday in Colorado Springs is accused of drowning her two children in a bathtub before slitting her wrists .',\n", + " 'She was surrounded by about 50 women who regret having abortions .',\n", + " 'Revelations of the expenditures shocked some employees at Delta , an airline that is cutting jobs as it struggles with increased security costs and fewer passengers .',\n", + " 'The search was concentrated in northeast Pennsylvania , but State Police Trooper Tom Kelly acknowledged that Selenski could have traveled hundreds of miles by now .',\n", + " 'The calculation shows that the number of deaths from pneumonia and influenza have exceeded the usual number for recent weeks .',\n", + " \"Researchers at Sweden 's Karolinska Institute reanalyzed data from more than 2,000 infants who had been treated with radiation for a benign birthmark condition between 1930 and 1959 .\",\n", + " \"Outside the court , Sriyanto , who faces up to 25 years ' jail , denied he had done anything wrong .\",\n", + " 'The state , which previously conducted two triple executions and two double executions , says the policy is designed to reduce stress on its prison staff .',\n", + " \"The little girl , the daughter of a teller , was taken to the bank after a doctor 's appointment .\",\n", + " 'At least 23 U.S. troops have been killed by hostile fire since Bush declared major combat in Iraq to be over on May 1 .',\n", + " '\" Apple is working with Oxford Semiconductor and affected drive manufacturers to resolve this issue , which resides in the Oxford 922 chipset , \" the company said in a statement .',\n", + " 'Even later in life , he was still cashing in : lately , he earned money by calling fans on the telephone with the service www.HollywoodIsCalling.com.',\n", + " \"The transaction will expand Callebaut 's sales revenues from its consumer products business to 45 percent from 23 percent .\",\n", + " 'A sign outside the Peachtree Restaurant reads : \" Pray for Eric Rudolph . \"',\n", + " 'In a statement , the company said both bids would allow Vivendi to \" maintain a substantial minority interest in a U.S. media corporation with excellent growth potential . \"',\n", + " 'In Vienna , the IAEA said ElBaradei would accept the invitation , although no date had yet been set .',\n", + " 'Strikingly , the poll saw little difference between women and men in their feelings towards Mrs Clinton .',\n", + " 'A 25 percent increase would raise undergraduate tuition to about $ 5,247 annually , including miscellaneous , campus-based fees .',\n", + " 'From July 1 , 2002 , to June 30 , 2003 , the organization said it spent $ 114.3 million but took in only $ 39.5 million .',\n", + " 'Hundreds of reporters and photographers swamped the courthouse grounds before the hearing , which was carried live on national cable networks .',\n", + " 'The 20 master computers are located in the United States , Canada and Korea , Mr. Kuo said .',\n", + " 'They said : “ We believe that the time has come for legislation to make public places smoke-free .',\n", + " 'Milwaukee prosecutors charged a church minister with physical abuse in the death of an 8-year-old autistic boy who died during a healing service .',\n", + " 'Niels told an interviewer in 1999 he thought the Old Man would outlive him by many years .',\n", + " 'The new effort , Taxpayers Against the Recall , will be formally launched Wednesday outside a Sacramento fire station .',\n", + " 'Furthermore , chest tightness after exercise and prevalence of asthma were both linked to the amount of time spent at the pool .',\n", + " 'British police arrested 21 people early Tuesday in connection with the suspected ritual murder of an African boy whose torso was found in the Thames River .',\n", + " 'Winners will be announced in a June 8 ceremony broadcast on CBS from Radio City Music Hall .',\n", + " 'A lawsuit has been filed in an attempt to block the removal of the Ten Commandments monument from the building .',\n", + " '\" I \\'m quite positive about it except I wouldn \\'t want it to cut across anything now occurring and she accepts that , \" he said .',\n", + " '\" I started crying and yelling at him , \\' What do you mean , what are you saying , why did you lie to me ? \\' \"',\n", + " 'Marisa Baldeo stated , however , the authority \\'s official uniform policy says \" they are not supposed to wear anything on their heads but a NYC transit depot logo cap .',\n", + " \"The Dow Jones industrial average .DJI jumped 2.09 percent , while the Standard & Poor 's 500 Index .SPX leapt 2.23 percent .\",\n", + " 'Between 50 and 100 persons die annually from these allergies , and thousands more suffer severe reactions such as constricted breathing and dramatic swelling .',\n", + " 'Only Intel Corp. has a lower dividend yield .',\n", + " 'Sean Harrigan of Calpers , the California fund , said : \" Today we are trying to pull the pig from the trough .',\n", + " 'That truck was spotted in the Campbells Creek area , fitting that description , Morris said .',\n", + " 'Initial autopsy reports show they died of dehydration , hyperthermia and suffocation .',\n", + " 'Most of those on board were Lebanese but some were from Benin , Guinea and Sierra Leone .',\n", + " 'A pamphlet will be issued to the public through major hardware chains , local libraries and on the EPA Web site : www.epa.gov.',\n", + " \"The captain , Michael J. Gansas , was notified yesterday by the city 's Department of Transportation that an agency hearing officer recommended that he be fired .\",\n", + " 'Since then , police divers have searched nearby parts of Long Island Sound looking for the remaining three .',\n", + " 'Mr. Yandarbiyev resides in Doha , Qatar , and Russian authorities have unsuccessfully tried to have him extradited for nearly two years .',\n", + " 'In December , he anticipated growth of 5.3 percent to nearly $ 154 billion .',\n", + " 'Clearly Roman creams of any type do not normally survive in the archaeological record .',\n", + " '\" I \\'m taking his office , and we \\'re gonna keep on building , \\' \\' he vowed . \"',\n", + " \"SARS has killed 296 people on China 's mainland and infected more than 5,200 .\",\n", + " '\" Third . . . we are not allowed to kill women , except those who join the war [ against Islam ] . \"',\n", + " 'The company will launch 800 hot spots , or \" Wi-Fi Zones , \" later this summer , and plans to have more than 2,100 by the end of the year .',\n", + " 'The virus spreads when unsuspecting computer users open file attachments in emails that contain familiar headings like \" Thank You ! \" and \" Re : Details \" .',\n", + " 'From California , Bush flies to Japan on Thursday , followed by visits to the Philippines , Thailand , Singapore , Indonesia and Australia before returning home on Oct. 24 .',\n", + " \"The transfers would reduce P & G 's worldwide work force to slightly less than 100,000 .\",\n", + " 'Two Iraqis and two Saudis grabbed shortly after the blast gave information leading to the arrest of the others , said the official .',\n", + " \"In court papers filed Tuesday , Lee asked for an injunction against Viacom 's use of the name , saying he had never given his consent for it to be used .\",\n", + " 'The church was already grieving the death of Chief Warrant Officer 3 Kyran Kennedy , a congregation member who was killed in another helicopter crash in Iraq on Nov. 7 .',\n", + " 'Glover said the killer used a \" peculiar slipknot \" on the ligature he used to strangle some of the victims .',\n", + " 'They weighed an average 220 pounds ( 100 kg ) and needed to lose between 30 and 80 pounds .',\n", + " '\" If it ain \\'t broke , don \\'t fix it , \" said Senate Minority Leader Tom Daschle , D-S.D. \" I don \\'t see much broken . \"',\n", + " 'Media Editor Jon Friedman contributed to this story .',\n", + " 'Her putt for birdie was three feet short but she saved par without difficulty .',\n", + " 'The test was conducted by the University of California at Los Angeles and Riverside and was paid $ 450,000 from the ARB .',\n", + " 'Against the Swiss franc CHF = , the dollar was at 1.3172 francs , down 0.60 percent .',\n", + " \"Instead , Weida decided that Meester 's case on charges of rape , sodomy , indecent assault and providing alcohol to minors should proceed to a court-martial .\",\n", + " 'The name for the robot , due to be launched at 2 : 05 p.m. , was selected from among 10,000 names submitted by U.S. school children .',\n", + " 'Doris Brasher , who owns a grocery store near the police station , said many in the close-knit town knew the men who were killed .',\n", + " '\" Craxi begged me to intervene because he believed the operation damaged the state , \" Mr Berlusconi said .',\n", + " 'Mills affected by Thursdays announcement are sawmills in Grande Cache , Alta . , Carrot River , Sask . , Chapleau , Ont . , and Aberdeen , Wash .',\n", + " 'It was the biggest protest since hundreds of thousands marched in outrage over the massacre of democracy activists occupying Tiananmen Square in Beijing in 1989 .',\n", + " 'The company is highlighting the addition of .NET and J2EE support to its Enterprise Application Environment ( EAE ) with the new mainframe .',\n", + " 'Gov. Linda Lingle and members of her staff were at the Navy base and watched the launch .',\n", + " \"That information was first reported in today 's editions of the New York Times .\",\n", + " 'The euro tagged another record high against the dollar on Tuesday as demand for higher-yielding euro-based assets overshadowed solid U.S. economic data .',\n", + " 'The technology-laced Nasdaq Composite Index .IXIC rose 17.33 points , or 1.07 percent , to 1,640.13 .',\n", + " '\" It \\'s going to happen , \" said Jim Santangelo , president of the Teamsters Joint Council 42 in El Monte .',\n", + " 'The seizure occurred at 4am on March 18 , just hours before the first American air assault .',\n", + " 'Bloomberg said on Wednesday that all 16 crew members survived and would be tested for drugs and alcohol .',\n", + " 'The moment of reckoning has arrived for this West African country founded by freed American slaves in the 19th century .',\n", + " 'However , commercial use of the 2.6.0 kernel is still months off for most customers .',\n", + " 'The Hulk weekend take surpasses the previous June record of $ 54.9-million for Austin Powers : The Spy Who Shagged Me .',\n", + " 'The time was about 4 a.m. on March 18 , just hours before the first pinpoint missiles rained down on the capital .',\n", + " 'Its attackers had to fire their rockets from hundreds of yards away , with a makeshift launcher hidden in a portable electric generator .',\n", + " 'In early U.S. trade , the euro was down 1.06 percent at $ 1.1607 < EUR = > .',\n", + " '\" Deviant cannibalistic tendencies \" were the primary motivation for the murders , and Brown \\'s body was mutilated , authorities said .',\n", + " 'Also Tuesday , a soldier drowned in an aqueduct in northern Iraq .',\n", + " \"Hunt 's attorneys filed a motion late Monday in Forsyth Superior Court to have Hunt 's murder conviction thrown out .\",\n", + " 'Other data showed that buyers snapped up new and existing homes at a brisk pace in April , spurred by low mortgage rates .',\n", + " \"But Close wondered whether the package would be worth the cost of licensing the third-party software , along with Salesforce.com 's rental price .\",\n", + " 'Still others cite the difficulty of assembling a group of subjects whose depression is of comparable severity , increasing the chances that the effectiveness of the medication will be diffused .',\n", + " '\" I have always tried to be honest with you and open about my life , \" Mr. Limbaugh told his audience , which numbers 22 million listeners a week .',\n", + " 'That \\'s the highest third-quarter growth rate we \\'ve seen in over 25 years , \" said Andy Bryant , Intel \\'s chief financial officer .',\n", + " 'Seattle Deputy Police Chief Clark Kimerer said the exercise here went well , with some aspects , including the communication system linking various agencies , working better than expected .',\n", + " 'Telecommunications gear maker Lucent Technologies is being investigated by two federal agencies for possible violations of U.S. bribery laws in its operations in Saudi Arabia .',\n", + " '\" This sale is part of our efforts to simplify our Foodservice Products business and improve its cost structure , \" Multifoods chairman and chief executive officer Gary Costley said .',\n", + " 'Three-year-old Jaryd Atadero vanished on Oct. 2 , 1999 while on a hiking trip with a church group .',\n", + " 'A DOD team is working to determine how hackers gained access to the system and what needs to be done to fix the breach .',\n", + " \"Parrado 's aunt believes her nephew , who has served a prison sentence in Cuba , will be persecuted if he is repatriated .\",\n", + " 'The Securities and Exchange Commission yesterday said companies trading on the biggest U.S. markets must win shareholder approval before granting stock options and other stock-based compensation plans to corporate executives .',\n", + " 'Microsoft is preparing to alter its Internet Explorer browser , following a patent verdict that went against the company , Microsoft said Friday .',\n", + " '\" It \\'s routine for IBM to challenge its internal IT teams to rigorously test new platforms and technologies inside IBM , \" she said .',\n", + " 'Nearly 6,000 MTA drivers and train operators joined the mechanics in the picket line .',\n", + " 'The tech-laced Nasdaq Composite Index .IXIC shed 23.45 points , or 1.44 percent , to end at 1,603.97 , based on the latest data .',\n", + " 'The Infineon case began in August 2000 , when Rambus accused Infineon of patent infringement and Infineon counter-sued Rambus for breach of contract and fraud .',\n", + " 'Low-income earners would pay a 5 percent tax on both types of investment income .',\n", + " 'The technology-laced Nasdaq Composite Index .IXIC was off 5.53 points , or 0.33 percent , at 1,662.91 .',\n", + " '\" The Princess \\' marriage was not set up by force , \" said Vasile Ionescu , of the Roma Centre for public policies .',\n", + " 'Lord of the Rings director Peter Jackson and companion Fran Walsh arrive at the 74th Annual Academy Awards .',\n", + " 'Hynix CEO E.J. Woo condemned the decision calling the ruling an \" outrageous act aimed at a hidden agenda . \"',\n", + " \"The U.S. Food and Drug Administration rejected ImClone 's original application in December 2001 , saying the trial had been sloppily conducted .\",\n", + " 'Gen. Sattler heads a Combined Joint Task Force based on ship in the Gulf of Aden and the Indian Ocean .',\n", + " 'Massachusetts Attorney General Tom Reilly said he was satisfied with the reimbursement .',\n", + " 'AutoAdvice is available as a one-year subscription at $ 400 per CPU , scaling from one to 50,000 CPUs .',\n", + " 'CAPPS II will not use bank records , records indicating creditworthiness or medical records . \"',\n", + " 'Moore had no immediate comment Tuesday .',\n", + " '\" Fewer than 10 \" FBI offices have conducted investigations involving visits to mosques , the Justice Department said .',\n", + " \"Moore of Alabama says he will appeal his case to the nation 's highest court .\",\n", + " '\" We \\'re confident that the new leadership will take responsibility for the past actions of the Archdiocese of Boston , \" Lincoln said .',\n", + " 'Her body was found several weeks later in the Green River .',\n", + " 'One question was whether France , which infuriated Washington by leading the charge against U.N. authorization for the war , would vote \" Yes \" or abstain .',\n", + " 'Money from Iraqi oil sales will go into that fund which will be controlled by the United States and Britain and used to rebuild the country .',\n", + " 'Car volume fell 8 per cent , while light truck sales -- which include vans , pickups and SUVs -- rose 2.7 per cent .',\n", + " 'He said he had made it \" very plain \" to the US that Australia did not support the death penalty .',\n", + " 'Special police detained Khodorkovsky early on Saturday in the Siberian city of Novosibirsk , where his plane had made a refuelling stop .',\n", + " \"Robert Stewart , a spokesman for Park Place , the parent company of Caesars Palace , said he was surprised by the court 's decision .\",\n", + " \"He has also served on the president 's Homeland Security Advisory Council .\",\n", + " \"Altria shares fell 2.2 percent or 96 cents to $ 42.72 and were the Dow 's biggest percentage loser .\",\n", + " '\" And if there is a leak out of my administration , \" he added , \" I want to know who it is .',\n", + " 'Mascia-Frye works in the state development office .',\n", + " 'Representatives for Puretunes could not immediately be reached for comment Wednesday .',\n", + " \"A key to that effort was the company 's introduction of a 10-year , 100,000-mile warranty , meant to reassure buyers that they could trust the Korean company .\",\n", + " 'ECUSA is the U.S. branch of the 70 million-member Anglican Communion .',\n", + " 'The word Advent , from Latin , means \" the coming . \"',\n", + " 'The companies said \" it was not our intention to target or offend any group or persons or to incite hatred or violence . \"',\n", + " 'Another brother , Ali Imron , was sentenced to life in prison after cooperating with investigators and showing remorse .',\n", + " 'Most times , wholesalers sold the questionable drugs to one of three huge national distributors that supply virtually all drugs in the country .',\n", + " 'He said it was not possible to rule out that the chemical was a cancer risk to humans .',\n", + " \"Attorney John Barnett , who represents Morse , showed photos of a scratch on Morse 's neck and the officer 's bloody ear .\",\n", + " 'The letter stated that a premature stillborn baby was placed on the bed in a blanket .',\n", + " 'Galveston County District Attorney Kurt Sistrunk said his office received the recordings this week .',\n", + " '\" This is a wide-ranging and continuing investigation which is likely to result in numerous other charges , \" Mr Spitzer said .',\n", + " 'In early afternoon trading , the Dow Jones industrial average was up 7.58 , or 0.1 percent , at 9,381.79 .',\n", + " 'Kyi , a U.N. envoy says , as Japan adds to growing international pressure by saying it will halt its hefty economic aid unless Suu Kyi is freed .',\n", + " 'The decision could also prove useful in the \" war on terrorism \" .',\n", + " \"Currently , the state 's congressional delegation is made up of 17 Democrats and 15 Republicans .\",\n", + " 'In the first stage of the attack , the Lovsan worm began fouling computers around the world .',\n", + " 'U.S. Agriculture Secretary Ann Veneman , who announced Tuesdays ban , also said Washington would send a technical team to Canada to help .',\n", + " 'Only 29 families of frogs are known and most were identified and described in the mid-1800s and the last in 1926 .',\n", + " 'The survey that found it covered only a narrow slice of the sky .',\n", + " 'However , the standards body warns that changes to Internet Explorer may affect a \" large number \" of existing Web sites .',\n", + " 'Both Blair and Bush have faced accusations that they manipulated intelligence about weapons of mass destruction to make the case for military action .',\n", + " '\" I think 70 percent of the work is already done , \" Tauzin told reporters in the Capitol .',\n", + " '\" We must defend those whom we dislike or even despise , \" said Neal R. Sonnett , a Miami lawyer who led an association panel on enemy combatants .',\n", + " 'Likewise , the 30-year bond US30YT = RR slid 23 / 32 for a yield of 4.34 percent , up from 4.30 percent .',\n", + " 'China has threatened to execute or jail for life anyone who breaks their quarantine and intentionally spreads the killer SARS virus .',\n", + " 'Earlier , they had defied a police order and cried \" Allahu Akbar \" ( God is Greatest ) as Bashir walked to his seat in the tightly guarded courtroom .',\n", + " 'The announcement comes one day after Microsoft ’ s global security chief , Scott Charney , reiterated Microsoft ’ s promises to simplify the way it distributes patches to users .',\n", + " 'The fighting around Bunia began after neighboring Uganda completed the withdrawal of its more than 6,000 soldiers from the town .',\n", + " \"An officer in the People 's Liberation Army selected from a pool of 14 , Lt-Col Yang is the son of a teacher and an official at an agricultural firm .\",\n", + " 'The Yankees , however , had big trouble handling Victor Zambrano ( 4-4 ) for the second straight time .',\n", + " 'A former candidate for governor of New York , he styled himself an advocate of better corporate governance .',\n", + " 'Yeager said the suspect in the Target attack showed tendencies of being a prior offender .',\n", + " 'Tenet has been under scrutiny since November , when former Chief Executive Jeffrey Barbakow said the company used aggressive pricing to trigger higher payments for the sickest Medicare patients .',\n", + " 'His decision means that the case against Gary Lee Sampson , including the capital charges against him , will be tried in September .',\n", + " \"Meanwhile , rival contender , General Electric 's NBC , submitted a letter of interest , a source familiar with the matter said .\",\n", + " \"Freeman 's civil hearing may be , on the surface , about a driver 's license .\",\n", + " '\" This deal makes sense for both companies , \" Brian Halla , National \\'s chief executive , said in a statement .',\n", + " 'The priest , the Rev. John F. Johnston , 64 , of 35th Avenue in Jackson Heights , was charged with aggravated harassment and criminal possession of a weapon .',\n", + " '\" Qualcomm has enjoyed many years of selling ... against little or no competition , \" Hubach said in the statement .',\n", + " 'That state will not emerge until the interim government decides on a process for writing a new constitution and for holding the first democratic elections .',\n", + " 'Sendmail said the system can even be set up to permit business-only usage .',\n", + " 'The Senate Armed Services Committee will hold a separate hearing Thursday .',\n", + " 'The telephone survey had a margin of error of 2 percentage points .',\n", + " \"Tonight , 21-year-old Morgan and as many as 1,200 fellow students at Wheaton College will gather in the gym for the first real dance in the school 's 143-year history .\",\n", + " \"The bodies of former Invercargill man Warren Campbell , 32 , and climbing companion Jonathan Smith , 47 , were recovered from below Mt D 'Archiac late yesterday morning .\",\n", + " 'In 2001 , President Bush named Kathy Gregg to the Student Loan Marketing Association board of directors .',\n", + " 'Beckham will receive a yearly salary of $ 10 million plus bonuses , earning him about $ 220,000 a game .',\n", + " 'Deaths in rollover crashes accounted for 82 percent of the number of traffic deaths in 2002 , the agency says .',\n", + " \"This week 's tour will take Bush to Senegal , South Africa , Botswana , Uganda and Nigeria , and is aimed at softening his warrior image at home and abroad .\",\n", + " 'Executive recruiters say that finding a seasoned chief merchandising officer will perhaps be the trickiest of all hires because there is such a dearth of good ones .',\n", + " '\" We have sent a message to the nation that this is a new Louisiana , \" she told a victory party in New Orleans .',\n", + " 'Five more human cases of West Nile virus , were reported by the Mesa County Health Department on Wednesday .',\n", + " 'It said the operation , which began on Monday , was part of \" the continued effort to eradicate Baath Party loyalists , paramilitary groups and other subversive elements . \"',\n", + " \"NBC probably will end the season as the second most popular network behind CBS , although it 's first among the key 18-to- 49-year-old demographic .\",\n", + " \"In Europe , France 's CAC-40 rose 0.6 percent , Britain 's FTSE 100 slipped 0.02 percent and Germany 's DAX index advanced 0.8 percent .\",\n", + " 'Advancing issues outnumbered decliners about 8 to 5 on the New York Stock Exchange .',\n", + " 'Some 660 prisoners from 42 countries , including Canada , are held , many captured during the war against in Afghanistan the al-Qaida network .',\n", + " '\" Beginning ; early stages ; not life-threatening ; treatable , \" he said at a news conference at the Capitol .',\n", + " 'Oracle \\'s offer \" undervalues the company and is not in the best interest of stockholders , \" PeopleSoft CEO Craig Conway said .',\n", + " 'Bryant surrendered to authorities on Friday and posted a $ 25,000 bond .',\n", + " 'Roberson was bitten on the back and scratched on the leg , according to her mother , Shamika Woumnm of Dorchester .',\n", + " 'He says information released about a tarot card left at one shooting scene may have prolonged the spree by cutting off fragile communications with the sniper .',\n", + " \"It was the 23-9 Yankees ' third straight defeat - their longest skid of the season .\",\n", + " 'Solomon 5.5 is available initially in the United States and Canada , for a starting price of about $ 12,700 .',\n", + " 'The report released Monday simply says , This report does not attempt to address the complexities of this issue .',\n", + " 'The SEC said on Thursday its staff was preparing to draw up rules to combat trading abuses .',\n", + " \"History will remember the University of Washington 's Dr. Belding Scribner as the man who has saved more than a million lives by making long-term kidney dialysis possible .\",\n", + " 'In the United States , 20.7 percent of all women smoke .',\n", + " 'But he will meet the French President , Jacques Chirac , privately .',\n", + " \"He is temporarily serving as Chechnya 's acting president while his boss , Akhmad Kadyrov , is on the campaign trail .\",\n", + " 'The technology-laced Nasdaq Composite Index dipped 0.08 of a point to 1,646 .',\n", + " 'The hearing came one day after the Pentagon for the first time singled out an officer , Dallager , for failing to address the scandal .',\n", + " \"Chechen officials working for the Moscow-backed government are a frequent target for rebels and tension is running high ahead of next Sunday 's presidential election in war-torn Chechnya .\",\n", + " 'Columbia was destroyed during re-entry Feb. 1 , killing its seven astronauts .',\n", + " \"Feral 's group was behind a successful tourism boycott about a decade ago that resulted in then-Gov . Walter J. Hickel imposing a moratorium on wolf control in 1992 .\",\n", + " 'She concludes that what her husband did was morally wrong but not a betrayal of the public .',\n", + " 'Sen. Bob Graham , Florida Democrat , raised $ 2 million after getting a late start .',\n", + " 'The exam contains four sections and tests a students knowledge of algebra and geometry as well as probability and statistics .',\n", + " 'Top U.S. executives are feeling increasingly sunny about business conditions and corporate profits , according to a survey released Monday .',\n", + " 'Massachusetts is one of 12 states that does not have the death penalty , having abolished capital punishment in 1984 .',\n", + " \"Taylor 's aides warn an abrupt departure could trigger more chaos , a view shared by many in Liberia and , in private , even by mediators .\",\n", + " 'Recall advocates say they have turned in 1.6 million signatures to counties .',\n", + " 'Mr. Gettelfinger said at that news conference that \" we are going to continue to hammer away at the negotiations process until we reach an agreement , \" with Ford and G.M.',\n", + " 'Country-music station KKCS has suspended two disc jockeys for playing songs by the Dixie Chicks in violation of a ban imposed after one group member criticized President George Bush .',\n", + " 'Their difference was over whether the court should pay attention to legal opinions of other world courts , such as the European Court of Human Rights .',\n", + " \"It 's also a strategic win for Overture , given that Knight Ridder had the option of signing on Google 's services .\",\n", + " 'BOSTON The Catholic archdiocese in Boston has offered $ 55 million to settle more than 500 clergy sex abuse lawsuits , according to a document obtained by The Associated Press .',\n", + " 'Lewis said that the third-quarter results were driven by deposit and loan growth , strong investment banking and trading results , and improved mortgage and card revenues .',\n", + " 'Swedish telecom equipment maker Ericsson ( nasdaq : QCOM - news - people ) scrambled up $ 2.19 , or almost 12 percent , to $ 20.59 .',\n", + " 'They were not supplied or given to us but unearthed by our reporter , David Blair , in the Foreign Ministry in Baghdad . \"',\n", + " 'Congress twice before passed similar restrictions , but President Bill Clinton vetoed them .',\n", + " 'Brian Brabazon said his son would get upset but then turn around and befriend his taunters .',\n", + " 'At his sentencing , Avants had tubes in his nose and a portable oxygen tank beside him .',\n", + " \"McKnight 's lawyers say there is no proof her cocaine use caused the child 's death .\",\n", + " 'The number of extremely obese American adults - those who are at least 100 pounds overweight - has quadrupled since the 1980s to about 4 million .',\n", + " \"The verbal flareup with Keating stemmed from Cardinal Mahony 's initial refusal to participate in that survey unless procedures were changed .\",\n", + " 'An animal rights group waited Friday to find out if it has succeeded in putting a stop to a state-sponsored program allowing hunters to shoot wolves from airplanes in Alaska .',\n", + " 'The CFTC has been investigating several energy companies , including at least three Colorado companies , for manipulating energy markets by reporting false natural-gas trading prices to industry publications .',\n", + " '\" In relation to the second paper , one part of that should have been sourced to a reference work .',\n", + " 'The court then stayed that injunction , pending an appeal by the Canadian company .',\n", + " \"He was eventually taken to London 's Hammersmith hospital , where doctors regulated Blair 's heart beat via electric shock .\",\n", + " 'Under the legislation , a physician who performs the procedure could face up to two years in prison .',\n", + " 'Dotson was taken to Chester River Hospital Center where he stayed overnight .',\n", + " 'When police removed the four from the home , they weighed 136 pounds combined .',\n", + " \"The second company , temporarily dubbed ``InternationalCo . ' ' , includes 19 international power and pipeline holdings .\",\n", + " 'He was convicted by a Ventura County Superior Court jury and sentenced in absentia to 124 years in prison .',\n", + " 'The draft of the report was forthright : \" Climate change has global consequences for human health and the environment . \"',\n", + " 'Civilian reservists also could be sent overseas for jobs such as the reconstruction of Afghanistan and Iraq .',\n", + " 'Westfield , which owns Galleria in Morley , also will continue discussions about the other co-owned centres such as Knox City in Melbourne , half-owned by Deutsche Bank .',\n", + " 'Last year Bloomberg boycotted the parade because he wanted to march with two actors from \" The Sopranos . \"',\n", + " 'Federal agents said Friday they are investigating the theft of 1,100 pounds of an explosive chemical from construction companies in Colorado and California in the past week .',\n", + " 'Like-for-like sales for the 17 weeks to 27 December were flat , while the gross margin fell by 2 percentage points , the company said .',\n", + " 'Investigators were searching his home in Muenster in the presence of his wife when news of his death came , prosecutor Wolfgang Schweer told The Associated Press .',\n", + " '\" What this allows us to do is have a crossover that is much more fuel efficient and aimed at young families . \"',\n", + " 'The teen was in surgery at Sacred Heart Medical Center , and the extent of his injuries was not available , Bragdon said .',\n", + " 'The goal will be to reduce congestion by upgrading road , building tollways , facilitating public transit or any other means to improve mobility .',\n", + " 'The conservancy helped in the purchase of the land from the Estate of Samuel Mills Damon for $ 22 million .',\n", + " 'Sir Paul already has three children fashion designer Stella , 31 , Mary , 33 , and James , 25 by his wife Linda , who died from breast cancer in 1998 .',\n", + " 'In recent years , Mr. Hampton continued to run into trouble , facing charges of fare-beating and credit-card theft .',\n", + " 'SCO has also alleged that the generic Linux kernel contains code which is from its Unix property .',\n", + " \"But Amrozi did not reveal and was not asked the company 's or his boss 's name .\",\n", + " 'General Jeffery announced he would give his substantial military pension accumulated over 40 years to charity during his stay at Government House .',\n", + " 'The rest said they belonged to another party or had no affiliation .',\n", + " 'Blair himself took the reins of the Labour Party after its leader John Smith died of a heart attack in 1994 .',\n", + " 'In 2002 , for example , the Dow dropped more than 1,500 points , or 15.6 percent , between May and October , due largely to terrorism fears driven by headlines .',\n", + " 'The House and Senate are expected to vote on the omnibus bill in the next two months .',\n", + " 'She said six were from Douglas County , the southwestern Oregon county that includes Roseburg , and two were from the Portland area .',\n", + " 'PeopleSoft gained 94 cents , or 5.5 percent , to $ 18.09 .',\n", + " 'A rival distributor , Mike Selwyn from United International Pictures , says that unlike The Matrix Revolutions , he cannot detect any advance scepticism , even within the business .',\n", + " '\" The law has several weaknesses which terrorists could exploit , undermining our defenses , \" Ashcroft said .',\n", + " '\" Canada is a small country , and it needs companies like this , deals like this . \"',\n", + " 'Revenue rose to $ 616.5 million from $ 610.6 million a year earlier .',\n", + " 'The winner of the Williams-Mauresmo match will play the winner of Justine Henin-Hardenne vs. Chanda Rubin .',\n", + " '\" By its actions , the Bush administration threatens to give a bad name to a just war , \" the Connecticut Democrat told a Capitol Hill news conference .',\n", + " 'I urge Congress to quickly resolve any differences and send me the final bill as soon as possible so that I can sign it into law . \"',\n", + " 'The House passed a similar measure by a wide margin on Sept . 9 .',\n", + " 'Reuters reported Braun ended June with $ 22,126 in the bank , according to her FEC report , and the Rev. Al Sharpton reported $ 12,061 in the bank .',\n", + " 'After learning she would no longer get an experimental drug that treated her severe peanut allergy , Allison Smith reacted with panic .',\n", + " 'Darren Dopp , a Spitzer spokesman , declined to comment late Thursday .',\n", + " 'Using bookmarks and back and forth buttons - we had about eighteen different things we had in mind for the browser , \" he told an industry audience in London yesterday .',\n", + " 'Against the dollar , the euro rose as high as $ 1.1535 -- a fresh four-year high -- in morning trade before standing at $ 1.1518 / 23 at 0215 GMT .',\n", + " 'Three American warships are off the Liberian coast carrying a total of 2,300 marines .',\n", + " 'Those involved were allegedly told to never speak of the incident again , according to the letter .',\n", + " 'Shares of Berkeley , California-based Xoma dropped $ 1.49 or 27 percent , to $ 4 in Instinet trading .',\n", + " '\" Removing the waiver means nothing when Oracle still has pending litigation in Delaware that opposes the PeopleSoft / J.D. Edwards transaction , \" PeopleSoft responded .',\n", + " \"At best , Davydenko 's supporters were naively ignorant of tennis etiquette ; at worst , they cheated - yet went without penality from umpire Lars Graf .\",\n", + " \"The Pentagon says it 's a technique that 's been successful in predicting elections , even box-office receipts .\",\n", + " 'Treatment guidelines , many written by medical-specialty organizations , recommend approaches to many ailments , such as painkillers and exercise for arthritis .',\n", + " 'After three months , Atkins dieters had lost an average of 14.7 pounds compared with 5.8 pounds in the conventional group .',\n", + " '\" It \\'s probably not the easiest time to come in and take over the shuttle program , but I look forward to the challenge , \" Parsons told reporters at NASA headquarters .',\n", + " 'None of Deans opponents picked him as someone to party with , nor was Dean asked that question .',\n", + " 'Mr. Kozlowski contends that the event included business and that some of those attending were Tyco employees .',\n", + " 'The Mariners torched Randy Choate and Juan Acevedo in the eighth for four runs and a 12-5 bulge .',\n", + " 'In a statement , the Redmond , Wash . , company said that it was acquiring the \" intellectual property and technology assets \" of GeCAD .',\n", + " 'The horrific nature of the attacks is often fueled by a mix of tribal hatreds and a desire to spread terror in the region .',\n", + " 'The Ireland Palestine Solidarity Campaign , of which Mr O Muireagáin was a member , welcomed his release .',\n", + " 'A spokeswoman for Interscope Geffen A & M declined to confirm how many employees were affected .',\n", + " 'In that same time , the S & P and the Nasdaq have gained about 3.5 percent .',\n", + " 'On Monday , U.S. Army Pfc. Jessica Lynch was awarded the Bronze Star , Purple Heart and Prisoner of War medals at Walter Reed Army Medical Center .',\n", + " 'He urged member states that contribute police and soldiers to U.N. peacekeeping operations to provide more women .',\n", + " 'Other changes in the plan refine his original vision , Libeskind said .',\n", + " 'The new contract extends the guarantee that his annual pay and bonus will be at least $ 2.4 million a year .',\n", + " 'A rebel who was captured said more than 2,000 insurgents were involved in the attack .',\n", + " 'Under Kansas law , it is illegal for children under the age of 16 to have sexual relations .',\n", + " 'Aside from meeting Chinese Premier Wen Jiabao on the sidelines of the Forum , Mr Goh also met Pakistani President Pervez Musharraf and Tajikistan President Emomali Sharipovich Rakhmonov .',\n", + " \"Fires in Spain 's Extremadura region , which borders Portugal , have forced hundreds of people to evacuate their homes .\",\n", + " 'On a planet that takes nearly 165 years to orbit the sun , spring can last more than 40 years .',\n", + " \"SSE CEO Marchant said he would sell on Midlands ' power generation assets in Turkey and Pakistan to fellow British utility International Power Plc IPR.L for 21 million pounds .\",\n", + " 'Crossing Jordan will be back in January after star Jill Hennessy gives birth .',\n", + " 'Yale spokesman Tom Conroy said the university was prepared to keep the campus running with temporary workers and managers doing extra work .',\n", + " 'A member of the chart-topping collective So Solid Crew dumped a loaded pistol in an alleyway as he fled from police , a court heard yesterday .',\n", + " 'They passed through the Lemelson Medical , Educational and Research Foundation Limited Partnership in 2001 to Syndia .',\n", + " 'Shares of USA Interactive rose $ 2.28 , or 7 percent , to $ 34.96 on Friday in Nasdaq Stock Market composite trading and have gained 53 percent this year .',\n", + " 'We believe them to be without merit , and will defend ourselves vigorously .',\n", + " 'The Russell 2000 index , which tracks smaller company stocks , was up 1.02 , or 0.21 percent , at 496.83 .',\n", + " 'The law does not regulate how much individuals can contribute to their own campaigns , a decided advantage for millionaires like Mr. Schwarzenegger and Mr. Ueberroth , both Republican candidates .',\n", + " 'Dixon put his style on display Sunday afternoon in winning the Honda Indy 225 at Pikes Peak International Raceway .',\n", + " 'In a statement later , he said it appeared his side may have fallen a bit short .',\n", + " 'Among the most recent additions to the list , which to date includes more than 360 groups and individuals , is Zelimkhan Yandarbiev , the former president of Chechnya .',\n", + " 'As of Tuesday , almost 250 health-care workers were in quarantine .',\n", + " 'About 1,000 people attended the ceremony , kicking off two days of observances tied to the March on Washington .',\n", + " 'Second quarter sales came in at $ 645 million , up from $ 600 million the year before , AMD said .',\n", + " \"The other 18 people inside the building - two visitors and 16 employees , including Harrison 's ex-girlfriend - escaped without injury , Aaron said .\",\n", + " 'Southwest exercised nine 2004 options and six 2005 options , which were accelerated for delivery next year .',\n", + " 'Professor Hermon-Taylor adds , An unexpected finding of the research showed that patients suffering with Irritable Bowel Syndrome ( IBS ) were also infected with the MAP bug .',\n", + " 'Preliminary reports were that the men were not seen together at the airport , sources said .',\n", + " 'Details of the research appear in the Nov. 5 issue of the Journal of the American Medical Association .',\n", + " 'Six countries have advised their citizens not to travel to Taiwan for any reason , the ministry said .',\n", + " 'The technology-laced Nasdaq Composite Index < .IXIC > tacked on 5.91 points , or 0.29 percent , to 2,053.27 .',\n", + " 'United Airlines plans to become the first domestic airline to offer e-mail on all its domestic flights by the end of the year , the company announced yesterday .',\n", + " \"Volodymyr Gorbanovsky , deputy general director of the plane 's owners , Mediterranean Airlines , said it appeared to have veered from its flight path because of the fog .\",\n", + " 'Meteorologists predicted the storm would become a Category 1 hurricane before landfall .',\n", + " \"I called the number and the lady told me she was talking on the phone to Toby Studabaker , ' ' Sherry Studabaker told BBC television .\",\n", + " 'Sweeney said he would formally announce the formation of the new union on Wednesday in Detroit .',\n", + " 'Shares in Safeway rose 1-1 / 4p to 290-1 / 2p while Morrison shares rose 1-3 / 4p to 222-3 / 4p in early morning trade .',\n", + " 'Emeryville-based Ask Jeeves agreed to sell a business software division to a Sunnyvale-based rival , Kanisa , for $ 4.25 million .',\n", + " '\" I picked prostitutes as my victims because I hate most prostitutes and I did not want to pay them for sex . \"',\n", + " 'A positive PSA test has to be followed up with a biopsy or other procedures before cancer can be confirmed .',\n", + " 'But Mr. Peterson added , \" I don \\'t know anybody in the conference committee who \\'s fighting to keep it out completely . \"',\n", + " 'The technology-heavy Nasdaq Composite Index gained 25.75 points , or 1.5 percent , to 1,746.46 , its highest close in about 15 months .',\n", + " 'In 1998 Intel invested $ 500 million in Micron , but later sold the equity it had bought , Mulloy said .',\n", + " 'So far , however , only four companies have licensed the protocols , according to the report to the judge yesterday .',\n", + " 'The speaker issued a one-paragraph statement , saying , \" I am advised that certain allegations of criminal conduct have been interposed against my counsel , J. Michael Boxley .',\n", + " 'In the second quarter , Anadarko now expects volume of 46 million BOE , down from 48 million BOE .',\n", + " \"The Institute for Supply Management 's manufacturing index dipped to 53.7 from 54.7 in August .\",\n", + " 'It got up on his personal radar screen this past week , I \\'ve given him my pitch and he was quite supportive , \" he said .',\n", + " '\" It \\'s just too important to keeping crime down , \" he said of Operation Impact , which began Jan. 3 .',\n", + " 'The couple was granted an annulment in September 2001 and Joanie Harper was given sole custody of Marques and Lyndsey , court records show .',\n", + " \"Iraq 's economy was ravaged during years of U.N. sanctions over ex-president Saddam Hussein 's 1990 occupation of Kuwait and by the U.S.-led war to oust him which ended in April .\",\n", + " \"Nationwide , severe acute respiratory syndrome has infected more than 5,300 people , about two-thirds of world 's total , and killed 343 of them .\",\n", + " 'Under the settlement , Solutia will pay $ 50 million in equal installments over a period of 10 years .',\n", + " 'Smith found the cigarette tax falls on the tobacco consumer , not the tribe , meaning the tribe is simply an agent for collecting a tax .',\n", + " 'Hoffa , 62 , vanished on the afternoon of July 30 , 1975 , from a Bloomfield Township parking lot in Oakland County , about 25 miles north of Detroit .',\n", + " \"After Mao 's death in 1976 , Madame Mao was sentenced to life imprisonment for her role in the oppressive Cultural Revolution .\",\n", + " 'Six vehicles , including one belonging to the army , were damaged in the powerful explosion .',\n", + " 'If convicted , they face up to five years in prison and a $ 250,000 fine on each of the 11 counts .',\n", + " \"There will be no vote on the issue but those opposed to Robinson 's appointment are thought to outnumber those who accept it by around 20 to 17 .\",\n", + " '\" Writing safe programs that demonstrate an infection vector is adequate [ to demonstrate a vulnerability ] without building in the reproductive sequences .',\n", + " \"In other words , Cablevision is obligated to pay for YES Network 's lawsuit against Time Warner Cable .\",\n", + " \"Germany 's Foreign Ministry said it believed the passengers were from the northern states of Lower Saxony and Schleswig-Holstein , but had no further details .\",\n", + " \"A gunman who had ``a beef with the Postal Service ' ' stormed into a suburban post office and took two employees hostage Wednesday , authorities said .\",\n", + " 'The agency will \" consider as timely any tax returns or payments due \" Aug. 15 if they are submitted by Aug. 22 .',\n", + " 'Appleton said police continue to hold out the possibility that more than one person was involved in the poisonings .',\n", + " '\" My judgment is 95 percent of that information should be declassified , become uncensored , so the American people would know . \"',\n", + " 'Shares of Microsoft fell 1 cent to close at $ 25.65 on the Nasdaq Stock Market .',\n", + " 'His other films include \" Malcolm X , \" \" Summer of Sam \" and \" Jungle Fever . \"',\n", + " 'She was arraigned in New York state on three counts of murder and ordered held without bail .',\n", + " 'He acted as an international executive producer on Who Wants to be a Millionaire and The Weakest Link .',\n", + " 'After 9 / 11 , Connolly said , agents spent thousands of hours investigating al-Bayoumi .',\n", + " 'The Dow Jones industrial average fell 98.30 , or 1.1 percent , while bond values fell , too .',\n", + " \"Proving that the Millville son 's sacrifice would not go unsung , Mayor James Quinn ordered all city flags flown at half-mast for the next 30 days .\",\n", + " \"With an estimated net worth of $ 1.7 billion , Mrs. Kroc ranked No. 121 on Forbes magazine 's latest list of the nation 's wealthiest people .\",\n", + " 'The biggest threat to order seemed to be looting and crime , including robberies by some of the prisoners freed by Saddam before the war .',\n", + " 'Pratt & Whitney had said that 75 per cent of the engine equipment would be outsourced to Europe , with final assembly in Germany .',\n", + " '\" People who are high in positive emotions sleep better , they have better diets , they exercise more , they have lower levels of these stress hormones , \" Cohen said .',\n", + " \"Robin Saunders , head of the bank 's London-based principal finance unit , is also expected to quit .\",\n", + " \"But the new study , from the Women 's Health Initiative , said the opposite .\",\n", + " 'It wants to force him to return his allegedly ill-gotten gains , with interest , and pay penalties .',\n", + " 'Mahmud was seized near Tikrit , the area from which he and Hussein hail , about 90 miles north of Baghdad , U.S. military officials said Wednesday night .',\n", + " \"They remain 40 percent below the levels prior to February 's initial overstatement news .\",\n", + " \"The arrests revealed new details about the four-month investigation at Selenski 's Mount Olivet Road home .\",\n", + " \"The broad Standard & Poor 's 500 Index < .SPX > lost 6 points , or 0.71 percent , to 927 .\",\n", + " 'Bond voiced disappointment that neither President Bush nor his brother attended the 2002 conference in Texas or the 2003 meeting in Florida .',\n", + " \"Now , Blanca 's American husband , 63-year-old Roger Lawrence Strunk , faces a murder indictment issued in February by the Philippine government , which says he 's the leading suspect .\",\n", + " 'A key question was whether France , which infuriated Washington by leading the charge against U.N. authorization for the war , would vote \" Yes \" or abstain .',\n", + " '\" Certainly what we know suggests that we should take what they \\'re saying very seriously , \" he added .',\n", + " 'On Wall Street , trading resumed with some glitches from the blackout that continued to affect parts of New York City .',\n", + " 'Tony Blair has taken a hardline stance arguing nothing should be done to lessen the pressure on Mugabe at the gathering in the capital Abuja .',\n", + " 'Emmy and Golden Globe Award-winners James Brolin and Judy Davis star as Ronald and Nancy Reagan in The Reagans .',\n", + " 'The Securities and Exchange Commission also filed a civil fraud complaint against Dinh , of Phoenixville .',\n", + " \"Levin 's attorney , Bo Hitchcock , declined to comment Friday .\",\n", + " 'A month ago a military C-17 transporter returned to Baghdad when an engine exploded .',\n", + " 'The Swiss franc rose nearly a third of a centime against the dollar and was last at 1.2998 < CHF = > to the greenback .',\n", + " 'Associated Press Writers Michelle Morgante in San Diego and Ken Ritter in Las Vegas contributed to this article .',\n", + " \"The purchase is the largest conservation transaction in Hawaii 's history , the agencies said .\",\n", + " 'She said Nelson heard someone shout , \" Let \\'s get the Jew ! \"',\n", + " 'The FCC said existing televisions , VCRs , DVD players and related equipment would remain fully functional under the new broadcast flag system .',\n", + " 'Motorola had scheduled its earnings report to be released today after the close of trading .',\n", + " 'For the third time in the past four years , wildfires are the problem .',\n", + " 'Most of those killed were labourers from Jharkhand and Nepal who were working at Rohtang tunnel .',\n", + " \"Axcan 's shares closed down 63 Canadian cents , or 4 percent , at C $ 16.93 in Toronto on Tuesday .\",\n", + " 'It forecast 445 billion yen in loan-loss charges for the year ending next March .',\n", + " 'Iraqi doctors who treated former prisoner of war Jessica Lynch dismissed on Friday claims made in her biography that she was raped by her Iraqi captors .',\n", + " 'On Thursday , Lee won a preliminary injunction in New York preventing Viacom from using the name \" Spike TV . \"',\n", + " 'A grief-stricken old woman , disconsolate with grief , smeared her face with dirt , uttering : \" My child , my child . \"',\n", + " 'In 1999 a California legislator proposed a law requiring driving tests for those over the age of 75 who sought to renew their licenses .',\n", + " 'More than 400 people have been killed since August , including Afghan and foreign aid workers , U.S. and Afghan soldiers , officials and police , and many guerrillas .',\n", + " 'The results will be published the July 10 issue of the journal Nature .',\n", + " 'The Opposition Leader , Simon Crean , said John Howard had been forced to make changes by the incompetence of his ministers .',\n", + " 'In fact , 10 million shares of the sale went to an unidentified charitable trust - which promptly sold them .',\n", + " 'The airline says only Robert Milton is taking a 15-per-cent reduction in pay .',\n", + " 'The WHIMS study found that combination hormone therapy doubled the risk for probable dementia in women 65 and older and did not prevent mild cognitive impairment .',\n", + " \"The broader Standard & Poor 's 500 Index gained 16.02 points , or 1.62 percent , at 1,004.63 .\",\n", + " \"Researchers found that people 65 and older who had fish once a week had a 60 % lower risk of Alzheimer 's than those who never or rarely ate fish .\",\n", + " 'The product also features an updated release of the Apache Web server , as well as Apache Tomcat and Apache Axis .',\n", + " \"Clayton 's shares were also suspended from trading on the New York Stock Exchange .\",\n", + " 'The cleanup , including new carpeting , electrical wiring and bathrooms , cost about $ 130 million .',\n", + " 'Three such vigilante-style attacks forced the hacker organiser , who identified himself only as \" Eleonora67 ] , \" to extend the contest until 8am ( AEST ) today .',\n", + " 'They said many had been beaten , and police officers also stormed private wards at the hospital and harassed patients .',\n", + " 'US authorities blame Al Qaeda for the attacks , which killed 231 people , including 12 Americans .',\n", + " \"Standing with reporters and photographers about 150 yards from the prison gates was Brent Newbury , president of the Rockland County Patrolmen 's Benevolent Association .\",\n", + " 'Bremer said one initiative is to launch a US $ 70 million nationwide program in the next two weeks to clean up neighborhoods and build community projects .',\n", + " 'Earlier this year , the company announced a restatement of its 2002 , 2001 and 2000 financial results .',\n", + " 'U.S. same-store sales last months at Tim Hortons rose 8.8 per cent .',\n", + " 'World economic leaders hailed signs of a global revival on Tuesday but agreed it had to be handled with care to prevent any setback .',\n", + " 'Sterling was down 0.8 percent against the dollar at $ 1.5875 GBP = .',\n", + " 'Police said they believe Cruz knew of the girl through one of her former schoolmates - though neither the girl nor her family knew him .',\n", + " 'Peck died peacefully at his Los Angeles home early Thursday with his wife of 48 years , Veronique , by his side .',\n", + " 'Holden toured Northmoor , a small town in Platte County , Mo . , where between 25 and 30 homes were either damaged or destroyed .',\n", + " 'But in the end , all the worm did was visit a pornography site , said Vincent Weafer , a security director with Symantec Security Response in California .',\n", + " 'While it was being called mandatory , Dupont said authorities were not forcing people from their homes .',\n", + " 'Shares in Juniper Networks jumped more than 10 per cent on Monday after the networking equipment maker inked a sales and marketing deal with Lucent Technologies .',\n", + " 'The legislation came after U.S. District Judge Lee R. West in Oklahoma City ruled last week that the FTC lacked authority to run the registry .',\n", + " 'The report said the Corpus Christi-based agent twice called Escobar while the legislator was with the other protesting Democrats in Ardmore , Okla .',\n", + " 'Yesterday , shares closed up 29 cents , or 0.54 percent , at $ 54.32 .',\n", + " 'North American futures pointed to a sub-par start to trading on Tuesday , with investors ready to get their first taste of quarterly earnings .',\n", + " \"President Bush and top officials in his administration cited the threat from Iraq 's alleged chemical and biological weapons and nuclear weapons program as the main justification for going to war .\",\n", + " 'Officials with Rescue California , one of the groups leading the recall campaign , called the lawsuit laughable .',\n", + " 'The agreement resolves a lawsuit AOL filed against Microsoft in January 2002 on behalf of its subsidiary , Netscape Communications .',\n", + " 'The street-racing sequel \" 2 Fast 2 Furious \" won the pole position at the box office , taking in an estimated $ 52.1 million in its opening weekend .',\n", + " 'Dell has about 32 percent of the U.S. market , but much lower share in the rest of the world .',\n", + " 'He was sent to Larned State Hospital , where he was evaluated and treated .',\n", + " \"Two weeks later on New Year 's night , Nikia Shanell Kilpatrick was discovered bound and strangled in her apartment .\",\n", + " 'With the power back on , state workers headed back to their jobs Friday morning .',\n", + " \"The report shows that drugs sold in Canadian pharmacies are manufactured in facilities approved by Health Canada - the FDA 's counterpart in Canada .\",\n", + " 'Democrats argue they are not constitutionally required to redraw the lines , and that proposed maps would disenfranchise minorities and rural Texas .',\n", + " 'After about three hours of negotiations , the gunman released the hostages after authorities delivered on his request for soft drinks .',\n", + " 'Lee Peterson testified that he reached his son on his cell phone and talked to him for a couple of minutes .',\n", + " 'Both bidders agreed to assume about $ 90 million in debt owed on the planes .',\n", + " \"In 1999 , the building 's owners , the Port Authority of New York and New Jersey , issued guidelines to upgrade the fireproofing to a thickness of 1 { inches .\",\n", + " \"A woman was listed in good condition at Memorial 's HealthPark campus , he said .\",\n", + " \"Palm plans to issue about 13.9 million shares of Palm common stock to Handspring 's shareholders , on a fully diluted basis .\",\n", + " 'The judge ordered the unsealing yesterday at the request of several news agencies , including The Seattle Times , The Associated Press and the Seattle Post-Intelligencer .',\n", + " 'Assani said Tuesday he did not know what to charge the men with because US officials refused to share information with him .',\n", + " 'Researchers predicted an additional 23 cases of dementia a year for every 10,000 women on the therapy .',\n", + " 'Jacob has pushed consolidation for years , but he has said many communities , especially rural ones , have opposed it .',\n", + " 'A slit the size of one created in the test would let in a stream of gas three times as hot as a blowtorch . \"',\n", + " 'Legato stockholders will get 0.9 of a share of EMC stock for every share of Legato they own .',\n", + " '\" The Republicans went into a closet , met with themselves , and announced a \\' compromise . \\' \"',\n", + " 'Defense Secretary Donald Rumsfeld is awaiting recommendations from his commanders .',\n", + " \"Goodrem , 18 , announced on Friday that she was suffering Hodgkin 's disease , a curable form of lymphoma cancer .\",\n", + " 'He says that \" this is a time when we priests need to be renewing our pledge to celibacy , not questioning it .',\n", + " \"Myanmar 's pro-democracy leader Aung San Suu Kyi will be kept under house arrest following her release from a hospital where she underwent surgery , her personal physician said Friday .\",\n", + " 'United already has paid the city $ 34 million in penalties for not meeting the first round of employment targets .',\n", + " 'The budget fell steadily until dropping as low as $ 2.93 billion in 1998 and has gradually risen to $ 3.27 billion for fiscal 2002 .',\n", + " 'A council of up to nine Iraqis probably will lead the countrys interim government through the coming months , the U.S. civil administrator said Monday .',\n", + " 'They found molecules that can only be produced when ozone breaks down cholesterol .',\n", + " 'Ghulam Mahaiuddin , head of administration in the southern province of Helmand , said the bus blast happened early in the morning , west of the provincial capital Lashkargah .',\n", + " \"Synthes-Stratec 's cash payment for Mathys would be financed with cash on hand plus bank borrowings being arranged by Credit Suisse First Boston .\",\n", + " 'Defense Secretary Donald H. Rumsfeld and others argued that Saddam Hussein possessed chemical and biological weapons and was hiding them .',\n", + " 'The numbers highlight a conundrum : the difficulty of classifying racial and ethnic categories in an increasingly fluid and multi-ethnic society .',\n", + " '\" Kmart needs to dramatically overhaul management in the areas of buying , store operations and sourcing , \" he said .',\n", + " 'The fight over online music sales was disclosed in documents filed last week with the judge and made available by the court yesterday .',\n", + " \"The two cases concerned the admissions policies of the University of Michigan 's law school and undergraduate college .\",\n", + " 'With the test , Hubbard said , \" I believe that we have found the smoking gun .',\n", + " \"Scribner 's body was found floating in the city 's Portage Bay , where he lived with his wife in a houseboat .\",\n", + " \"Congo 's war began in 1998 when Uganda and Rwanda invaded to back rebels fighting to topple the central government .\",\n", + " 'The Pentagon , saying that Boykin requested it , is investigating his remarks .',\n", + " \"She said Jane Doe 's lawyers asked Verizon to withhold her name because she was planning on challenging the subpoena .\",\n", + " 'The two men were allegedly trying to engage Russian exiles in Britain in the assassination plot .',\n", + " \"The council comprises 13 Shi 'ites , five Sunni Arabs , five Kurds , an Assyrian Christian and a Turkmen .\",\n", + " 'Eyewitnesses told how Sally , 62 , originally from Coatbridge , Lanarkshire , screamed as the escalator collapsed then sucked her in .',\n", + " 'The company is working with the ObjectWeb consortium , Apache Software Foundation and the Eclipse IDE development community as part of the effort .',\n", + " 'The United States has accused Iran of masking plans to make nuclear weapons behind a civilian nuclear programme .',\n", + " 'A new variant of Blaster also appeared Wednesday and seemed to be spreading , according to antivirus companies .',\n", + " 'Directed by Jonathan Lynn from a script by Elizabeth Hunter and Saladin K. Patterson .',\n", + " 'The U.N. nuclear watchdog reprimanded Iran on Thursday for failing to comply with its nuclear safeguards obligations and called on Tehran to unconditionally accept stricter inspections by the agency .',\n", + " 'She said he persisted and she eventually allowed him to fix her bike while she sat in the front seat of his car .',\n", + " 'A base configuration with a 2.0GHz Intel Celeron processor , 128M bytes of memory , a 40G-byte hard drive , and a CD-ROM drive costs US $ 729 .',\n", + " '\" We have some small opportunities in November and maybe January , \" Mr. Parsons said optimistically .',\n", + " \"Tidmarsh will compete in today 's third round .\",\n", + " 'Foam flaking off from all over the tank left dozens of pockmarks each flight on the thermal tiles that cover much of the shuttle , Turcotte said .',\n", + " 'Mr Eddington described Monday \\'s talks with union leaders as \" sensible \" .',\n", + " 'Vaccine makers have been thrust into the limelight as government programs to encourage wider vaccination and fears of biological attacks on civilian and military targets .',\n", + " 'During the same quarter last year , EDS declared a profit of $ 354 million , or 72 cents per share .',\n", + " 'ICANN has criticised the changes and asked VeriSign to voluntarily suspend them .',\n", + " 'Less than a month after departing her anchor \\'s chair at \" Dateline NBC , \" Jane Pauley has signed a new deal with NBC to host a syndicated daytime talk show .',\n", + " '\" The releases are the latest in a series of efforts by the government to move Myanmar to multi-party democracy and national conciliation . \"',\n", + " 'The administration cited those links as primary justification for invading Iraq and toppling the regime of Saddam Hussein .',\n", + " 'A few hours later , the House voted 256 to 170 to pass the Healthy Forest Restoration Act sponsored by Rep. Scott McInnis , R-Colo .',\n", + " 'I would like to publicly thank Sir Alex Ferguson for making me the player I am today . \"',\n", + " 'Bremer will focus on the politics , Garner said , adding that he expects Bremer to arrive in Iraq by next week .',\n", + " 'More than 130 people and $ 17 million have been seized nationwide in operations by the FBI and other agencies to stop cybercrime .',\n", + " \"The family stopped for lunch at Freshwater Spit , where several children went to the water 's edge to play in the surf shortly after noon .\",\n", + " 'Island Def Jam must pay $ 52 million in punitive damages , and Cohen must pay the remaining $ 56 million , the jury said .',\n", + " \"Without going into specifics , Oracle managers also indicated PeopleSoft 's roughly 8,000 workers could expect mass layoffs .\",\n", + " '\" I am happy to be here in the United States , far away from the clutches of the tyrant Castro , \" Wilson said through a Spanish interpreter .',\n", + " 'Excluding one-time items , the company enjoyed a profit of 6 cents a share .',\n", + " 'In January 1996 , two of his nude models were arrested atop of a Manhattan snowdrift , posed beneath an ice-cream parlor sign that advertised Frozen Fantasies .',\n", + " 'Lakhani was charged with attempting to provide material support and resources to a terrorist group and acting as an arms broker without a license .',\n", + " 'General Motors Corp. posted a record 8.4 percent improvement in 2000 .',\n", + " 'Woodley , who underwent a liver transplant in 1992 , died of liver and kidney failure .',\n", + " 'Her detention brings to 19 the number of the 55 most-wanted Iraqis now in US custody .',\n", + " 'Just five months ago , Dean committed to accepting taxpayer money and vowed to attack any Democrat who didnt .',\n", + " '\" Contrary to the court \\'s decision , we firmly believe Congress gave the FTC authority to implement the national Do Not Call list .',\n", + " \"Ruffner , 45 , doesn 't yet have an attorney in the murder charge , authorities said .\",\n", + " \"It also faces significant regulatory delays and uncertainty , and threatens serious damage to the company 's business , he added .\",\n", + " 'Mason said al-Amoudi was arrested at Dulles International Airport on Sunday as he came into the country .',\n", + " \"I never thought I 'd write these words , but here goes : I miss Vin Diesel .\",\n", + " 'The report ranked 45 large companies based on employment , marketing , procurement , community reinvestment and charitable donations .',\n", + " \"Those findings , published in today 's Journal of the American Medical Association , are the latest bad news about estrogen-progestin therapy .\",\n", + " 'A view of the devastation left behind when a man drove his car through a crowded farmers market in Santa Monica on Wednesday .',\n", + " \"Experts said the case marks one of the first times in which a parent was charged with contributing to a child 's suicide .\",\n", + " 'An amendment to remove the exemption for the state-sanctioned sites failed on a much closer 237-186 vote .',\n", + " 'The AP quotes a local policeman as saying the British troops were targeted by townspeople angry over civilian deaths during a demonstration yesterday in the town of Majar Al-Kabir .',\n", + " 'Baer said he had concluded that lawyers for the two victims \" have shown , albeit barely ... that Iraq provided material support to bin Laden and al-Qaeda \" .',\n", + " 'Mayor Michael R. Bloomberg said yesterday that the men \\'s behavior \" was a disgrace , and totally inappropriate for city employees . \"',\n", + " 'Right now , only six states do : Arkansas , Michigan , Minnesota , New Jersey , Virginia , and Wisconsin .',\n", + " 'Mr. Brendsel is expected to remain with the Freddie Mac Foundation .',\n", + " 'They also are reshaping the retail business relationship elsewhere , as companies take away ideas and practices that change how they do business in their own firms and with others .',\n", + " 'The technology-laced Nasdaq Composite Index .IXIC rose 39.39 points , or 2.2 percent , to 1,826.33 , after losing more than 2 percent on Tuesday .',\n", + " 'Frank Quattrone , the former Credit Suisse First Boston technology investment-banking guru , reportedly pleaded not guilty Tuesday to charges of obstruction of justice and witness tampering .',\n", + " \"It is the fifth such election in Iraq , after the northern city of Mosul and three cities in Iraq 's south .\",\n", + " 'It said a civil complaint by the Securities and Exchange Commission is expected as well .',\n", + " '\" This individual \\'s lawyers are trying to obtain from the court a free pass to download or upload music online illegally . \"',\n", + " 'The 30-year bond US30YT = RR firmed 26 / 32 , taking its yield to 4.17 percent , after hitting another record low of 4.16 percent .',\n", + " 'The shooter ran away , police said , eluding an officer who gave chase .',\n", + " 'But SCO has hit back , saying : \" We view IBM \\'s counterclaim filing today as an effort to distract attention from its flawed Linux business model .',\n", + " 'Helicopters hovered over al-Khalidiya into the early hours of Sunday .',\n", + " \"If Walker appeals Parrish 's ruling , it would stop the extradition process and could take several months , Rork said .\",\n", + " 'Boykin ’ s also referred to Islamist fighters as America ’ s “ spiritual enemy ” that , “ will only be defeated if we come against them in the name of Jesus ” .',\n", + " \"The first vulnerability is a buffer overrun that results from IE 's failure to properly determine an object type returned from a Web server .\",\n", + " '\" To make sure that we avoided any perception of wrongdoing , we are not co-mingling appropriated and non-appropriated funds ( from Congress ) , \" said Faletti .',\n", + " 'In Falluja on Thursday , two grenades were thrown at soldiers from the Third Armored Cavalry Regiment , wounding seven .',\n", + " 'Prince Saud said , \" The Kingdom of Saudi Arabia has been wrongfully and morbidly accused of complicity in the tragic terrorist attacks of September 11 , 2001 . \"',\n", + " '\" If the voluntary reliability standards were complied with , we wouldn \\'t have had a problem . \"',\n", + " \"China accounted for about 14 percent of Motorola 's sales last year , and the company has large manufacturing operations there .\",\n", + " 'Shares of MONY were gaining $ 2.57 , or 9 % , to $ 31.90 in after-hours trading on Instinet .',\n", + " 'Founded in 1996 , the LendingTree exchange consists of more than 200 banks , lenders , and brokers .',\n", + " 'Houston fourth-graders also performed similarly to national peers in writing .',\n", + " 'If achieved it would represent an increase of 10 per cent from the same quarter a year ago .',\n", + " \"It cut taxes while balancing the budget for three years and reduced Europe 's second highest per capita national debt .\",\n", + " '\" We continue to work with the Saudis on this , but they did not , as of the time of this tragic event , provide the additional security we requested . \"',\n", + " 'In the second quarter last year , the company experienced a net loss of $ 185 million , or 54 cents a share , on sales of $ 600 million .',\n", + " 'We don \\'t know if any will be SARS , \" said Dr. James Young , Ontario \\'s commissioner of public safety .',\n", + " 'We need a certifiable pay as you go budget by mid-July or schools wont open in September , Strayhorn said .',\n", + " 'In 1995 , Schwarzenegger expanded the program nationwide to serve 200,000 children in 15 cities , most who come from housing projects or homeless shelters .',\n", + " 'A spokesman said : \" Further testing is under way but at this stage , given the early detection , the outlook is positive . \"',\n", + " \"Except for a few archaic characteristics , they are as recognizable as Hamlet 's poor Yorick .\",\n", + " \"He is a brother to three-year-old Mia , from Kate 's first marriage , to film producer Jim Threapleton .\",\n", + " 'Only two other countries , Germany and the Dominican Republic , have publicly expressed reservations , and Germany has since said it will sign it .',\n", + " \"The settlement includes $ 4.1 million in attorneys ' fees and expenses .\",\n", + " 'It reports a mailing list of 50,000 and support from about 500 congregations and 50 bishops .',\n", + " 'Dick is going to be there as long as Dick wants to be there , \" Reuters reports Langone as saying .',\n", + " 'The state has received 19 entries in a competition for a World Trade Center Memorial and is working with families of victims to select a winner .',\n", + " 'However , John Clare , chief executive of the Dixons Group , expressed his disappointment .',\n", + " '\" The same three Response Team members also met with Monsignor Grass , who while manifestly repentant , admitted that the allegations were true .',\n", + " 'U.S. manufacturing growth expanded for the third straight month in September but at a slower pace , according to a report released shortly after the opening bell .',\n", + " 'Microsoft has described the technology as \" a brand new client platform for building smart , connected , media rich applications in Longhorn . \"',\n", + " \"But for more than a century , an untold amount of money intended for some of the nation 's poorest residents was lost , stolen or never collected .\",\n", + " 'It is rare for a legal challenge to occur before a bill becomes law .',\n", + " 'St. Paul Chairman and Chief Executive Jay S. Fishman , 51 , will be CEO of the combined company .',\n", + " 'There are now 37 active probable cases in the GTA , compared with 70 cases on June 6 .',\n", + " 'The constitutionality of outlawing partial birth abortion is not an open question .',\n", + " '\" It should have reported that it was sailing with an atomic bomb cargo , \" Anomeritis said , referring to the quantity of explosives on board .',\n", + " \"Mr. Bergonzi was the finance chief from 1995 until his departure in 1999 , and was intimately involved in the alleged scheme to pump up Rite Aid 's earnings .\",\n", + " 'The Aspen Fire had charred more than 12,400 acres by today on Mount Lemmon just north of Tucson , and more than 250 homes had been destroyed .',\n", + " 'They said he would be open to letting that license go to the city of Chicago .',\n", + " 'It argued that such access \" is not required by domestic or international law and should not be treated as a precedent . \"',\n", + " 'The male eagle was found by a zookeeper early Thursday , suffering from severe puncture wounds in his abdomen .',\n", + " 'He served as a marine in the Second World War and afterward began submitting articles to magazines .',\n", + " 'The Legislature on Wednesday sent Gov. Jeb Bush a bill it hopes will lower malpractice insurance rates and keep doctors from limiting their practices or moving out of state .',\n", + " \"In Taiwan , the capital 's mayor and other officials handed out free thermometers in a islandwide ``take-your-temperature ' ' campaign Sunday , amid evidence that containment efforts were also paying off .\",\n", + " \"Lawtey is not the first faith-based program in Florida 's prison system .\",\n", + " 'American forces here organized the elections , which officials say are important steps toward establishing democracy in Iraq .',\n", + " 'Negotiators talked with the boy for about an hour and a half , Bragdon said .',\n", + " '\" If draining the ponds in Maryland will help further establish [ his ] innocence , we welcome it .',\n", + " \"But Cruz resembled a police sketch of the suspect and had injuries consistent with what police expected from the struggle he had with the girl 's mother , Lansdowne said .\",\n", + " 'Passed in 1999 but never put into effect , the law would have made it illegal for bar and restaurant patrons to light up .',\n", + " 'A number below 50 suggests contraction in the manufacturing sector , while a number above that indicates expansion .',\n", + " 'Half of the women were given a daily dose of the drug and half took a placebo .',\n", + " 'It would be difficult to overestimate the potential dangers of the Remote Procedure Call ( RPC ) vulnerability .',\n", + " 'Mr. Bloomberg later told reporters that the agreement \" will be a compromise , like the real world requires . \"',\n", + " 'The man , who entered the post office in Lakeside shortly before 3 p.m. , gave up to deputies about 6 : 30 p.m.',\n", + " 'Rock singer Ozzy Osbourne , recovering from a quad bike crash , is conscious and joking in his hospital bed , his daughter Kelly said tonight .',\n", + " 'The New York senator \\'s new book , \" Living History , \" appears a certain bestseller .',\n", + " 'IBM is also \" pursuing membership in the group \" and plans to be an active participant , according to the CELF statement .',\n", + " 'Through Thursday , Oracle said 34.75 million PeopleSoft shares had been tendered .',\n", + " \"The company 's chief executive retired and chief financial officer resigned .\",\n", + " 'The FBI is trying to determine when White House officials and members of the vice president ’ s staff first focused on Wilson and learned about his wife ’ s employment at the agency .',\n", + " 'The tech-heavy Nasdaq Stock Markets composite index added 1.16 points to 1,504.04 .',\n", + " 'He will replace Ron Dittemore , who announced his resignation April 23 .',\n", + " 'Pen Hadow , who became the first person to reach the geographic North Pole unsupported from Canada , has just over two days of rations left .',\n", + " 'Many of the victims had been headed home for R & R or emergency leave when they were killed .',\n", + " 'The face of President Saddam Hussein was added to Iraqi currency after the 1991 Gulf War .',\n", + " '\" It \\'s safe to assume that the Senate is prepared to pass some form of cap , \" King said .',\n", + " 'He was also accused of masterminding bombings that killed 22 people in Manila in December 2000 .',\n", + " 'The Nikkei average ended trading down 1.83 percent at 10,310.04 , a four-week low .',\n", + " 'That exploit works on unpatched Windows 2000 machines with Service Pack 3 and 4 .',\n", + " 'The center has a budget of $ 45 million , most of which will be spent on research and testing , Bridges said .',\n", + " 'Out of the nearly $ 20 billion , $ 18.6 billion is earmarked for reconstruction of Iraq and $ 1.2 billion for Afghanistan .',\n", + " '\" For me , the Lewinsky imbroglio seemed like just another vicious scandal manufactured by political opponents , \" according to extracts released yesterday .',\n", + " 'Associated Press Writer Sara Kugler contributed to this report from New York City .',\n", + " 'That compares with a profit of $ 1 million , or breakeven , on $ 1.39 billion in revenue during the same period last year .',\n", + " 'The company reported quarterly revenue of $ 388.1 million , compared with $ 318.5 million in the same period a year ago .',\n", + " 'The study was presented yesterday in Chicago at the annual meeting of the Radiological Society of North America .',\n", + " 'Under the plan , Maine would act as a \" pharmacy benefit manager \" to lower the cost of prescription drugs .',\n", + " '\" The impact of increased prices has only begun to be seen in the financial numbers of the company , \" Noranda president and chief executive officer Derek Pannell said .',\n", + " 'In exchange , North Korea would be required to end its nuclear weapons program .',\n", + " 'Nine years ago , they were married by a justice of the peace .',\n", + " 'Thousands of people in the South of England caught a glimpse of a lunar eclipse as they gazed up at the sky on Saturday night .',\n", + " 'The rats that consumed the tomato powder had a 26 percent lower risk of prostate cancer death than the control rats , researchers found .',\n", + " 'It is a national concern that will touch virtually every American , \" Abraham said .',\n", + " 'It will also unveil a version of its Windows Server 2003 operating system tuned specifically for storage devices .',\n", + " 'At 12 , Lionel Tate was charged with first-degree murder over the death of Tiffany Eunick .',\n", + " 'About 1,417 schools statewide receive Title I money .',\n", + " 'Now , nearly two years later , Mallard prepares for trial on charges of murder and tampering with evidence .',\n", + " 'In Nairobi , Kenya , the Very Rev. Peter Karanja , provost of All Saints Cathedral , said the U.S. Episcopal Church \" is alienating itself from the Anglican Communion . \"',\n", + " 'Recall backers say they have collected 1,600,000 signatures , approaching twice the 897,158 needed to force an election .',\n", + " 'The book , called \" Lies and the Lying Liars Who Tell Them : A Fair and Balanced Look at the Right , \" hits stores this weekend .',\n", + " 'Selenski had previously spent seven years in prison on a bank robbery conviction .',\n", + " 'The decision was among the most significant steps toward deregulation undertaken during the Bush administration .',\n", + " '\" For us , that doesn \\'t make a difference - the sexual orientation , \" Archbishop Tutu said in the black urban centre of Soweto .',\n", + " 'Around 9 : 00 a.m. EDT ( 1300 GMT ) , the euro was at $ 1.1566 against the dollar , up 0.07 percent on the day .',\n", + " \"Sony 's portfolio gives subscribers a variety of personalized services including the ability to download and experience images , ring tones , music videos and other music entertainment services .\",\n", + " 'He said the local organized crime police were investigating a suspect , but he would not confirm if it was the 24-year-old cited by Bitdefender .',\n", + " 'Previously , it had reported a profit of $ 12 million , or 0 cents a share , for that period .',\n", + " 'The life expectancy for boys born in 2001 was 74.4 years , up two years since 1990 .',\n", + " 'Big Blue says the SEC calls the action a fact-finding investigation and has not reached any conclusions related to this matter .',\n", + " 'They were held under Section 41 of the Terrorism Act 2000 on suspicion of involvement in the commission , preparation or instigation of acts of terrorism .',\n", + " \"Starting on Saturday , every computer infected with MSBlast is expected to start flooding Microsoft 's Windows Update service with legitimate-looking connection requests .\",\n", + " '\" We see the First Amendment to protect religious liberty , not crush religious liberty , \" said Patrick Mahoney , director of the Christian Defense Coalition .',\n", + " 'Florida \\'s Third District Court of Appeal saidon Wednesdaythe original proceedings were \" irretrievably tainted \" by misconduct by lawyers representing the class .',\n", + " 'Courant Staff Writers Matt Eagan and Ken Davis contributed to this story .',\n", + " 'In addition , the committee noted , \" while spending is firming , the labour market has been weakening \" .',\n", + " 'Nigeria and other African oil producers are increasingly important in U.S. plans to lessen dependence on Middle Eastern suppliers for its energy security .',\n", + " 'Appeals had kept him on death row longer than anyone else in the U.S. , AP said .',\n", + " 'While many likely now will quit – as millions of women already have – Soltes said she likely will continue to prescribe the supplements for relief of change-of-life symptoms .',\n", + " \"The federal and local governments remained largely shut down in the aftermath of Hurricane Isabel , as were schools , and much of the region 's transportation network was slowed .\",\n", + " 'Ursel Reisen confirmed it operated the coach , but gave no details other than to say the passengers were of mixed ages .',\n", + " 'But he is the only candidate who has won national labor endorsements so far , picking up his 11th union on Tuesday when the United Steelworkers of America endorsed him .',\n", + " 'The judge also refused to postpone the trial date of Sept . 29 .',\n", + " 'Regardless , its first quarter saw a profit of $ 721 million , or 29 cents , on revenue of $ 17.9 billion .',\n", + " 'Under the proposal , Slocan shareholders will get 1.3147 Canfor shares for each common share .',\n", + " 'BellSouth also added 654,000 net long-distance customers in the third quarter .',\n", + " 'The day after Wilkie resigned , Howard said in a televised address that Iraq possessed \" chemical and biological weapons capable of causing death and destruction on a mammoth scale \" .',\n", + " 'The Dow Jones industrial average .DJI slipped 9.54 points , or 0.1 percent , at 9,558.92 .',\n", + " 'Earlier on Monday , Grant Thornton SpA repeated previous statements that it was \" a victim of fraudulent action \" .',\n", + " \"The winner of this year 's Most Popular New Talent Logie , Goodrem first went to a specialist on Monday feeling tired and unwell .\",\n", + " 'All three were studied for fingerprints , DNA and other traces of evidence , but prosecutors have not yet testified to what , if anything , they yielded .',\n", + " 'Cambone said U.S forces found the tractor-trailer April 19 at a Kurdish checkpoint near the city of Mosul in northern Iraq .',\n", + " 'Sony Ericsson also said it would shut down its GSM / UMTS R & D center in Munich , Germany , to increase profitability .',\n", + " 'J.D. Edwards shareholders will receive 0.86 of a share of PeopleSoft for each share of J.D. Edwards .',\n", + " 'That failure to act contributed to September the 11th and the failure to act today continues ( to put ) Americans in a vulnerable circumstance , \" Graham said .',\n", + " 'The Intel C + + Compiler for Platform Builder for Microsoft .NET is available for the suggested list price of $ 1,499 and is intended for OEM and system integrator use .',\n", + " '\" There were , \" said board member and Nobel-prize winning Stanford physicist Douglas Osheroff , \" some extremely bad decisions .',\n", + " \"He later learned that the incident was caused by the Concorde 's sonic boom .\",\n", + " \"Zulifquar Ali , a worshiper slightly wounded by shrapnel , said the attackers first targeted the mosque 's guards .\",\n", + " 'He said the problem needs to be corrected before the space shuttle fleet is cleared to fly again .',\n", + " 'Doctors would face fines and up to two years in prison for performing the procedure , except as a lifesaving step .',\n", + " '\" We make no apologies for finding every legal way possible to protect the American public from further terrorist attack , \" Barbara Comstock said .',\n", + " \"Since March 11 , when the market 's major indices were at their lowest levels since hitting multi-year lows in October , the tech-laden Nasdaq has climbed nearly 20 per cent .\",\n", + " 'The two men , whose names were not released , both were using Pakistani passports and were seen together at the airport earlier in the evening , police said .',\n", + " '\" We \\'ve put a lot of effort and energy into improving our patching progress , probably later than we should have .',\n", + " 'Rebels sought to consolidate their grip on a troubled northeastern Congolese town Tuesday after a week of fighting that killed at least 112 people .',\n", + " 'This was around the time Congress was debating a resolution granting the President broad authority to wage war .',\n", + " 'After their arrests , the men told investigators they paid to be smuggled into Blaine , Wash . , from Canada last month , two sources said .',\n", + " 'But Adora Obi Nweze , the NAACP state president , said the state only tried to prove its conclusion of suicide , rather than consider the possibility of murder .',\n", + " '\" We can \\'t change the past , but we can do a lot about the future , \" Sheehan said at a news conference Wednesday afternoon .',\n", + " 'Subsequently , some bondholders have filed suit to block the exchange offer .',\n", + " 'Officer Evans did not respond to requests for an interview last week .',\n", + " 'Sixty players and five coaches from the school attended the training camp in August in Preston Park , about 125 miles north of Philadelphia .',\n", + " 'The official would not name the leakers for the record and said he had no indication that Mr Bush knew about the calls .',\n", + " \"In his new position , Dynes will earn $ 395,000 , a significant increase over Atkinson 's salary of $ 361,400 .\",\n", + " 'Schering-Plough shares fell 72 cents to close at $ 18.34 on the New York Stock Exchange .',\n", + " \"Defense Secretary Donald Rumsfeld and other top Pentagon officials downplayed Wallace 's comments .\",\n", + " \"One , Fort Carson-based Sgt. Ernest Bucklew , 33 , was on his way home to attend his mother 's funeral in Pennsylvania , relatives said .\",\n", + " 'And \" although the preconditions for recovery remain in place , \" it said the prospects for British exports were \" weaker than previously expected . \"',\n", + " \"Israel 's defense minister on Sunday raised the specter of an Israeli invasion in the Gaza Strip , where Palestinian militants already face a deadly air campaign .\",\n", + " \"The broader Standard & Poor 's 500 Index .SPX eased 7.57 points , or 0.76 percent , at 990.94 .\",\n", + " 'The Organization of Petroleum Exporting Countries defied expectations on Wednesday and lowered its output ceiling by 900,000 barrels a day to 24.5 million barrels starting in November .',\n", + " 'Though that slower spending made 2003 look better , many of the expenditures actually will occur in 2004 .',\n", + " 'But Odette is the first to form over the Caribbean Sea in December , the Center said .',\n", + " \"Announced last week , Apple 's iTunes Music Store has sold over 1 million songs in the first week , the company announced on Monday .\",\n", + " '\" Such communication will help adult smokers make more informed choices , \" company vice president Richard Verheij told a House Energy and Commerce subcommittee at a separate hearing .',\n", + " 'The dollar was last at $ 1.1149 to the euro , close to its strongest level since April 30 .',\n", + " 'After their arrests , the men told investigators they paid to be smuggled into the United States from Canada last month .',\n", + " 'The Production Index rose by 1.4 percentage points from 51.5 percent in May to 52.9 percent in June .',\n", + " 'Germany , another opponent of the war currently on the U.N. Security Council , has moved closer to the United States , and did not insist on a timetable Monday .',\n", + " 'Power5 , like Power4 , includes two processor cores in a single slice of silicon .',\n", + " 'She said in 2003 , not one U.S. state had an obesity rate below 15 percent .',\n", + " 'Special , sensitive light sensors pick up the telltale glow , he said .',\n", + " \"The federal government is approving new pesticides without basic information such as whether they harm children , says Canada 's environment commissioner .\",\n", + " 'In September 2002 , the nearby Gard region was hit by similar floods .',\n", + " 'Overall , 83 percent of women washed up , compared with 74 percent of men .',\n", + " 'Analysts had expected 53 cents a share , according to research firm Thomson First Call .',\n", + " 'The number of people in the UK infected by HIV , the virus that causes Aids , increased by almost 20 per cent last year to nearly 50,000 .',\n", + " 'But stocks have rallied sharply over the past 3-1 / 2 months amid hopes for an economic rebound later this year .',\n", + " '\" Spring has arrived in Estonia -- we \\'re back in Europe , \" Prime Minister Juhan Parts told a news conference on Sunday .',\n", + " 'AMD will own a 60 percent interest in the new company and Fujitsu owns 40 percent .',\n", + " 'He was then given leave to appeal against the sentence to the Supreme Court of Appeal .',\n", + " \"Songs can be burned to CDs , but the same playlist can only be burned up to five times - Apple 's puts the limit at ten burns .\",\n", + " 'SSE shares were little changed , up 0.3 percent at 654 pence by 1018 GMT , but analysts welcomed the move .',\n", + " 'Shares of EDS gained $ 1.49 , or 6.6 percent , to $ 23.999 early Wednesday on the New York Stock Exchange .',\n", + " 'The deficit is still expected to top $ 500 billion in 2004 -- even with an improving economy .',\n", + " '\" As a responsible leader we feel it necessary to make these changes because online chat services are increasingly being misused , \" Microsoft told the BBC .',\n", + " 'We have already found two trailers , both of which we believe were used for the manufacture of biological weapons .',\n", + " 'America Online last quarter lost 846,000 dial-up subscribers .',\n", + " 'Governor Gray Davis estimated yesterday that the fires could cost nearly $ 2 billion .',\n", + " 'None of the Prairie Plant Systems marijuana can be distributed until the document is made available , she said .',\n", + " 'He left the ship after the collision , went to his home , and attempted suicide .',\n", + " \"He reportedly claims Prime Minister Sir Allan Kemakeza had made a deal with him to keep his guns while Harold Keke 's group was armed .\",\n", + " '\" The woman was taken to New Charing Cross Hospital by ambulance and her condition is critical .',\n", + " 'If sufficient evidence exists at the end of what is expected to be a five-day hearing , Peterson will be held for trial on the charges .',\n", + " 'They returned in 1999 after rebel raids on a neighboring Russian region and a series of deadly apartment-building bombings that were blamed on the rebels .',\n", + " 'Tropical Storm Claudette continued its slow churn toward the Texas coast early Sunday , with forecasters expecting the plodding storm system to make landfall as a hurricane by early Tuesday .',\n", + " '\" Everything is going to move everywhere , \" Doug Feith , undersecretary of defence for policy , said in an interview .',\n", + " 'According to Sanmina-SCI , Newisys , based in Austin , Texas , will become a wholly-owned subsidiary .',\n", + " 'Several black Democratic leaders were attempting to arrange a meeting with DNC chairman Terry McAuliffe to discuss the layoffs .',\n", + " \"The company 's shares fell 3.6 percent to close at $ 66.89 on the Nasdaq .\",\n", + " 'The Emeryville-based toy company filed suit Friday in federal court in Wilmington , Del . , claiming Fisher-Price is violating its 1998 patent on interactive learning books for toddlers and preschoolers .',\n", + " 'They also drafted a non-binding priorities list specifying that a quarter of it may be used to reduce planned 5 percent cuts in fees paid to health-care providers .',\n", + " 'The Thomson First Call consensus was for earnings of 19 cents a share .',\n", + " 'Mr Hadley , who has a new partner and child , said : \" I am absolutely delighted .',\n", + " '\" I love the Catholic Church with all my heart , mind , soul and strength , \" said Troy , who spoke quickly but in a steady voice .',\n", + " \"A divided Supreme Court ruled that Congress can force the nation 's public libraries to equip computers with anti-pornography filters .\",\n", + " 'Under NASD regulations , Mr. Young can file a response and request a hearing before an NASD panel .',\n", + " '\" We make no apologies for finding every legal way possible to protect the American public from further terrorist attacks , \" said Barbara Comstock , Ashcroft \\'s press secretary .',\n", + " 'One man died and 15 others were hospitalized after drinking tainted coffee following the April 27 worship service at Gustaf Adolph Lutheran Church in New Sweden .',\n", + " 'The Dow Jones industrial average < .DJI > rose 98.74 points , or 1.12 percent , to 8,949.00 .',\n", + " \"Analysts ' consensus estimate from Thomson First Call was for a loss of $ 2.08 a share , excluding one-time items .\",\n", + " 'He blamed guerrillas from the Taliban regime ousted in late 2001 and said it was possible the bomber died in the blast .',\n", + " 'Responding later , Edward Skyler , a spokesman for the mayor , said , \" The comptroller \\'s statements , while noteworthy for their sensationalism , bear no relation to the facts .',\n", + " 'Hannum was five and a half months pregnant when her mother died .',\n", + " \"Goold said reporters ' calls to Peterson may have been monitored .\",\n", + " \"The state 's population was 6,080,485 in 2000 , according to the U.S. census .\",\n", + " \"The other 18 people in the building , including Harrison 's ex-girlfriend , were not injured , police spokesman Don Aaron said .\",\n", + " 'He also accused royal courtiers of poisoning the brothers ’ “ little minds ” .',\n", + " 'The total for the Johnny Depp swashbuckler rose to $ 232.8 million .',\n", + " 'I would like to congratulate Russia on the successful World Climate Change Conference last week .',\n", + " 'At least seven other governments have signed agreements , but have asked not to have them publicized .',\n", + " \"The G + J executive was testifying in Manhattan 's State Supreme Court where O 'Donnell and G + J are suing each other for breach of contract .\",\n", + " 'By 10 p.m. , Claudette was centered about 320 miles east of Brownsville , with maximum sustained winds of 65 mph , 9 mph shy of hurricane strength .',\n", + " \"Jim Furyk celebrated his first Father 's Day as a father by winning his first major golf championship .\",\n", + " '\" We must not engage in borough warfare , \" Thompson testified at a budget hearing by the City Council \\'s Finance Committee .',\n", + " \"The Standard & Poor 's 500 Index slipped 3.14 points , or 0.29 percent , to 1,071.99 .\",\n", + " ...]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(raw_datasets[\"train\"][\"sentence1\"])\n", + "s1 = raw_datasets[\"train\"][\"sentence1\"]\n", + "list(s1)" + ] + }, + { + "cell_type": "markdown", + "id": "5cf6de11", + "metadata": {}, + "source": [ + "### Preprocessing a dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d943aef8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "checkpoint = \"bert-base-uncased\"\n", + "tokenizer = AutoTokenizer.from_pretrained(checkpoint)\n", + "\n", + "\n", + "\n", + "tokenized_setences_1 = tokenizer(list(raw_datasets[\"train\"][\"sentence1\"]))\n", + "tokenized_setences_2 = tokenizer(list(raw_datasets[\"train\"][\"sentence2\"]))\n", + "\n", + "inputs = tokenizer(\"This is the first sentence.\", \"This is the second one.\")\n", + "inputs" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "fe8b6ee0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[CLS]',\n", + " 'this',\n", + " 'is',\n", + " 'the',\n", + " 'first',\n", + " 'sentence',\n", + " '.',\n", + " '[SEP]',\n", + " 'this',\n", + " 'is',\n", + " 'the',\n", + " 'second',\n", + " 'one',\n", + " '.',\n", + " '[SEP]']" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tokenizer.convert_ids_to_tokens(inputs[\"input_ids\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "985efef8", + "metadata": {}, + "outputs": [], + "source": [ + "tokenized_datasets = tokenizer(\n", + " list(raw_datasets[\"train\"][\"sentence1\"]),\n", + " list(raw_datasets[\"train\"][\"sentence2\"]),\n", + " # padding=True,\n", + " # truncation=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "1f74cede", + "metadata": {}, + "outputs": [], + "source": [ + "def tokenize_function(example):\n", + " return tokenizer(example[\"sentence1\"], example[\"sentence2\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "daf40aba", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DatasetDict({\n", + " train: Dataset({\n", + " features: ['sentence1', 'sentence2', 'label', 'idx', 'input_ids', 'token_type_ids', 'attention_mask'],\n", + " num_rows: 3668\n", + " })\n", + " validation: Dataset({\n", + " features: ['sentence1', 'sentence2', 'label', 'idx', 'input_ids', 'token_type_ids', 'attention_mask'],\n", + " num_rows: 408\n", + " })\n", + " test: Dataset({\n", + " features: ['sentence1', 'sentence2', 'label', 'idx', 'input_ids', 'token_type_ids', 'attention_mask'],\n", + " num_rows: 1725\n", + " })\n", + "})" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)\n", + "tokenized_datasets" + ] + }, + { + "cell_type": "markdown", + "id": "c7eabb1e", + "metadata": {}, + "source": [ + "### Data Collator" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d09b2da6", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import DataCollatorWithPadding\n", + "\n", + "data_collator = DataCollatorWithPadding(tokenizer=tokenizer)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "dd8d1edb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 59, 47, 67, 59, 50, 62, 32]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "samples = tokenized_datasets[\"train\"][:8]\n", + "samples = {k:v for k, v in samples.items() if k not in [\"idx\", \"sentence1\", \"sentence2\"]}\n", + "[len(x) for x in samples[\"input_ids\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "324411eb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'input_ids': torch.Size([8, 67]),\n", + " 'token_type_ids': torch.Size([8, 67]),\n", + " 'attention_mask': torch.Size([8, 67]),\n", + " 'labels': torch.Size([8])}" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "batch = data_collator(samples)\n", + "{k: v.shape for k, v in batch.items()}" + ] + }, + { + "cell_type": "markdown", + "id": "186683dd", + "metadata": {}, + "source": [ + "### Training" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "2dc06641", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "54b1fbdffd5a490ea3f5c77bb333cd30", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/199 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mBertForSequenceClassification LOAD REPORT\u001b[0m from: bert-base-uncased\n", + "Key | Status | \n", + "-------------------------------------------+------------+-\n", + "cls.predictions.transform.dense.bias | UNEXPECTED | \n", + "cls.seq_relationship.weight | UNEXPECTED | \n", + "cls.seq_relationship.bias | UNEXPECTED | \n", + "cls.predictions.transform.dense.weight | UNEXPECTED | \n", + "cls.predictions.bias | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.weight | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.bias | UNEXPECTED | \n", + "classifier.bias | MISSING | \n", + "classifier.weight | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n", + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='50' max='50' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [50/50 00:24, Epoch 7/8]\n", + " </div>\n", + " <table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Step</th>\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>25</td>\n", + " <td>No log</td>\n", + " <td>0.627650</td>\n", + " </tr>\n", + " <tr>\n", + " <td>50</td>\n", + " <td>No log</td>\n", + " <td>0.723105</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "TrainOutput(global_step=50, training_loss=0.33963230133056643, metrics={'train_runtime': 24.9881, 'train_samples_per_second': 32.015, 'train_steps_per_second': 2.001, 'total_flos': 27186772017120.0, 'train_loss': 0.33963230133056643, 'epoch': 7.142857142857143})" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from transformers import TrainingArguments, Trainer, AutoModelForSequenceClassification\n", + "\n", + "model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)\n", + "\n", + "small_train_dataset = tokenized_datasets[\"train\"].shuffle(seed=42).select(range(100))\n", + "small_eval_dataset = tokenized_datasets[\"validation\"].shuffle(seed=42).select(range(100))\n", + "\n", + "training_args = TrainingArguments(\n", + " output_dir=\"test-trainer\",\n", + " per_device_train_batch_size=16,\n", + " per_device_eval_batch_size=32,\n", + " max_steps=50,\n", + " eval_strategy=\"steps\",\n", + " eval_steps=25,\n", + " save_strategy=\"no\",\n", + ")\n", + "\n", + "trainer = Trainer(\n", + " model,\n", + " training_args,\n", + " train_dataset=small_train_dataset,\n", + " eval_dataset=small_eval_dataset,\n", + " data_collator=data_collator,\n", + " processing_class=tokenizer,\n", + ")\n", + "\n", + "trainer.train()" + ] + }, + { + "cell_type": "markdown", + "id": "b85c9b79", + "metadata": {}, + "source": [ + "### Evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "ca131547", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "((408, 2), (408,))" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "predictions = trainer.predict(tokenized_datasets[\"validation\"])\n", + "predictions.predictions.shape, predictions.label_ids.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "335fb05b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1])" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "preds = np.argmax(predictions.predictions, axis=-1)\n", + "preds" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "de34b45d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'accuracy': 0.6985294117647058, 'f1': 0.810477657935285}" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import evaluate\n", + "\n", + "metric = evaluate.load(\"glue\", \"mrpc\")\n", + "metric.compute(predictions=preds, references=predictions.label_ids)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "c5419702", + "metadata": {}, + "outputs": [], + "source": [ + "def compute_metrics(eval_preds):\n", + " metric = evaluate.load(\"glue\", \"mrpc\")\n", + " logits, labels = eval_preds\n", + " predictions = np.argmax(logits, axis=-1)\n", + " return metric.compute(predictions=predictions, references=labels)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "d0d00e93", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fa90f9611a4b4ef199a9af203a303ef5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/199 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mBertForSequenceClassification LOAD REPORT\u001b[0m from: bert-base-uncased\n", + "Key | Status | \n", + "-------------------------------------------+------------+-\n", + "cls.predictions.transform.dense.bias | UNEXPECTED | \n", + "cls.seq_relationship.weight | UNEXPECTED | \n", + "cls.seq_relationship.bias | UNEXPECTED | \n", + "cls.predictions.transform.dense.weight | UNEXPECTED | \n", + "cls.predictions.bias | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.weight | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.bias | UNEXPECTED | \n", + "classifier.bias | MISSING | \n", + "classifier.weight | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n", + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='690' max='690' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [690/690 05:37, Epoch 3/3]\n", + " </div>\n", + " <table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Epoch</th>\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " <th>Accuracy</th>\n", + " <th>F1</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>1</td>\n", + " <td>No log</td>\n", + " <td>0.359542</td>\n", + " <td>0.855392</td>\n", + " <td>0.894831</td>\n", + " </tr>\n", + " <tr>\n", + " <td>2</td>\n", + " <td>No log</td>\n", + " <td>0.390240</td>\n", + " <td>0.850490</td>\n", + " <td>0.894281</td>\n", + " </tr>\n", + " <tr>\n", + " <td>3</td>\n", + " <td>0.359089</td>\n", + " <td>0.570827</td>\n", + " <td>0.852941</td>\n", + " <td>0.897959</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n", + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "02d141e6e38443e491fb09b4242f2cdc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "39e47c779a0045adbb40e3bdbcb16817", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "text/plain": [ + "TrainOutput(global_step=690, training_loss=0.2879850995713386, metrics={'train_runtime': 337.7385, 'train_samples_per_second': 32.581, 'train_steps_per_second': 2.043, 'total_flos': 428577075854640.0, 'train_loss': 0.2879850995713386, 'epoch': 3.0})" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "training_args = TrainingArguments(\"test-trainer\", eval_strategy=\"epoch\")\n", + "model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)\n", + "\n", + "training_args = TrainingArguments(\n", + " \"test-trainer\",\n", + " eval_strategy=\"epoch\",\n", + " per_device_train_batch_size=16,\n", + " per_device_eval_batch_size=32,\n", + ")\n", + "\n", + "trainer = Trainer(\n", + " model,\n", + " training_args,\n", + " train_dataset=tokenized_datasets[\"train\"],\n", + " eval_dataset=tokenized_datasets[\"validation\"],\n", + " data_collator=data_collator,\n", + " processing_class=tokenizer,\n", + " compute_metrics=compute_metrics,\n", + ")\n", + "\n", + "trainer.train()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch3/__init__.py b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch3/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch3/a_full_training_loop.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch3/a_full_training_loop.ipynb new file mode 100644 index 0000000..4be3e61 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch3/a_full_training_loop.ipynb @@ -0,0 +1,464 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "d0b5fa04", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "74a843d027e04caea684677ba30cab42", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/3668 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "26d88b5fa74845b184313b4fc131fb84", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/408 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "120fb83def4d4848a6b3db92ab23380d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/1725 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from datasets import load_dataset\n", + "from transformers import AutoTokenizer, DataCollatorWithPadding\n", + "\n", + "raw_datasets = load_dataset(\"nyu-mll/glue\", \"mrpc\")\n", + "checkpoint = \"bert-base-uncased\"\n", + "tokenizer = AutoTokenizer.from_pretrained(checkpoint)\n", + "\n", + "def tokenize_function(example):\n", + " return tokenizer(example[\"sentence1\"], example[\"sentence2\"], truncation=True)\n", + "\n", + "tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)\n", + "data_collator = DataCollatorWithPadding(tokenizer=tokenizer)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0bd864c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['labels', 'input_ids', 'token_type_ids', 'attention_mask']" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tokenized_datasets = tokenized_datasets.remove_columns([\"sentence1\", \"sentence2\", \"idx\"])\n", + "tokenized_datasets = tokenized_datasets.rename_column(\"label\", \"labels\")\n", + "tokenized_datasets.set_format(\"torch\")\n", + "tokenized_datasets[\"train\"].column_names" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "78d190b9", + "metadata": {}, + "outputs": [], + "source": [ + "from torch.utils.data import DataLoader\n", + "\n", + "train_dataloader = DataLoader(tokenized_datasets[\"train\"], shuffle=True, batch_size=8, collate_fn=data_collator)\n", + "eval_dataloader = DataLoader(tokenized_datasets[\"validation\"], batch_size=8, collate_fn=data_collator)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d816664d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'labels': torch.Size([8]),\n", + " 'input_ids': torch.Size([8, 77]),\n", + " 'token_type_ids': torch.Size([8, 77]),\n", + " 'attention_mask': torch.Size([8, 77])}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for batch in train_dataloader:\n", + " break\n", + "{k: v.shape for k, v in batch.items()}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7224c789", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f36d4178c1d645779fec77963fa799f8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/199 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mBertForSequenceClassification LOAD REPORT\u001b[0m from: bert-base-uncased\n", + "Key | Status | \n", + "-------------------------------------------+------------+-\n", + "cls.seq_relationship.bias | UNEXPECTED | \n", + "cls.seq_relationship.weight | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.weight | UNEXPECTED | \n", + "cls.predictions.bias | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.bias | UNEXPECTED | \n", + "cls.predictions.transform.dense.weight | UNEXPECTED | \n", + "cls.predictions.transform.dense.bias | UNEXPECTED | \n", + "classifier.weight | MISSING | \n", + "classifier.bias | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + } + ], + "source": [ + "from transformers import AutoModelForSequenceClassification\n", + "\n", + "model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "43e4e86a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(tensor(0.4104, grad_fn=<NllLossBackward0>), torch.Size([8, 2]))" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "outputs = model(**batch)\n", + "outputs.loss, outputs.logits.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "cd7ccfb8", + "metadata": {}, + "outputs": [], + "source": [ + "from torch.optim import AdamW\n", + "\n", + "optimizer = AdamW(model.parameters(), lr=5e-5)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "c31696e3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1377\n" + ] + } + ], + "source": [ + "from transformers import get_scheduler\n", + "\n", + "num_epochs = 3\n", + "num_training_steps = num_epochs * len(train_dataloader)\n", + "lr_scheduler = get_scheduler(\n", + " \"linear\",\n", + " optimizer=optimizer,\n", + " num_warmup_steps=0,\n", + " num_training_steps=num_training_steps,\n", + ")\n", + "print(num_training_steps)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "9256b941", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "device(type='cpu')" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import torch\n", + "\n", + "device = torch.device(\"cuda\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", + "model.to(device)\n", + "device" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ab289b88", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "de12f910b26a46cc8221bc66a859e000", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/1377 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from tqdm.auto import tqdm\n", + "\n", + "progress_bar = tqdm(range(num_training_steps))\n", + "\n", + "model.train()\n", + "for epoch in range(num_epochs):\n", + " for batch in train_dataloader:\n", + " batch = {k: v.to(device) for k, v in batch.items()}\n", + " outputs = model(**batch)\n", + " loss = outputs.loss\n", + " loss.backward()\n", + "\n", + " optimizer.step()\n", + " lr_scheduler.step()\n", + " optimizer.zero_grad()\n", + " progress_bar.update(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b9790a70", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'accuracy': 0.8529411764705882, 'f1': 0.8983050847457628}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import evaluate\n", + "\n", + "metric = evaluate.load(\"glue\", \"mrpc\")\n", + "model.eval()\n", + "for batch in eval_dataloader:\n", + " batch = {k: v.to(device) for k, v in batch.items()}\n", + " with torch.no_grad():\n", + " outputs = model(**batch)\n", + " logits = outputs.logits\n", + " predictions = torch.argmax(logits, dim=-1)\n", + " metric.add_batch(predictions=predictions, references=batch[\"labels\"])\n", + "\n", + "metric.compute()" + ] + }, + { + "cell_type": "markdown", + "id": "5f0c22ad", + "metadata": {}, + "source": [ + "### Accelerate" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "07d86220", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "aaa21b3692954c5d9990b506e29dbaa6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/199 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mBertForSequenceClassification LOAD REPORT\u001b[0m from: bert-base-uncased\n", + "Key | Status | \n", + "-------------------------------------------+------------+-\n", + "cls.seq_relationship.bias | UNEXPECTED | \n", + "cls.seq_relationship.weight | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.weight | UNEXPECTED | \n", + "cls.predictions.bias | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.bias | UNEXPECTED | \n", + "cls.predictions.transform.dense.weight | UNEXPECTED | \n", + "cls.predictions.transform.dense.bias | UNEXPECTED | \n", + "classifier.weight | MISSING | \n", + "classifier.bias | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "91eb760110f44718a8c7f3bff5d2c62b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/1377 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from accelerate import Accelerator\n", + "from torch.optim import AdamW\n", + "from transformers import AutoModelForSequenceClassification, get_scheduler\n", + "\n", + "accelerator = Accelerator()\n", + "\n", + "model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)\n", + "optimizer = AdamW(model.parameters(), lr=3e-5)\n", + "\n", + "train_dl, eval_dl, model, optimizer = accelerator.prepare(\n", + " train_dataloader, eval_dataloader, model, optimizer\n", + ")\n", + "\n", + "num_epochs = 3\n", + "num_training_steps = num_epochs * len(train_dl)\n", + "lr_scheduler = get_scheduler(\n", + " \"linear\",\n", + " optimizer=optimizer,\n", + " num_warmup_steps=0,\n", + " num_training_steps=num_training_steps,\n", + ")\n", + "\n", + "progress_bar = tqdm(range(num_training_steps))\n", + "\n", + "model.train()\n", + "for epoch in range(num_epochs):\n", + " for batch in train_dl:\n", + " outputs = model(**batch)\n", + " loss = outputs.loss\n", + " accelerator.backward(loss)\n", + "\n", + " optimizer.step()\n", + " lr_scheduler.step()\n", + " optimizer.zero_grad()\n", + " progress_bar.update(1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch5/__init__.py b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch5/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch5/load_dataset.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch5/load_dataset.ipynb new file mode 100644 index 0000000..9bbd3f0 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/ch5/load_dataset.ipynb @@ -0,0 +1,1358 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "11843b4f", + "metadata": {}, + "source": [ + "### Loading a local dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a5f7cbf6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--2026-07-21 08:57:52-- https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz\n", + "Resolving github.com (github.com)... 198.18.0.52\n", + "Connecting to github.com (github.com)|198.18.0.52|:443... connected.\n", + "HTTP request sent, awaiting response... 302 Found\n", + "Location: https://raw.githubusercontent.com/crux82/squad-it/master/SQuAD_it-train.json.gz [following]\n", + "--2026-07-21 08:57:57-- https://raw.githubusercontent.com/crux82/squad-it/master/SQuAD_it-train.json.gz\n", + "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n", + "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 7725286 (7.4M) [application/octet-stream]\n", + "Saving to: ‘SQuAD_it-train.json.gz’\n", + "\n", + "SQuAD_it-train.json 100%[===================>] 7.37M 2.94MB/s in 2.5s \n", + "\n", + "2026-07-21 08:58:02 (2.94 MB/s) - ‘SQuAD_it-train.json.gz’ saved [7725286/7725286]\n", + "\n", + "--2026-07-21 08:58:03-- https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz\n", + "Resolving github.com (github.com)... 198.18.0.52\n", + "Connecting to github.com (github.com)|198.18.0.52|:443... connected.\n", + "HTTP request sent, awaiting response... 302 Found\n", + "Location: https://raw.githubusercontent.com/crux82/squad-it/master/SQuAD_it-test.json.gz [following]\n", + "--2026-07-21 08:58:11-- https://raw.githubusercontent.com/crux82/squad-it/master/SQuAD_it-test.json.gz\n", + "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n", + "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 1051245 (1.0M) [application/octet-stream]\n", + "Saving to: ‘SQuAD_it-test.json.gz’\n", + "\n", + "SQuAD_it-test.json. 100%[===================>] 1.00M 499KB/s in 2.1s \n", + "\n", + "2026-07-21 08:58:17 (499 KB/s) - ‘SQuAD_it-test.json.gz’ saved [1051245/1051245]\n", + "\n" + ] + } + ], + "source": [ + "!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz\n", + "!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "52a02ac7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SQuAD_it-test.json.gz:\t 87.4% -- replaced with SQuAD_it-test.json\n", + "SQuAD_it-train.json.gz:\t 82.2% -- replaced with SQuAD_it-train.json\n" + ] + } + ], + "source": [ + "!gzip -dkv SQuAD_it-*.json.gz" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "29629ed2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DatasetDict({\n", + " train: Dataset({\n", + " features: ['title', 'paragraphs'],\n", + " num_rows: 442\n", + " })\n", + "})" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datasets import load_dataset\n", + "\n", + "squad_it_dataset = load_dataset(\"json\", data_files=\"SQuAD_it-train.json\", field=\"data\")\n", + "squad_it_dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9f8a9310", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'title': 'Terremoto del Sichuan del 2008',\n", + " 'paragraphs': [{'context': \"Il terremoto del Sichuan del 2008 o il terremoto del Gran Sichuan, misurato a 8.0 Ms e 7.9 Mw, e si è verificato alle 02:28:01 PM China Standard Time all' epicentro (06:28:01 UTC) il 12 maggio nella provincia del Sichuan, ha ucciso 69.197 persone e lasciato 18.222 dispersi.\",\n", + " 'qas': [{'id': '56cdca7862d2951400fa6826',\n", + " 'answers': [{'text': '2008', 'answer_start': 29}],\n", + " 'question': 'In quale anno si è verificato il terremoto nel Sichuan?'},\n", + " {'id': '56cdca7862d2951400fa6828',\n", + " 'answers': [{'text': '69.197', 'answer_start': 232}],\n", + " 'question': 'Quante persone sono state uccise come risultato?'},\n", + " {'id': '56d4f9902ccc5a1400d833c0',\n", + " 'answers': [{'text': '2008', 'answer_start': 29}],\n", + " 'question': 'Quale anno ha avuto luogo il terremoto del Sichuan?'},\n", + " {'id': '56d4f9902ccc5a1400d833c1',\n", + " 'answers': [{'text': '8.0 Ms e 7.9 Mw', 'answer_start': 78}],\n", + " 'question': 'Che cosa ha fatto la misura di sisma?'},\n", + " {'id': '56d4f9902ccc5a1400d833c2',\n", + " 'answers': [{'text': '12 maggio', 'answer_start': 183}],\n", + " 'question': 'Che giorno si è verificato il terremoto?'},\n", + " {'id': '56d4f9902ccc5a1400d833c3',\n", + " 'answers': [{'text': '02:28:01 PM China Standard Time',\n", + " 'answer_start': 118}],\n", + " 'question': 'Che ora del giorno è accaduto il terremoto?'},\n", + " {'id': '56d4f9902ccc5a1400d833c4',\n", + " 'answers': [{'text': '69.197', 'answer_start': 232}],\n", + " 'question': 'Quante persone sono morte?'}]},\n", + " {'context': 'E\\' noto anche come terremoto di Wenchuan (Cinese:????; pinyin: Wènchu? n dà dìzhèn; letteralmente:\"Grande terremoto di Wenchuan\"), dopo l\\' ubicazione dell\\' epicentro del terremoto, Wenchuan County, Sichuan. L\\' epicentro era di 80 chilometri (50 miglia) ad ovest nord-ovest di Chengdu, capoluogo di provincia, con una profondità focale di 19 km (12 miglia). Il terremoto ha colpito anche i paesi vicini e lontani da Pechino e Shanghai-1.500 km (930 mi) e 1.700 km (1.060 mi) di distanza, dove gli edifici adibiti a uffici hanno oscillato con il tremore. Forti scosse di assestamento, un po\\' superiori alla magnitudo 6, hanno continuato a colpire l\\' area anche mesi dopo il sisma principale, causando nuove vittime e danni.',\n", + " 'qas': [{'id': '56cdcb2c62d2951400fa6830',\n", + " 'answers': [{'text': 'Pechino e Shanghai', 'answer_start': 415}],\n", + " 'question': 'Quali città lontane in altri paesi potrebbero sentire il terremoto?'},\n", + " {'id': '56cdcb2c62d2951400fa6833',\n", + " 'answers': [{'text': '19 km', 'answer_start': 338}],\n", + " 'question': 'Qual è stata la profondità focale del terremoto?'},\n", + " {'id': '56cdcb2c62d2951400fa6834',\n", + " 'answers': [{'text': 'mesi dopo', 'answer_start': 659}],\n", + " 'question': 'Quanto tempo dopo il terremoto si sono sentite le scosse di assestamento?'},\n", + " {'id': '56d4fb6f2ccc5a1400d833d5',\n", + " 'answers': [{'text': 'Wenchuan County, Sichuan', 'answer_start': 181}],\n", + " 'question': \"Dov' era l' epicentro del terremoto?\"},\n", + " {'id': '56d4fb6f2ccc5a1400d833d6',\n", + " 'answers': [{'text': '80 chilometri', 'answer_start': 227}],\n", + " 'question': 'Quanto lontano era da Chengdu?'},\n", + " {'id': '56d4fb6f2ccc5a1400d833d7',\n", + " 'answers': [{'text': '19 km', 'answer_start': 338}],\n", + " 'question': 'Qual era la profondità focale del sisma?'}]},\n", + " {'context': \"I dati ufficiali (al 21 luglio 2008 ore 12:00 CST) hanno dichiarato che 69.197 sono stati confermati morti, di cui 68.636 nella provincia del Sichuan e 374.176 feriti, di cui 18.222 scomparsi. Il terremoto ha lasciato circa 4,8 milioni di senzatetto, anche se il numero potrebbe raggiungere gli 11 milioni. Circa 15 milioni di persone vivevano nell' area colpita. E' stato il terremoto più mortale per colpire la Cina dopo il terremoto di Tangshan 1976, che ha ucciso almeno 240.000 persone, e il più forte del paese dal terremoto di Chayu 1950, che ha registrato a 8,5 sulla scala di magnitudo Richter. E' il 21° terremoto più morto di tutti i tempi. Il 6 novembre 2008, il governo centrale ha annunciato che avrebbe speso 1 trilione di RMB (circa 146,5 miliardi di dollari USA) nei prossimi tre anni per ricostruire le aree devastate dal terremoto, come parte del programma cinese di stimolo economico.\",\n", + " 'qas': [{'id': '56cdcbb762d2951400fa683a',\n", + " 'answers': [{'text': '69.197', 'answer_start': 72}],\n", + " 'question': 'Quante persone sono state confermate morte?'},\n", + " {'id': '56cdcbb762d2951400fa683b',\n", + " 'answers': [{'text': '68.636', 'answer_start': 115}],\n", + " 'question': 'Quanti morti sono stati confermati solo nella provincia del Sichuan?'},\n", + " {'id': '56cdcbb762d2951400fa683c',\n", + " 'answers': [{'text': '4,8 milioni', 'answer_start': 224}],\n", + " 'question': 'Quante persone sono rimaste senza tetto a causa del terremoto?'},\n", + " {'id': '56cdcbb762d2951400fa683d',\n", + " 'answers': [{'text': '15 milioni', 'answer_start': 313}],\n", + " 'question': 'Quante persone hanno vissuto nella zona colpita?'},\n", + " {'id': '56cdcbb762d2951400fa683e',\n", + " 'answers': [{'text': '1 trilione di RMB', 'answer_start': 724}],\n", + " 'question': 'Quanto denaro è stato destinato alla ricostruzione di aree devastate?'},\n", + " {'id': '56d4fca52ccc5a1400d833de',\n", + " 'answers': [{'text': '68.636', 'answer_start': 115}],\n", + " 'question': 'Quante persone sono morte nella provincia del Sichuan?'},\n", + " {'id': '56d4fca52ccc5a1400d833df',\n", + " 'answers': [{'text': '374.176', 'answer_start': 152}],\n", + " 'question': 'Quanti sono stati feriti nel Sichuan?'},\n", + " {'id': '56d4fca52ccc5a1400d833e0',\n", + " 'answers': [{'text': '18.222', 'answer_start': 175}],\n", + " 'question': 'Quante persone sono elencate come scomparse?'},\n", + " {'id': '56d4fca52ccc5a1400d833e1',\n", + " 'answers': [{'text': '4,8 milioni', 'answer_start': 224}],\n", + " 'question': 'Quante persone sono senzatetto a causa del terremoto?'},\n", + " {'id': '56d4fca52ccc5a1400d833e2',\n", + " 'answers': [{'text': '11 milioni', 'answer_start': 295}],\n", + " 'question': 'Quanto potrebbe arrivare il numero dei senzatetto?'}]},\n", + " {'context': \"Il terremoto ha avuto una magnitudine di 8,0 Ms e 7,9 Mw. L' epicentro è stato in Wenchuan County, Ngawa Tibetano e Prefettura Autonoma di Qiang, 80 km ad ovest / nord-ovest del capoluogo di provincia di Chengdu, con il suo tremore principale che si verifica alle 14:28:01.42 China Standard Time (06:28:01.42 UTC), il 12 maggio 2008 della durata di circa 2 minuti, nel terremoto quasi l' 80% degli edifici sono stati distrutti.\",\n", + " 'qas': [{'id': '56cdcc5562d2951400fa6847',\n", + " 'answers': [{'text': '2 minuti', 'answer_start': 355}],\n", + " 'question': 'Quanto tempo è durato il terremoto?'},\n", + " {'id': '56cdcc5562d2951400fa6848',\n", + " 'answers': [{'text': '80%', 'answer_start': 388}],\n", + " 'question': 'Quale percentuale di edifici è stata distrutta?'},\n", + " {'id': '56d4fe332ccc5a1400d833e8',\n", + " 'answers': [{'text': '8,0 Ms e 7,9 Mw', 'answer_start': 41}],\n", + " 'question': 'Qual era la magnitudo del terremoto?'},\n", + " {'id': '56d4fe332ccc5a1400d833eb',\n", + " 'answers': [{'text': '2 minuti', 'answer_start': 355}],\n", + " 'question': 'Quanto tempo è durato il tremore principale?'},\n", + " {'id': '56d4fe332ccc5a1400d833ec',\n", + " 'answers': [{'text': \"quasi l' 80%\", 'answer_start': 379}],\n", + " 'question': 'Quale percentuale di edifici è stata distrutta durante il sisma?'}]},\n", + " {'context': \"Secondo uno studio della China Earthquake Administration (CEA), il terremoto si è verificato lungo la faglia Longmenshan, una struttura di spinta lungo il confine della piastra Indo-Australiana e placca eurasiatica. Le attività sismiche si sono concentrate sulla sua frattura media (nota come frattura Yingxiu-Beichuan). La rottura è durata quasi 120 sec, con la maggior parte dell' energia rilasciata nei primi 80 sec. Partendo da Wenchuan, la rottura si propagò ad una velocità media di 3,1 chilometri al secondo 49° verso nord-est, rompendosi per un totale di circa 300 km. Lo spostamento massimo ammontava a 9 metri. Il fuoco era più profondo di 10 km.\",\n", + " 'qas': [{'id': '56cdccd962d2951400fa6850',\n", + " 'answers': [{'text': '120 sec', 'answer_start': 347}],\n", + " 'question': 'Quanto tempo ha durato la rottura?'},\n", + " {'id': '56cdccd962d2951400fa6852',\n", + " 'answers': [{'text': '10 km', 'answer_start': 650}],\n", + " 'question': 'Quanto è stato profondo il centro del terremoto?'},\n", + " {'id': '56d5005d2ccc5a1400d833f4',\n", + " 'answers': [{'text': 'frattura Yingxiu-Beichuan', 'answer_start': 293}],\n", + " 'question': 'Su quale frattura ha messo a fuoco il terremoto?'},\n", + " {'id': '56d5005d2ccc5a1400d833f6',\n", + " 'answers': [{'text': '9 metri', 'answer_start': 612}],\n", + " 'question': 'Qual è stato lo sfollamento più causato dal terremoto?'}]},\n", + " {'context': 'Yazhou Zhoukan, con sede in Malesia, ha condotto un\\' intervista con l\\' ex ricercatore presso il China Seismological Bureau Geng Qingguo (??), in cui Geng ha sostenuto che un rapporto scritto riservato è stato inviato all\\' Ufficio sismologico di Stato il 30 aprile 2008, avvertendo circa il possibile verificarsi di un terremoto significativo nella regione della prefettura di Ngawa del Sichuan intorno all\\' 8 maggio, con una gamma di 10 giorni prima o dopo il terremoto. Il Geng, pur riconoscendo che la predizione del terremoto era ampiamente considerata problematica dalla comunità scientifica, credeva che \"più grande è il terremoto, più facile è prevedere\". Geng aveva a lungo tentato di stabilire una correlazione tra il verificarsi di siccità e terremoti; Premier Zhou Enlai ha riferito che ha preso interesse nel lavoro di Geng. La teoria di correlazione siccità-terra terremoto di Geng è stata pubblicata per la prima volta nel 1972, e ha detto di aver pronosticato con successo i terremoti di Haicheng 1975 e 1976 Tangshan. Lo stesso articolo di Yazhou Zhoukan ha sottolineato le difficoltà intrinseche associate alla previsione dei terremoti.',\n", + " 'qas': [{'id': '56cdce0b62d2951400fa685b',\n", + " 'answers': [{'text': '1972', 'answer_start': 936}],\n", + " 'question': 'Quando è stata rilasciata la teoria di correlazione siccità-terra terremoto di Geng?'},\n", + " {'id': '56d67a8d1c8504140094714f',\n", + " 'answers': [{'text': \"intorno all' 8 maggio\", 'answer_start': 394}],\n", + " 'question': 'Qual era il tempo previsto per il terremoto?'},\n", + " {'id': '56d67a8d1c85041400947150',\n", + " 'answers': [{'text': 'siccità', 'answer_start': 741}],\n", + " 'question': 'Che cosa ha cercato Geng a lungo di stabilire come rapporto con i terremoti?'}]},\n", + " {'context': 'In uno studio dell\\' USGS (United States Geological Survey) i modelli preliminari di rottura del terremoto hanno indicato uno spostamento fino a 9 metri lungo un guasto lungo circa 240 km di lunghezza e 20 km di profondità. Il terremoto ha generato deformazioni della superficie superiori a 3 metri e ha aumentato lo stress (e la probabilità che si verifichino eventi futuri) alle estremità nord-orientale e sud-occidentale del guasto. Il 20 maggio, il sismologo dell\\' USGS Tom Parsons ha avvertito che c\\' è \"alto rischio\" di una grave scossa di assestamento M>7 nelle prossime settimane o mesi.',\n", + " 'qas': [{'id': '56cdcebe62d2951400fa6862',\n", + " 'answers': [{'text': 'fino a 9 metri', 'answer_start': 137}],\n", + " 'question': 'Quanto è stato grande lo spostamento?'},\n", + " {'id': '56cdcebe62d2951400fa6866',\n", + " 'answers': [{'text': 'Tom Parsons', 'answer_start': 473}],\n", + " 'question': \"Chi ha prima messo in guardia da possibili attività sismiche nell' area?\"},\n", + " {'id': '56d5027a9d1b871400ae05e1',\n", + " 'answers': [{'text': '9 metri', 'answer_start': 144}],\n", + " 'question': \"Che cosa ha mostrato il Geological Survey degli Stati Uniti come l' entità dello spostamento?\"},\n", + " {'id': '56d5027a9d1b871400ae05e3',\n", + " 'answers': [{'text': '20 km di profondità', 'answer_start': 202}],\n", + " 'question': 'Quanto è profondo il guasto?'},\n", + " {'id': '56d5027a9d1b871400ae05e5',\n", + " 'answers': [{'text': 'alto rischio', 'answer_start': 508}],\n", + " 'question': 'Che cosa ha considerato Tom Parsons come il fattore di rischio per forti terremoti futuri?'}]},\n", + " {'context': 'Sismologo giapponese Yuji Yagi Yuji Yagi presso l\\' Università di Tsukuba ha detto che il terremoto si è verificato in due fasi:\"Il 155 miglia Longmenshan Fault tore in due sezioni, il primo uno strappo di circa sette metri, seguita da un secondo che ha tosato quattro metri\". I suoi dati hanno anche mostrato che il terremoto è durato circa due minuti e ha rilasciato 30 volte l\\' energia del grande terremoto di Hanshin del 1995 in Giappone, che ha ucciso oltre 6.000 persone. Egli ha sottolineato che la scarsità dell\\' epicentro e la densità di popolazione hanno notevolmente aumentato la gravità del terremoto. Teruyuki Kato, un sismologo dell\\' Università di Tokyo, ha detto che le onde sismiche del terremoto hanno percorso una lunga distanza senza perdere il loro potere a causa della fermezza del terreno nella Cina centrale.',\n", + " 'qas': [{'id': '56d504539d1b871400ae05eb',\n", + " 'answers': [{'text': 'in due fasi', 'answer_start': 115}],\n", + " 'question': 'Come ha fatto Yuji Yagi a dire che il terremoto è successo?'},\n", + " {'id': '56d504539d1b871400ae05ec',\n", + " 'answers': [{'text': 'Longmenshan Fault', 'answer_start': 142}],\n", + " 'question': 'Che errore si è verificato in due luoghi?'},\n", + " {'id': '56d504539d1b871400ae05ee',\n", + " 'answers': [{'text': '30 volte', 'answer_start': 368}],\n", + " 'question': 'Quanta più energia di quella prodotta dal terremoto del 1995 in Giappone?'},\n", + " {'id': '56d504539d1b871400ae05ef',\n", + " 'answers': [{'text': 'fermezza del terreno', 'answer_start': 789}],\n", + " 'question': 'Perché le onde sismiche hanno viaggiato finora?'}]},\n", + " {'context': 'Tra 64 e 104 scosse di assestamento importanti, di magnitudo compresa tra 4,0 e 6,1, sono state registrate entro 72 ore dal sisma principale. Secondo i conteggi ufficiali cinesi,\"alle ore 12:00 CST, 6 novembre 2008 c\\' erano state 42.719 scariche post-shock totali, di cui 246 variavano da 4,0 SM a 4,9 SM, 34 da 5,0 MS a 5,9 MS, e 8 da 6,0 Ms a 6,4 MS; l\\' urto post-shock più forte misurato 6,4 MS.\" L\\' ultima scossa post-shock superiore a M6 si è verificato il 5 agosto.',\n", + " 'qas': [{'id': '56cdd08862d2951400fa6894',\n", + " 'answers': [{'text': 'Tra 64 e 104', 'answer_start': 0}],\n", + " 'question': 'Quante sono state le scosse di assestamento?'},\n", + " {'id': '56cdd08862d2951400fa6896',\n", + " 'answers': [{'text': 'entro 72 ore dal sisma principale',\n", + " 'answer_start': 107}],\n", + " 'question': 'Quando sono state registrate le scosse di assestamento?'},\n", + " {'id': '56d505f99d1b871400ae05f5',\n", + " 'answers': [{'text': 'Tra 64 e 104', 'answer_start': 0}],\n", + " 'question': 'Quante sono state le scosse di assestamento entro 72 ore?'},\n", + " {'id': '56d505f99d1b871400ae05f6',\n", + " 'answers': [{'text': '42.719', 'answer_start': 230}],\n", + " 'question': 'Che cosa dicono i cinesi è il numero totale di shock dopo il terremoto?'},\n", + " {'id': '56d505f99d1b871400ae05f8',\n", + " 'answers': [{'text': '246', 'answer_start': 272}],\n", + " 'question': 'In quale data si sono verificate le scosse di assestamento più recenti superiori a 6 SM?'}]},\n", + " {'context': '(Il terremoto della sig. ra 6.1 del 30 agosto 2008 nel Sichuan meridionale non faceva parte di questa serie perché è stato causato da una diversa colpa.',\n", + " 'qas': [{'id': '56cdd10962d2951400fa68a0',\n", + " 'answers': [{'text': 'Sichuan meridionale', 'answer_start': 55}],\n", + " 'question': 'Dove si è verificato il terremoto?'},\n", + " {'id': '56cdd10962d2951400fa68a2',\n", + " 'answers': [{'text': 'Sichuan meridionale', 'answer_start': 55}],\n", + " 'question': \"Dov' era il terremoto del 30 agosto 2008?\"},\n", + " {'id': '56d507269d1b871400ae05ff',\n", + " 'answers': [{'text': 'sig', 'answer_start': 20}],\n", + " 'question': 'Qual era la grandezza del terremoto del Sichuan meridionale?'}]},\n", + " {'context': 'La mappa dell\\' intensità sismica pubblicata dal CEA dopo aver rilevato 500.000 km2 dell\\' area colpita mostra una liedu massima di XI sulla scala di intensità sismica cinese (CSIS), descritta come \"molto distruttiva\" sulla scala macrosismica europea (EMS) da cui il CSIS ha tratto riferimento. (USGS, utilizzando la scala di intensità Mercalli modificata (CC), collocava anche l\\' intensità massima a XI,\"molto disastrosa\". Due strisce sud-ovest-nord-est di liedu XI sono centrate intorno a Yingxiu, Wenchuan (la città più vicina all\\' epicentro del terremoto principale) e Beichuan (la città ripetutamente colpita da forti scosse di assestamento tra cui una che registra MS 6.1 il 1 agosto 2008), entrambe nella provincia del Sichuan, occupando un totale di 2.419 km2. La zona Yingxiu liedu-XI è lunga circa 66 km e larga 20 km lungo Wenchuan-Dujiangyan-Pengzhou; la zona Beichuan liedu-XI è lunga circa 82 km e larga 15 km lungo An County-Beichuan-Pingwu. L\\' area con liedu X (comparabile a X su EMS,\"distruttivo\" e X su MM,\"disastroso\") ha una superficie di 3.144 km2.',\n", + " 'qas': [{'id': '56cdd21562d2951400fa68b0',\n", + " 'answers': [{'text': 'CEA', 'answer_start': 48}],\n", + " 'question': \"Chi ha pubblicato la mappa dell' intensità dei terremoti?\"},\n", + " {'id': '56cdd21562d2951400fa68b2',\n", + " 'answers': [{'text': 'XI', 'answer_start': 130}],\n", + " 'question': 'A quale intensità è stata scalata?'},\n", + " {'id': '56d5098a9d1b871400ae0609',\n", + " 'answers': [{'text': 'Yingxiu, Wenchuan', 'answer_start': 489}],\n", + " 'question': \"Quale città era più vicina all' epicentro principale?\"}]},\n", + " {'context': \"Il sistema Longmen Shan Fault System si trova al confine orientale dell' altopiano tibetano e contiene diversi difetti. Questo terremoto ha rotto almeno due strutture imbricate in Longmen Shan Fault System, vale a dire il Fault Beichuan e il Guanxian-Anxian Fault. Nell' area epicentrale, lo slittamento medio in Fault Beichuan era di circa 3,5 metri (11 ft) verticale, di 3,5 metri (11 ft) orizzontale parallelo al guasto, e di 4,8 metri (16 ft) orizzontale-pendicolare al guasto. Nell' area di circa 30 chilometri (19 miglia) a nord-est dell' epicentro, lo scivolamento superficiale su Beichuan Fault è stato quasi esclusivamente dextral strike-slip fino a circa 3 metri, mentre lo scivolamento medio in Guanxian-Anxian Fault è stato di circa 2 metri verticali (6 ft 7 in) e 2,3 metri (7 ft 7 in) orizzontali. il confine orientale dell' altopiano tibetano.\",\n", + " 'qas': [{'id': '56cdd4d762d2951400fa68cd',\n", + " 'answers': [{'text': '3 metri', 'answer_start': 665}],\n", + " 'question': 'Quanto è stato grande lo slip 30 km a nord-est del guasto?'},\n", + " {'id': '56cdd4d762d2951400fa68ce',\n", + " 'answers': [{'text': '2 metri', 'answer_start': 745}],\n", + " 'question': 'Qual è stato lo slittamento verticale media sul Guanxian-Anxian errore?'},\n", + " {'id': '56cdd4d762d2951400fa68cf',\n", + " 'answers': [{'text': '2,3 metri', 'answer_start': 777}],\n", + " 'question': 'Che cosa è stato lo slittamento orizzontale media sul Guanxian-Anxian errore?'},\n", + " {'id': '56cdd4d762d2951400fa68d0',\n", + " 'answers': [{'text': 'altopiano tibetano', 'answer_start': 73}],\n", + " 'question': 'Dove si trova il guasto dello shan Longmen?'},\n", + " {'id': '56d516439d1b871400ae0611',\n", + " 'answers': [{'text': 'sistema Longmen Shan Fault System',\n", + " 'answer_start': 3}],\n", + " 'question': 'Dove sono i guasti Beichuan e Guanxian-Ansia?'},\n", + " {'id': '56d516439d1b871400ae0612',\n", + " 'answers': [{'text': '3,5 metri', 'answer_start': 341}],\n", + " 'question': 'Qual è stato lo slittamento verticale medio nel difetto di Beichaun?'},\n", + " {'id': '56d516439d1b871400ae0613',\n", + " 'answers': [{'text': '3,5 metri', 'answer_start': 341}],\n", + " 'question': \"Qual è stato lo slittamento orizzontale nel Fault Beichuan all' epicentro?\"},\n", + " {'id': '56d516439d1b871400ae0614',\n", + " 'answers': [{'text': '4,8 metri', 'answer_start': 429}],\n", + " 'question': 'Qual è stato lo slittamento orizzontale perpendicolare al guasto?'}]},\n", + " {'context': 'Gli edifici per uffici nel distretto finanziario di Shanghai, tra cui la Jin Mao Tower e la Hong Kong New World Tower, sono stati evacuati. Un receptionist al Tibet Hotel di Chengdu ha detto che le cose erano \"calme\" dopo che l\\' hotel ha evacuato i suoi ospiti. Nel frattempo, i lavoratori di uno stabilimento Ford nel Sichuan sono stati evacuati per circa 10 minuti. L\\' aeroporto internazionale di Chengdu Shuangliu è stato chiuso e la torre di controllo e il controllo radar regionale sono stati evacuati. Un volo di SilkAir è stato deviato e atterrato a Kunming. Cathay Pacific ha ritardato entrambe le tratte del suo quadruplo quotidiano Hong Kong a Londra a causa di questa perturbazione dei servizi di traffico aereo. Chengdu Shuangliu Aeroporto riaperto più tardi la sera del 12 maggio, offrendo un servizio limitato come l\\' aeroporto ha cominciato ad essere utilizzato come una zona di sosta per le operazioni di soccorso.',\n", + " 'qas': [{'id': '56cdd63a62d2951400fa68d7',\n", + " 'answers': [{'text': 'calm', 'answer_start': 210}],\n", + " 'question': \"Come ha descritto un receptionist l' atmosfera dopo l' evacuazione?\"},\n", + " {'id': '56cdd63a62d2951400fa68d8',\n", + " 'answers': [{'text': '10 minuti', 'answer_start': 357}],\n", + " 'question': 'Per quanto tempo sono stati evacuati i lavoratori di Ford Plant?'},\n", + " {'id': '56cdd63a62d2951400fa68d9',\n", + " 'answers': [{'text': 'aeroporto internazionale di Chengdu Shuangliu',\n", + " 'answer_start': 371}],\n", + " 'question': 'Quale aeroporto è stato chiuso?'},\n", + " {'id': '56cdd63a62d2951400fa68da',\n", + " 'answers': [{'text': '12 maggio', 'answer_start': 783}],\n", + " 'question': \"Quando è stato riaperto l' aeroporto?\"},\n", + " {'id': '56d5185a9d1b871400ae061d',\n", + " 'answers': [{'text': 'operazioni di soccorso', 'answer_start': 907}],\n", + " 'question': \"A cosa servivano l' aeroporto per fare tappa?\"}]},\n", + " {'context': \"Reporter a Chengdu hanno detto che hanno visto crepe sulle pareti di alcuni edifici residenziali nelle zone del centro, ma nessun edificio è crollato. Molti uffici di Pechino torri ufficio sono stati evacuati, tra cui l' edificio che ospita gli uffici dei media per gli organizzatori delle Olimpiadi estive 2008. Nessuna delle sedi olimpiche sono state danneggiate. Nel frattempo, un treno cargo che trasportava 13 cisterne a benzina deragliato nella contea di Hui, Gansu, e prese fuoco dopo che la ferrovia era stata distorta.\",\n", + " 'qas': [{'id': '56cddec762d2951400fa692c',\n", + " 'answers': [{'text': 'a Chengdu', 'answer_start': 9}],\n", + " 'question': 'Dove hanno detto i giornalisti che hanno visto crepe sulle pareti di alcuni edifici?'},\n", + " {'id': '56cddec762d2951400fa692d',\n", + " 'answers': [{'text': 'Pechino', 'answer_start': 167}],\n", + " 'question': 'Dove sono state evacuate le torri degli uffici?'},\n", + " {'id': '56d519a82593cc1400307a6b',\n", + " 'answers': [{'text': 'crepe sulle pareti', 'answer_start': 47}],\n", + " 'question': 'Cosa è stato riportato in Chengdu?'}]},\n", + " {'context': \"Tutte le autostrade in Wenchuan, e altri in tutta la provincia, sono stati danneggiati, con conseguente ritardo di arrivo delle truppe di soccorso. Nella contea di Beichuan, l' 80% degli edifici è crollato secondo Xinhua News. Nella città di Shifang, il crollo di due impianti chimici ha portato alla fuoriuscita di circa 80 tonnellate di ammoniaca liquida, con centinaia di persone segnalate sepolte. Nella città di Dujiangyan, a sud-est dell' epicentro, un' intera scuola è crollata con 900 studenti sepolti e meno di 60 sopravvissuti. La Juyuan Middle School, dove sono stati sepolti molti adolescenti, è stata scavata da civili e gru. Dujiangyan è la sede del Dujiangyan Irrigation System, un antico progetto di deviazione dell' acqua che è ancora in uso ed è un patrimonio mondiale dell' UNESCO. La famosa bocca di pesce del progetto è stata incrinata, ma non gravemente danneggiata altrimenti.\",\n", + " 'qas': [{'id': '56cddf9e62d2951400fa6934',\n", + " 'answers': [{'text': 'Tutte le autostrade', 'answer_start': 0}],\n", + " 'question': 'Quante autostrade che conducono a Wenchuan sono state danneggiate?'},\n", + " {'id': '56cddf9e62d2951400fa6936',\n", + " 'answers': [{'text': '80%', 'answer_start': 177}],\n", + " 'question': \"Quale percentuale dell' edificio è crollata a Beichuan?\"},\n", + " {'id': '56cddf9e62d2951400fa6937',\n", + " 'answers': [{'text': 'Dujiangyan', 'answer_start': 417}],\n", + " 'question': 'Dove sono crollati due impianti chimici?'},\n", + " {'id': '56cddf9e62d2951400fa6938',\n", + " 'answers': [{'text': '60', 'answer_start': 520}],\n", + " 'question': 'Quanti studenti su 900 nella scuola sono sopravvissuti al crollo?'},\n", + " {'id': '56d51b832593cc1400307a75',\n", + " 'answers': [{'text': 'Tutte le autostrade', 'answer_start': 0}],\n", + " 'question': 'Quali autostrade di Wenchuan sono state danneggiate?'},\n", + " {'id': '56d51b832593cc1400307a77',\n", + " 'answers': [{'text': '80%', 'answer_start': 177}],\n", + " 'question': 'Quanti edifici di Beichuan sono crollati?'},\n", + " {'id': '56d51b832593cc1400307a78',\n", + " 'answers': [{'text': 'due impianti chimici', 'answer_start': 264}],\n", + " 'question': 'Quale ammoniaca liquida fuoriuscita a Shifang?'},\n", + " {'id': '56d51b832593cc1400307a79',\n", + " 'answers': [{'text': 'meno di 60', 'answer_start': 512}],\n", + " 'question': 'Quanti dei 900 studenti sepolti in un crollo scolastico Dujiangyan sono sopravvissuti?'}]},\n", + " {'context': 'Sia la borsa valori di Shanghai che la Shenzhen Stock Exchange hanno sospeso il commercio di società con sede nella Cina sudoccidentale. Il rame è aumentato a causa delle speculazioni che la produzione nel sud-ovest della Cina potrebbe essere influenzata, e i prezzi del petrolio è sceso a fronte di speculazioni che la domanda dalla Cina sarebbe scesa.',\n", + " 'qas': [{'id': '56cddfff62d2951400fa693f',\n", + " 'answers': [{'text': 'Cina sudoccidentale', 'answer_start': 116}],\n", + " 'question': 'Dove si sono basati gli scambi?'},\n", + " {'id': '56cddfff62d2951400fa6940',\n", + " 'answers': [{'text': 'rame', 'answer_start': 140}],\n", + " 'question': 'Quale metallo è aumentato di valore?'},\n", + " {'id': '56cddfff62d2951400fa6941',\n", + " 'answers': [{'text': 'olio', 'answer_start': 275}],\n", + " 'question': 'Quale risorsa naturale è diminuita di valore?'},\n", + " {'id': '56d51d272593cc1400307a80',\n", + " 'answers': [{'text': 'Cina sudoccidentale', 'answer_start': 116}],\n", + " 'question': 'Dove si trovavano le società che avevano sospeso il loro commercio di azioni?'},\n", + " {'id': '56d51d272593cc1400307a81',\n", + " 'answers': [{'text': 'rame', 'answer_start': 140}],\n", + " 'question': 'Quale metallo è aumentato a causa della speculazione?'},\n", + " {'id': '56d51d272593cc1400307a83',\n", + " 'answers': [{'text': 'Shenzhen Stock Exchange', 'answer_start': 39}],\n", + " 'question': 'Oltre alla borsa valori di Shanghai, quale altra borsa sospesa negoziazione di titoli azionari sud-ovest della Cina?'}]},\n", + " {'context': \"Immediatamente dopo l' evento sismico, le telecomunicazioni mobili e terrestri sono state tagliate verso l' area colpita e quella circostante, con tutte le capacità internet tagliate anche verso l' area del Sichuan. Elementi di telecomunicazioni sono stati ripristinati dal governo pezzo per pezzo nel corso dei prossimi mesi come la situazione nella provincia del Sichuan gradualmente migliorata. Alla fine, una manciata di importanti siti web di notizie e media sono stati resi accessibili online nella regione, anche se con pagine web drammaticamente ridotte.\",\n", + " 'qas': [{'id': '56cde07662d2951400fa6947',\n", + " 'answers': [{'text': 'internet', 'answer_start': 165}],\n", + " 'question': \"Quali capacità sono state tagliate all' intera area del Sichuan?\"},\n", + " {'id': '56cde07662d2951400fa6948',\n", + " 'answers': [{'text': 'mesi', 'answer_start': 321}],\n", + " 'question': 'Quanto tempo ci è voluto per ripristinare queste capacità?'},\n", + " {'id': '56d51ea52593cc1400307a89',\n", + " 'answers': [{'text': 'telecomunicazioni', 'answer_start': 42}],\n", + " 'question': 'Cosa è stato tagliato dopo il terremoto?'},\n", + " {'id': '56d51ea52593cc1400307a8c',\n", + " 'answers': [{'text': 'siti web di notizie e media', 'answer_start': 436}],\n", + " 'question': \"Quali servizi internet sono diminuiti nell' area?\"}]},\n", + " {'context': \"China Mobile ha avuto più di 2.300 stazioni base sospese a causa di interruzione dell' alimentazione o grave congestione del traffico delle telecomunicazioni. Metà delle comunicazioni wireless sono andate perse nella provincia del Sichuan. Il servizio di China Unicom a Wenchuan e in quattro contee vicine è stato interrotto, con più di 700 torri sospese.\",\n", + " 'qas': [{'id': '56cde11f62d2951400fa694c',\n", + " 'answers': [{'text': '2.300', 'answer_start': 29}],\n", + " 'question': 'Quante stazioni base sono state sospese?'},\n", + " {'id': '56d5202a2593cc1400307a93',\n", + " 'answers': [{'text': '2.300', 'answer_start': 29}],\n", + " 'question': 'Quante stazioni base China Mobile hanno smesso di funzionare?'},\n", + " {'id': '56d5202a2593cc1400307a94',\n", + " 'answers': [{'text': 'congestione del traffico', 'answer_start': 109}],\n", + " 'question': \"Oltre all' interruzione dell' alimentazione, cosa ha causato la sospensione delle telecomunicazioni?\"},\n", + " {'id': '56d5202a2593cc1400307a95',\n", + " 'answers': [{'text': 'Metà', 'answer_start': 159}],\n", + " 'question': 'Quante comunicazioni wireless sono fallite nel Sichuan?'},\n", + " {'id': '56d5202a2593cc1400307a96',\n", + " 'answers': [{'text': 'China Unicom', 'answer_start': 255}],\n", + " 'question': 'Chi servizio a Wenchuan è stato interrotto?'},\n", + " {'id': '56d5202a2593cc1400307a97',\n", + " 'answers': [{'text': '700', 'answer_start': 337}],\n", + " 'question': 'Quante torri di China Unicom sono state tagliate?'}]},\n", + " {'context': \"Inizialmente, i funzionari non erano in grado di contattare la Wolong National Nature Reserve, sede di circa 280 panda giganti. Tuttavia, il Ministero degli Esteri in seguito ha detto che un gruppo di 31 turisti britannici che visitano la riserva Wolong Panda nella zona colpita dal terremoto ha restituito sicuro e indenne a Chengdu. Tuttavia, il benessere di un numero ancora maggiore di panda nelle vicine riserve del panda è rimasto sconosciuto. Cinque guardie di sicurezza della riserva sono state uccise dal terremoto. Sei panda sono fuggiti dopo che i loro recinti sono stati danneggiati. Entro il 20 maggio, due panda presso la riserva sono stati trovati per essere feriti, mentre la ricerca è continuata per altri due panda adulti che è andato perduto dopo il terremoto. Al 28 maggio 2008 mancava ancora un panda. Il panda mancante è stato poi trovato morto sotto le macerie di un recinto. Mao Mao Mao Mao, nove anni, madre di cinque anni nel centro di allevamento, è stata scoperta lunedì, il suo corpo schiacciato da un muro nel suo recinto. I detentori di panda e altri lavoratori hanno messo i suoi resti in una piccola cassa di legno e l' hanno seppellita fuori dal centro di allevamento.\",\n", + " 'qas': [{'id': '56cde1f462d2951400fa695f',\n", + " 'answers': [{'text': 'circa 280', 'answer_start': 103}],\n", + " 'question': 'Quanti panda vivono nella riserva?'},\n", + " {'id': '56cde1f462d2951400fa6960',\n", + " 'answers': [{'text': '31', 'answer_start': 201}],\n", + " 'question': 'Quanti visitatori britannici della Riserva hanno lasciato illeso?'},\n", + " {'id': '56cde1f462d2951400fa6961',\n", + " 'answers': [{'text': '2', 'answer_start': 109}],\n", + " 'question': 'Quanti panda sono stati feriti?'},\n", + " {'id': '56d521ee2593cc1400307a9d',\n", + " 'answers': [{'text': 'Wolong National Nature Reserve',\n", + " 'answer_start': 63}],\n", + " 'question': 'Quale centro naturalistico è stato tagliato?'},\n", + " {'id': '56d521ee2593cc1400307a9f',\n", + " 'answers': [{'text': 'Sei', 'answer_start': 525}],\n", + " 'question': 'Quanti panda sono fuggiti dalla riserva?'},\n", + " {'id': '56d521ee2593cc1400307aa0',\n", + " 'answers': [{'text': 'cinque', 'answer_start': 450}],\n", + " 'question': 'Quante guardie di sicurezza sono morte nella riserva?'},\n", + " {'id': '56d521ee2593cc1400307aa1',\n", + " 'answers': [{'text': 'Mao Mao Mao', 'answer_start': 899}],\n", + " 'question': 'Quale famoso panda è stato ucciso sotto le macerie?'}]},\n", + " {'context': \"La centrale idroelettrica Zipingpu (semplificata cinese:????; tradizionale cinese:??????) situata a 20 km ad est dell' epicentro è stata danneggiata. Da una recente ispezione è emerso che il danno era meno grave di quanto inizialmente temuto e resta strutturalmente stabile e sicuro. Il serbatoio di Tulong a monte rischia di crollare. Circa 2.000 soldati sono stati assegnati a Zipingpu, cercando di liberare la pressione attraverso lo sfioratore. In totale sono state segnalate 391 dighe, per la maggior parte di piccole dimensioni, danneggiate dal sisma.\",\n", + " 'qas': [{'id': '56cde29b62d2951400fa696b',\n", + " 'answers': [{'text': '2.000', 'answer_start': 342}],\n", + " 'question': 'Quante truppe sono state assegnate a Zipingpu?'},\n", + " {'id': '56cde29b62d2951400fa696c',\n", + " 'answers': [{'text': '391', 'answer_start': 480}],\n", + " 'question': 'Quante dighe sono state danneggiate?'},\n", + " {'id': '56d523bd2593cc1400307aa8',\n", + " 'answers': [{'text': '20 km', 'answer_start': 100}],\n", + " 'question': \"Quanto era vicina all' epicentro la centrale elettrica?\"},\n", + " {'id': '56d523bd2593cc1400307aa9',\n", + " 'answers': [{'text': 'meno grave', 'answer_start': 201}],\n", + " 'question': 'Che cosa è emerso da una recente ispezione sui danni alla centrale?'},\n", + " {'id': '56d523bd2593cc1400307aab',\n", + " 'answers': [{'text': '391', 'answer_start': 480}],\n", + " 'question': 'Qual è il numero totale di dighe danneggiate?'}]},\n", + " {'context': \"Secondo i funzionari di Stato cinesi, il terremoto ha causato 69.180 morti conosciute tra cui 68.636 nella provincia del Sichuan; 18.498 persone sono elencate come scomparse, e 374.176 feriti, ma queste cifre possono ulteriormente aumentare con l' arrivo di più rapporti.\",\n", + " 'qas': [{'id': '56cde34662d2951400fa6973',\n", + " 'answers': [{'text': '68.636', 'answer_start': 94}],\n", + " 'question': 'Quanti decessi sono stati segnalati solo nella provincia del Sichuan?'},\n", + " {'id': '56cde34662d2951400fa6974',\n", + " 'answers': [{'text': '18.498', 'answer_start': 130}],\n", + " 'question': \"Quante persone sono state inserite nell' elenco come scomparse?\"},\n", + " {'id': '56cde34662d2951400fa6975',\n", + " 'answers': [{'text': '374.176', 'answer_start': 177}],\n", + " 'question': 'Quante persone sono rimaste ferite?'},\n", + " {'id': '56d525192593cc1400307ab1',\n", + " 'answers': [{'text': '68.636', 'answer_start': 94}],\n", + " 'question': 'Quanti morti nel Sichuan?'},\n", + " {'id': '56d525192593cc1400307ab2',\n", + " 'answers': [{'text': '69.180', 'answer_start': 62}],\n", + " 'question': 'Qual è il conteggio totale dei morti noti causati dal terremoto?'},\n", + " {'id': '56d525192593cc1400307ab3',\n", + " 'answers': [{'text': '18.498', 'answer_start': 130}],\n", + " 'question': 'Qual è il numero di persone scomparse?'},\n", + " {'id': '56d525192593cc1400307ab4',\n", + " 'answers': [{'text': '374.176', 'answer_start': 177}],\n", + " 'question': 'Quante persone sono rimaste ferite?'}]},\n", + " {'context': \"Una squadra di soccorso ha riportato solo 2.300 sopravvissuti della città di Yingxiu nella contea di Wenchuan, su una popolazione totale di circa 9.000 abitanti. Da 3.000 a 5.000 persone sono state uccise nella sola contea di Beichuan, nel Sichuan; nello stesso luogo, 10.000 persone sono rimaste ferite e l' 80% degli edifici sono stati distrutti. L' antica sede della contea di Beichuan è stata abbandonata e conservata come parte del Beichuan Earthquake Museum. Otto scuole sono state abbattute nel Dujiangyan. Un cinquantaseienne è stato ucciso a Dujiangyan durante un tentativo di salvataggio sulla Lingyanshan Ropeway, dove a causa del terremoto 11 turisti taiwanesi erano rimasti intrappolati all' interno di funivie dal 13 maggio. Un ragazzo di 4 anni di nome Zhu Shaowei (cinese tradizionale:???; cinese semplificato:???; pinyin: Zh? Shàowéi) è stato ucciso anche a Mianzhu City quando una casa è crollata su di lui e un altro è stato segnalato perduto.\",\n", + " 'qas': [{'id': '56ceb9c4aab44d1400b8892b',\n", + " 'answers': [{'text': '2.300', 'answer_start': 42}],\n", + " 'question': 'Quanti sopravvissuti ci sono stati da Yingxiu?'},\n", + " {'id': '56ceb9c4aab44d1400b8892d',\n", + " 'answers': [{'text': '9.000', 'answer_start': 146}],\n", + " 'question': 'Quante persone in totale vivevano a Yingxiu?'},\n", + " {'id': '56ceb9c4aab44d1400b8892e',\n", + " 'answers': [{'text': 'Da 3.000 a 5.000', 'answer_start': 162}],\n", + " 'question': 'Quante persone sono state uccise nella contea di Beichuan?'},\n", + " {'id': '56ceb9c4aab44d1400b8892f',\n", + " 'answers': [{'text': '10.000', 'answer_start': 269}],\n", + " 'question': 'Quante persone sono rimaste ferite nella contea di Beichuan?'},\n", + " {'id': '56d5307f2593cc1400307abb',\n", + " 'answers': [{'text': '2.300', 'answer_start': 42}],\n", + " 'question': \"Quanti sopravvissuti c' erano nella città di Yingxiu?\"},\n", + " {'id': '56d5307f2593cc1400307abc',\n", + " 'answers': [{'text': 'circa 9.000', 'answer_start': 140}],\n", + " 'question': 'Qual era la popolazione precedente di Yingxiu?'},\n", + " {'id': '56d5307f2593cc1400307abd',\n", + " 'answers': [{'text': 'Da 3.000 a 5.000', 'answer_start': 162}],\n", + " 'question': 'Quanti residenti sono stati uccisi nella contea di Beichuan?'},\n", + " {'id': '56d5307f2593cc1400307abe',\n", + " 'answers': [{'text': '10.000', 'answer_start': 269}],\n", + " 'question': 'Quanto è stato grande il numero di feriti nella contea di Beichuan?'},\n", + " {'id': '56d5307f2593cc1400307abf',\n", + " 'answers': [{'text': 'Otto scuole', 'answer_start': 465}],\n", + " 'question': 'Qual è il numero di scuole crollate in Dujiangyan?'}]},\n", + " {'context': 'Gli esperti sottolineano che il terremoto ha colpito un\\' area in gran parte trascurata e non toccata dall\\' ascesa economica della Cina. L\\' assistenza sanitaria è carente nelle zone interne come il Sichuan, il che mette in evidenza il crescente divario tra gli abitanti urbani prosperi e le popolazioni rurali in difficoltà. Vice Ministro della Sanità Gao Qiang ha detto ai giornalisti a Pechino che il \"sistema sanitario pubblico in Cina è insufficiente\".',\n", + " 'qas': [{'id': '56ceba72aab44d1400b88936',\n", + " 'answers': [{'text': 'Gao Qiang', 'answer_start': 351}],\n", + " 'question': 'Chi era il Vice Ministro della Salute?'},\n", + " {'id': '56d5324d2593cc1400307ac6',\n", + " 'answers': [{'text': 'zone interne', 'answer_start': 176}],\n", + " 'question': \"Dove è povera l' assistenza sanitaria in Cina?\"},\n", + " {'id': '56d5324d2593cc1400307ac7',\n", + " 'answers': [{'text': 'insufficiente', 'answer_start': 440}],\n", + " 'question': 'Che cosa ha chiamato il Vice Ministro della Salute il sistema sanitario pubblico in Cina?'}]},\n", + " {'context': 'In termini di feriti scolastici, migliaia di bambini sono morti a causa di costruzioni scadenti. A Mianyang City, sette scuole sono crollate, seppellendo almeno 1.700 persone. Almeno 7.000 edifici scolastici in tutta la provincia sono crollati. Altri 700 studenti sono stati sepolti in una scuola di Hanwang. Alla scuola elementare Juyuan sono morti almeno 600 studenti e personale. Fino a 1.300 bambini e insegnanti sono morti alla Beichuan Middle School.',\n", + " 'qas': [{'id': '56cebae8aab44d1400b8893d',\n", + " 'answers': [{'text': 'migliaia', 'answer_start': 33}],\n", + " 'question': 'Quanti bambini sono morti a causa di costruzioni scadenti?'},\n", + " {'id': '56cebae8aab44d1400b8893e',\n", + " 'answers': [{'text': 'sette', 'answer_start': 114}],\n", + " 'question': 'Quante scuole sono crollate a Mianyang City?'},\n", + " {'id': '56cebae8aab44d1400b8893f',\n", + " 'answers': [{'text': '1.700', 'answer_start': 161}],\n", + " 'question': 'Quante persone sono state sepolte nelle scuole crollate?'},\n", + " {'id': '56cebae8aab44d1400b88940',\n", + " 'answers': [{'text': '7.000', 'answer_start': 183}],\n", + " 'question': 'Quanti edifici scolastici sono crollati in provincia?'},\n", + " {'id': '56cebae8aab44d1400b88941',\n", + " 'answers': [{'text': '700', 'answer_start': 163}],\n", + " 'question': 'Quanti studenti sono stati sepolti in una scuola di Hanwang?'},\n", + " {'id': '56d533a52593cc1400307ad0',\n", + " 'answers': [{'text': 'sette', 'answer_start': 114}],\n", + " 'question': 'Quante scuole sono crollate a Mianyang City?'},\n", + " {'id': '56d533a52593cc1400307ad1',\n", + " 'answers': [{'text': '7.000', 'answer_start': 183}],\n", + " 'question': 'Quanti edifici scolastici sono caduti in tutta la provincia?'},\n", + " {'id': '56d533a52593cc1400307ad2',\n", + " 'answers': [{'text': '600', 'answer_start': 357}],\n", + " 'question': 'Quanti sono stati uccisi alla scuola elementare di Juyuan?'}]},\n", + " {'context': \"I dettagli delle vittime scolastiche sono stati oggetto di indagini non governative dal dicembre 2008 da parte di volontari, tra cui l' artista e architetto Ai Weiwei, che dal marzo 2009 ha costantemente pubblicato aggiornamenti sul suo blog. Il conteggio ufficiale degli studenti uccisi nel terremoto è stato rilasciato solo il 7 maggio 2009, quasi un anno dopo il terremoto. Secondo l' agenzia di stampa statale Xinhua, il terremoto ha ucciso 5.335 studenti e lasciato altri 546 bambini disabili. All' indomani del terremoto, il governo cinese ha dichiarato che i genitori che avevano perso i loro soli figli avrebbero ricevuto cure gratuite da cliniche di fertilità per invertire vasectomie e legature tubarie condotte dalle autorità di pianificazione familiare.\",\n", + " 'qas': [{'id': '56cebb71aab44d1400b88951',\n", + " 'answers': [{'text': 'dicembre 2008', 'answer_start': 88}],\n", + " 'question': \"Quando si è svolta un' indagine in seguito a incidenti scolastici?\"},\n", + " {'id': '56cebb71aab44d1400b88952',\n", + " 'answers': [{'text': '7 maggio 2009', 'answer_start': 329}],\n", + " 'question': 'Quando è stato rilasciato il conteggio ufficiale degli studenti uccisi nel terremoto?'},\n", + " {'id': '56cebb71aab44d1400b88953',\n", + " 'answers': [{'text': '5.335', 'answer_start': 445}],\n", + " 'question': 'Quanti studenti sono stati uccisi a Xinhua?'},\n", + " {'id': '56cebb71aab44d1400b88954',\n", + " 'answers': [{'text': '546', 'answer_start': 477}],\n", + " 'question': 'Quanti studenti sono stati disabili a Xinhua?'},\n", + " {'id': '56d5357b2593cc1400307ad9',\n", + " 'answers': [{'text': '7 maggio 2009', 'answer_start': 329}],\n", + " 'question': 'Qual è stata la data in cui è stato rilasciato il numero ufficiale degli studenti uccisi nel terremoto?'},\n", + " {'id': '56d5357b2593cc1400307ada',\n", + " 'answers': [{'text': 'Ai Weiwei', 'answer_start': 157}],\n", + " 'question': 'Chi ha tenuto un blog sui decessi scolastici?'},\n", + " {'id': '56d5357b2593cc1400307adb',\n", + " 'answers': [{'text': '5.335', 'answer_start': 445}],\n", + " 'question': 'Qual è il numero totale di bambini che hanno perso la scuola?'},\n", + " {'id': '56d5357b2593cc1400307adc',\n", + " 'answers': [{'text': '546', 'answer_start': 477}],\n", + " 'question': 'Quanti bambini sono disabili?'},\n", + " {'id': '56d5357b2593cc1400307add',\n", + " 'answers': [{'text': 'cliniche di fertilità', 'answer_start': 647}],\n", + " 'question': 'Dove ha deciso il governo cinese che i genitori che avevano perso i figli potessero farsi curare gratuitamente?'}]},\n", + " {'context': \"Il terremoto ha lasciato almeno 5 milioni di persone senza alloggio, anche se il numero potrebbe raggiungere gli 11 milioni. Milioni di animali da allevamento e una notevole quantità di agricoltura sono stati distrutti, tra cui 12,5 milioni di animali, principalmente uccelli. Nella provincia del Sichuan sono morti un milione di suini su un totale di 60 milioni. Catastrophe modeling firm AIR Worldwide ha riportato stime ufficiali delle perdite degli assicuratori pari a 1 miliardo di dollari USA dal terremoto; i danni totali stimati superano i 20 miliardi di dollari USA. Valorizza Chengdu, all' epoca con una popolazione urbana di 4,5 milioni di persone, a circa 115 miliardi di dollari, con solo una piccola parte coperta da assicurazioni.\",\n", + " 'qas': [{'id': '56cebbdeaab44d1400b88959',\n", + " 'answers': [{'text': '5 milioni', 'answer_start': 32}],\n", + " 'question': 'Quante persone sono rimaste senza alloggio?'},\n", + " {'id': '56cebbdeaab44d1400b8895a',\n", + " 'answers': [{'text': '11 milioni', 'answer_start': 113}],\n", + " 'question': 'Quante persone potrebbero essere potenzialmente senza alloggio?'},\n", + " {'id': '56cebbdeaab44d1400b8895b',\n", + " 'answers': [{'text': '12,5 milioni', 'answer_start': 228}],\n", + " 'question': 'Quanti animali sono stati uccisi?'},\n", + " {'id': '56d5372f2593cc1400307ae4',\n", + " 'answers': [{'text': '11 milioni', 'answer_start': 113}],\n", + " 'question': 'Quante persone potrebbero effettivamente essere senzatetto?'},\n", + " {'id': '56d5372f2593cc1400307ae5',\n", + " 'answers': [{'text': '12,5 milioni di animali', 'answer_start': 228}],\n", + " 'question': 'Quanto bestiame è andato perduto?'},\n", + " {'id': '56d5372f2593cc1400307ae6',\n", + " 'answers': [{'text': 'un milione', 'answer_start': 316}],\n", + " 'question': 'Quanti maiali sono morti a causa del terremoto nel Sichuan?'}]},\n", + " {'context': 'Reginald DesRoches, professore di ingegneria civile e ambientale presso la Georgia Tech, ha sottolineato che i danni massicci di proprietà e case nella zona sismica sono dovuti al fatto che la Cina ha creato un adeguato codice di progettazione sismica solo dopo il devastante terremoto di Tangshan del 1976. DesRoches ha detto:\"Se gli edifici fossero più vecchi e costruiti prima di quel terremoto del 1976, è probabile che non siano stati costruiti per un\\' adeguata forza sismica\".',\n", + " 'qas': [{'id': '56cebcb4aab44d1400b88963',\n", + " 'answers': [{'text': 'Reginald DesRoches', 'answer_start': 0}],\n", + " 'question': 'Chi era professore di ingegneria civile e ambientale alla Georgia Tech?'},\n", + " {'id': '56d538bc2593cc1400307aed',\n", + " 'answers': [{'text': 'professore di ingegneria civile e ambientale',\n", + " 'answer_start': 20}],\n", + " 'question': 'Qual è la professione di Reginald DesRoches?'},\n", + " {'id': '56d538bc2593cc1400307aee',\n", + " 'answers': [{'text': '1976', 'answer_start': 302}],\n", + " 'question': \"Quando la Cina ha creato un codice di progettazione sismica per l' edilizia?\"},\n", + " {'id': '56d538bc2593cc1400307aef',\n", + " 'answers': [{'text': 'terremoto di Tangshan', 'answer_start': 276}],\n", + " 'question': \"Che catastrofe li ha spinti a fare un codice di progettazione dell' edificio /.?\"}]},\n", + " {'context': \"Nei giorni successivi al disastro, un team internazionale di ingegneri è stato inviato nella regione per effettuare un' indagine preliminare dettagliata sugli edifici danneggiati. I loro risultati mostrano una varietà di ragioni per cui molte costruzioni non sono riuscite a resistere al terremoto.\",\n", + " 'qas': []},\n", + " {'context': 'Le notizie di cronaca indicano che i villaggi rurali più poveri sono stati colpiti più duramente. Swaminathan Krishnan, assistente professore di ingegneria civile e geofisica presso il California Institute of Technology ha dichiarato:\"Il terremoto si è verificato nella parte rurale della Cina. Probabilmente, molti degli edifici sono stati appena costruiti; non sono stati progettati, per così dire. Swaminathan Krishnan ha aggiunto:\"In Cina ci sono norme edilizie molto forti, che si occupano di terremoto e problemi di progettazione sismica.',\n", + " 'qas': [{'id': '56cebd8aaab44d1400b88978',\n", + " 'answers': [{'text': 'Swaminathan Krishnan', 'answer_start': 98}],\n", + " 'question': 'Chi era professore assistente di ingegneria civile e geofisica presso il California Institute of Technology?'},\n", + " {'id': '56d53bf82593cc1400307afd',\n", + " 'answers': [{'text': 'parte rurale', 'answer_start': 270}],\n", + " 'question': 'In quale parte della Cina si è verificato il terremoto?'}]},\n", + " {'context': 'Anche con le cinque città più grandi del Sichuan che subiscono solo danni di modesta entità a causa del terremoto, alcune stime della perdita economica superano i 75 miliardi di dollari, rendendo il terremoto uno dei disastri naturali più costosi della storia cinese.',\n", + " 'qas': [{'id': '56cebdd0aab44d1400b8897f',\n", + " 'answers': [{'text': '75 miliardi di dollari', 'answer_start': 163}],\n", + " 'question': 'Qual è la stima della perdita economica del terremoto?'},\n", + " {'id': '56d53d9a2593cc1400307b08',\n", + " 'answers': [{'text': '75 miliardi di dollari', 'answer_start': 163}],\n", + " 'question': 'Qual è la stima delle perdite economiche?'},\n", + " {'id': '56d53d9a2593cc1400307b09',\n", + " 'answers': [{'text': 'storia cinese', 'answer_start': 253}],\n", + " 'question': 'Chi è questo uno dei disastri più costosi della storia di?'},\n", + " {'id': '56d53d9a2593cc1400307b0a',\n", + " 'answers': [{'text': 'cinque città più grandi', 'answer_start': 13}],\n", + " 'question': 'Quali città del Sichuan hanno subito danni minori?'}]},\n", + " {'context': \"Forti scosse di assestamento hanno continuato a colpire anche mesi dopo il terremoto principale. Il 25 maggio, una scossa di assestamento di 6.0 Mw (6.4 Ms secondo CEA) ha colpito a nord-est dell' epicentro del terremoto originale, nella contea di Qingchuan, Sichuan, causando otto morti, 1000 feriti e distruggendo migliaia di edifici. Il 27 maggio, due scosse di assestamento, una 5.2 Mw nella contea di Qingchuan e una 5.7 Mw nella contea di Ningqiang, Shaanxi, hanno portato al crollo di oltre 420.000 case e feriti 63 persone. La stessa area ha sofferto due scosse di assestamento più di 5,6 e 6,0 Ms (5,8 e 5,5 Mw, rispettivamente, secondo USGS) il 23 luglio, con conseguente 1 decesso, 6 feriti gravi, crollo di centinaia di case e danni chilometri di autostrade. Pingwu County e Beichuan County, Sichuan, anche a nord-est di Wenchuan e vicino all' epicentro di un terremoto della sig. ra 7.2 nel 1976, subì un terremoto di 6.1 Ms aftershock (5.7 Mw secondo l' USGS) il 1 agosto 1; ha causato 2 morti, 345 feriti, crollo di 707 case, danni a oltre 1.000 case, e bloccato 25 chilometri (16 mi) di strade di campagna.\",\n", + " 'qas': [{'id': '56d540072593cc1400307b0f',\n", + " 'answers': [{'text': '6.0 Mw', 'answer_start': 141}],\n", + " 'question': 'Quanto è stata forte la scossa di assestamento del 25 maggio nella contea di Qingchuan?'},\n", + " {'id': '56d540072593cc1400307b10',\n", + " 'answers': [{'text': '1000', 'answer_start': 289}],\n", + " 'question': 'Quante persone sono rimaste ferite durante la scossa di assestamento del 25 maggio?'},\n", + " {'id': '56d540072593cc1400307b12',\n", + " 'answers': [{'text': '63', 'answer_start': 520}],\n", + " 'question': 'Durante il 27 maggio scosse di assestamento, quante persone sono rimaste ferite?'},\n", + " {'id': '56d540072593cc1400307b13',\n", + " 'answers': [{'text': 'Qingchuan, Sichuan', 'answer_start': 248}],\n", + " 'question': \"Dov' è stata la scossa di assestamento del 5 agosto che ha causato ampie frane di collina?\"}]},\n", + " {'context': 'Vice governatore esecutivo Wei Hong Wei ha confermato il 21 novembre 2008 che più di 90.000 persone in totale erano morti o dispersi nel terremoto. Ha dichiarato che 200.000 case sono state ricostruite e 685.000 sono in fase di ricostruzione, ma 1,94 milioni di famiglie sono ancora prive di ricovero permanente. Sono state ricostruite 1.300 scuole, con il trasferimento iniziale di 25 città, tra cui Beichuan e Wenchuan, due delle aree più devastate. Il governo ha speso 441 miliardi di dollari per gli sforzi di soccorso e ricostruzione.',\n", + " 'qas': [{'id': '56cebef6aab44d1400b88997',\n", + " 'answers': [{'text': 'Wei Hong', 'answer_start': 27}],\n", + " 'question': 'Chi era il vice governatore esecutivo?'},\n", + " {'id': '56cebef6aab44d1400b88999',\n", + " 'answers': [{'text': '200.000', 'answer_start': 166}],\n", + " 'question': 'Quante case sono state ricostruite?'},\n", + " {'id': '56cebef6aab44d1400b8899b',\n", + " 'answers': [{'text': '1.300', 'answer_start': 336}],\n", + " 'question': 'Quante scuole sono state ricostruite?'},\n", + " {'id': '56d548552593cc1400307b19',\n", + " 'answers': [{'text': 'Wei Hong', 'answer_start': 27}],\n", + " 'question': 'Chi ha parlato dei morti e dei dispersi il 21 novembre 2008?'},\n", + " {'id': '56d548552593cc1400307b1a',\n", + " 'answers': [{'text': '90.000', 'answer_start': 85}],\n", + " 'question': 'Quante persone hanno detto Wei Hong che erano morte o mancanti?'},\n", + " {'id': '56d548552593cc1400307b1b',\n", + " 'answers': [{'text': '200.000', 'answer_start': 166}],\n", + " 'question': 'Quante case sono state ricostruite?'},\n", + " {'id': '56d548552593cc1400307b1c',\n", + " 'answers': [{'text': '685.000', 'answer_start': 204}],\n", + " 'question': 'Quante case erano ancora in costruzione?'}]},\n", + " {'context': \"Il segretario generale e presidente Hu Jintao ha annunciato che la risposta alle catastrofi sarà rapida. A soli 90 minuti dal terremoto, il premier Wen Jiabao, che ha una formazione accademica in geomeccanica, ha volato nella zona del terremoto per sorvegliare i lavori di salvataggio. Poco dopo, il Ministero della Salute ha detto che aveva inviato dieci squadre mediche di emergenza a Wenchuan County. Lo stesso giorno, il comando militare della regione militare di Chengdu ha inviato 50.000 soldati e polizia armata per aiutare con il lavoro di soccorso in caso di catastrofe nella contea di Wenchuan. Tuttavia, a causa del terreno accidentato e della vicinanza dell' epicentro del sisma, i soldati hanno avuto molte difficoltà ad ottenere aiuto nelle regioni rurali della provincia.\",\n", + " 'qas': [{'id': '56cebf6aaab44d1400b889a1',\n", + " 'answers': [{'text': 'il premier Wen Jiabao', 'answer_start': 137}],\n", + " 'question': \"Chi ha volato nella zona sismica 90 minuti dopo l' urto?\"},\n", + " {'id': '56cebf6aaab44d1400b889a2',\n", + " 'answers': [{'text': 'geomeccanica', 'answer_start': 196}],\n", + " 'question': \"In cosa c' era il background di Premier Wen Jiabao?\"},\n", + " {'id': '56cebf6aaab44d1400b889a3',\n", + " 'answers': [{'text': 'i lavori di salvataggio', 'answer_start': 261}],\n", + " 'question': 'Che cosa ha supervisionato Jiabao nella regione?'},\n", + " {'id': '56cebf6aaab44d1400b889a4',\n", + " 'answers': [{'text': '50.000', 'answer_start': 487}],\n", + " 'question': 'Quante truppe sono state inviate dai militari Chengdu?'},\n", + " {'id': '56d54a582593cc1400307b25',\n", + " 'answers': [{'text': 'dieci', 'answer_start': 350}],\n", + " 'question': 'Quante squadre mediche sono state inviate nella contea di Wenchuan?'},\n", + " {'id': '56d54a582593cc1400307b26',\n", + " 'answers': [{'text': '50.000', 'answer_start': 487}],\n", + " 'question': 'Quante truppe sono state inviate nella zona per operazioni di soccorso?'}]},\n", + " {'context': 'La Commissione nazionale di soccorso in caso di catastrofe ha avviato un \"piano di emergenza di II livello\", che copre la categoria più grave di catastrofi naturali. Il piano è salito al livello I alle 22:15 CST, 12 maggio.',\n", + " 'qas': [{'id': '56cebfcaaab44d1400b889ab',\n", + " 'answers': [{'text': 'alle 22:15 CST, 12 maggio', 'answer_start': 197}],\n", + " 'question': 'Quando è salito il piano al livello I?'},\n", + " {'id': '56d54bc82593cc1400307b2e',\n", + " 'answers': [{'text': 'più grave', 'answer_start': 132}],\n", + " 'question': \"Quale classe di catastrofi è un' emergenza di livello II?\"},\n", + " {'id': '56d54bc82593cc1400307b30',\n", + " 'answers': [{'text': 'Commissione nazionale di soccorso in caso di catastrofe',\n", + " 'answer_start': 3}],\n", + " 'question': 'Quale dipartimento ha avviato il piano di emergenza?'}]},\n", + " {'context': \"Un' équipe di soccorso in caso di terremoto di 184 persone (composta da 12 persone dell' Ufficio sismologico di Stato, 150 del Comando dell' Area militare di Pechino e 22 dell' Ospedale Generale della Polizia Armata) ha lasciato Pechino dall' aeroporto di Nanyuan alla fine del 12 maggio in due aerei da trasporto militare per raggiungere la contea di Wenchuan.\",\n", + " 'qas': [{'id': '56cec033aab44d1400b889af',\n", + " 'answers': [{'text': '184', 'answer_start': 47}],\n", + " 'question': 'Quante persone erano nel team di soccorso in caso di terremoto?'},\n", + " {'id': '56cec033aab44d1400b889b0',\n", + " 'answers': [{'text': '12', 'answer_start': 72}],\n", + " 'question': \"Quante squadre di soccorso provenivano dall' Ufficio sismologico di Stato?\"},\n", + " {'id': '56cec033aab44d1400b889b1',\n", + " 'answers': [{'text': '150', 'answer_start': 119}],\n", + " 'question': 'Quanti membri della squadra erano militari?'},\n", + " {'id': '56cec033aab44d1400b889b2',\n", + " 'answers': [{'text': '22', 'answer_start': 168}],\n", + " 'question': 'Quanti membri della squadra provenivano dalla polizia?'},\n", + " {'id': '56d5d41b1c85041400946e0d',\n", + " 'answers': [{'text': '184', 'answer_start': 47}],\n", + " 'question': 'Quante persone costituivano la squadra di soccorso?'},\n", + " {'id': '56d5d41b1c85041400946e0e',\n", + " 'answers': [{'text': '150', 'answer_start': 119}],\n", + " 'question': \"Quanti soldati provenivano dall' esercito di Pechino?\"},\n", + " {'id': '56d5d41b1c85041400946e0f',\n", + " 'answers': [{'text': 'Ospedale Generale della Polizia Armata',\n", + " 'answer_start': 177}],\n", + " 'question': 'Da dove provengono 22 della squadra di soccorso?'}]},\n", + " {'context': 'Nel China Digital Times un articolo riporta un\\' analisi approfondita da parte di un presunto ingegnere edile cinese conosciuto online come \"Book Blade\" (??), che ha dichiarato: un\\' analisi approfondita da parte di un presunto ingegnere edile cinese.',\n", + " 'qas': [{'id': '56cec11faab44d1400b889c8',\n", + " 'answers': [{'text': 'Book Blade', 'answer_start': 140}],\n", + " 'question': \"Dov' è stato riportato un articolo sullo scandalo?\"},\n", + " {'id': '56d64d231c85041400947089',\n", + " 'answers': [{'text': 'China Digital Times', 'answer_start': 4}],\n", + " 'question': 'Qual è stato il nome della persona che ha pubblicato un rapporto nel China Digital Times?'}]},\n", + " {'context': 'In occasione della Festa dei bambini, il 1° giugno 2008, molti genitori si sono recati sulle macerie delle scuole per piangere i loro figli. I bambini sopravvissuti, che vivevano per lo più in centri di soccorso, hanno compiuto cerimonie che hanno segnato il giorno speciale, ma anche riconosciuto il terremoto.',\n", + " 'qas': [{'id': '56cec174aab44d1400b889cb',\n", + " 'answers': [{'text': 'Festa dei bambini', 'answer_start': 19}],\n", + " 'question': 'Che cosa è stato chiamato 1 giugno 2008?'}]},\n", + " {'context': \"Le imprese statali centrali hanno donato complessivamente più di 48,6 milioni di dollari. China National Petroleum Corp e Sinopec ha donato 10 milioni di yuan ciascuno per l' area del disastro.\",\n", + " 'qas': [{'id': '56cec1ffaab44d1400b889de',\n", + " 'answers': [{'text': '10 milioni di yuan', 'answer_start': 140}],\n", + " 'question': 'Quanto ha donato China National Petroleum Corp e Sinopec?'},\n", + " {'id': '56d6647b1c850414009470ed',\n", + " 'answers': [{'text': '48,6 milioni', 'answer_start': 65}],\n", + " 'question': 'Quanto hanno donato le imprese statali centrali?'},\n", + " {'id': '56d6647b1c850414009470ee',\n", + " 'answers': [{'text': '10 milioni di yuan ciascuno', 'answer_start': 140}],\n", + " 'question': 'Quanto ha donato China National Petroleum e Sinopec?'}]},\n", + " {'context': \"Il 16 maggio la Cina ha dichiarato di aver ricevuto anche 457 milioni di dollari in donazioni di denaro e beni per gli sforzi di salvataggio finora compiuti, compresi 83 milioni di dollari provenienti da 19 paesi e quattro organizzazioni internazionali. L' Arabia Saudita è stata il principale donatore di aiuti alla Cina, fornendo quasi 40.000.000 di euro in assistenza finanziaria e altri 8.000.000 di euro di materiale di soccorso.\",\n", + " 'qas': [{'id': '56cec2a1aab44d1400b889e1',\n", + " 'answers': [{'text': '457 milioni di dollari', 'answer_start': 58}],\n", + " 'question': 'Quanto ha ricevuto la Cina in denaro e beni donati?'},\n", + " {'id': '56cec2a1aab44d1400b889e2',\n", + " 'answers': [{'text': '19 paesi', 'answer_start': 204}],\n", + " 'question': 'Quanti paesi hanno donato?'},\n", + " {'id': '56cec2a1aab44d1400b889e3',\n", + " 'answers': [{'text': 'quattro', 'answer_start': 215}],\n", + " 'question': 'Quante organizzazioni internazionali hanno donato?'},\n", + " {'id': '56cec2a1aab44d1400b889e4',\n", + " 'answers': [{'text': 'Arabia Saudita', 'answer_start': 257}],\n", + " 'question': 'Quale paese è stato il maggiore donatore di aiuti alla Cina?'},\n", + " {'id': '56d6696d1c850414009470fb',\n", + " 'answers': [{'text': '83 milioni', 'answer_start': 167}],\n", + " 'question': 'Quanto denaro è stato donato da fonti straniere?'},\n", + " {'id': '56d6696d1c850414009470fc',\n", + " 'answers': [{'text': 'Arabia Saudita', 'answer_start': 257}],\n", + " 'question': 'Quale paese è stato il maggiore donatore di aiuti alla Cina?'},\n", + " {'id': '56d6696d1c850414009470fe',\n", + " 'answers': [{'text': 'quattro', 'answer_start': 215}],\n", + " 'question': 'Quante organizzazioni internazionali hanno fatto donazioni?'}]},\n", + " {'context': 'Nel 2008, il Consiglio di Stato ha stabilito un piano di sostegno alla controparte????????? Il piano è quello di organizzare 19 province orientali e centrali e municipalitie per aiutare 18 contee, su \"una provincia a una contea interessata\" base. Il piano ha una durata di 3 anni, e costa non meno dell\\' uno per cento del bilancio della provincia o del comune. un piano di sostegno di contropartita.',\n", + " 'qas': [{'id': '56d66f3e1c85041400947117',\n", + " 'answers': [{'text': '3 anni', 'answer_start': 273}],\n", + " 'question': 'Quanto dura il piano?'},\n", + " {'id': '56d66f3e1c85041400947118',\n", + " 'answers': [{'text': 'uno per cento', 'answer_start': 304}],\n", + " 'question': 'Qual è il costo per il bilancio della provincia?'}]},\n", + " {'context': 'Un articolo in Science suggerisce che la costruzione e il riempimento della diga di Zipingpu potrebbe aver innescato il terremoto. L\\' ingegnere capo del Sichuan Geology and Mineral Bureau ha detto che lo spostamento improvviso di una grande quantità d\\' acqua nella regione avrebbe potuto allentare la tensione tra i due lati del guasto, permettendo loro di allontanarsi, e avrebbe potuto aumentare la pressione diretta su di esso, causando una violenta rottura. L\\' effetto è stato \"25 volte più\" dello stress naturale del movimento tettonico di un anno. Il governo aveva ignorato gli avvertimenti relativi a tanti progetti di dighe su larga scala in un\\' area sismicamente attiva. Ai ricercatori è stato negato l\\' accesso ai dati sismologici e geologici per esaminare ulteriormente la causa del terremoto.',\n", + " 'qas': [{'id': '56d677f31c85041400947145',\n", + " 'answers': [{'text': 'Il governo', 'answer_start': 554}],\n", + " 'question': 'Chi ha ignorato gli avvertimenti sulle dighe nella zona?'},\n", + " {'id': '56d677f31c85041400947147',\n", + " 'answers': [{'text': 'accesso ai dati sismologici e geologici',\n", + " 'answer_start': 713}],\n", + " 'question': 'Che cosa sono stati negati ai ricercatori?'}]},\n", + " {'context': 'Il terremoto ha inoltre offerto ai ricercatori l\\' opportunità di aggiornare i dati per modellare le previsioni future. Utilizzando i dati dell\\' osservatorio geomagnetico Intermagnet Lanzhou, i geologi Lazo Pekevski della Ss. Cyril e Methodius University di Skopje in Macedonia e Strachimir Mavrodiev dell\\' Accademia bulgara delle Scienze hanno cercato di stabilire un \"metodo di previsione temporale\" attraverso la raccolta di statistiche sul geomagnetismo con potenziale gravitazionale delle maree. Utilizzando questo metodo, si diceva che avessero previsto il tempo del terremoto del Sichuan del 2008 con una precisione di ± 1 giorno. Lo stesso studio, tuttavia, riconosce la limitazione dei modelli di previsione dei terremoti e non menziona che la localizzazione del sisma potrebbe essere accuratamente prevista.',\n", + " 'qas': [{'id': '56cec484aab44d1400b88a09',\n", + " 'answers': [{'text': 'il tempo del terremoto del Sichuan del 2008',\n", + " 'answer_start': 559}],\n", + " 'question': 'Cosa prevedevano i professori?'},\n", + " {'id': '56d675ed1c8504140094713d',\n", + " 'answers': [{'text': 'statistiche', 'answer_start': 427}],\n", + " 'question': 'Cosa hanno raccolto per usare questo metodo?'}]},\n", + " {'context': 'In una conferenza stampa tenuta dall\\' Ufficio informazioni del Consiglio di Stato il giorno dopo il terremoto, il geologo Zhang Xiaodong, vice direttore del CEA Seismic Monitoring Network Center, ha ribadito che la previsione del terremoto era un problema globale, nel senso che non esistono metodi comprovati, e che nessuna notifica di previsione è stata ricevuta prima del terremoto. Il sismologo Gary Gibson dell\\' Università di Monash University in Australia ha detto a Deutsche Presse-Agentur che anche lui non vedeva nulla che potesse essere considerato come aver \"previsto\" l\\' accadimento del terremoto.',\n", + " 'qas': [{'id': '56d674591c85041400947133',\n", + " 'answers': [{'text': 'non esistono metodi comprovati',\n", + " 'answer_start': 279}],\n", + " 'question': 'Che cosa credono molti geologi nella previsione dei terremoti?'},\n", + " {'id': '56d674591c85041400947135',\n", + " 'answers': [{'text': 'nessuna notifica di previsione',\n", + " 'answer_start': 317}],\n", + " 'question': 'Cosa è stato ricevuto prima del terremoto?'}]},\n", + " {'context': \"Nel 2002, il geologo cinese Chen Xuezhong ha pubblicato uno studio di analisi del rischio sismico in cui è giunto alla conclusione che a partire dal 2003, l' attenzione dovrebbe essere prestata alla possibilità di un terremoto con una magnitudo superiore a 7,0 che si verifica nella regione del Sichuan. Ha basato il suo studio sulla correlazione statistica. Che il Sichuan è un' area sismicamente attiva è stata discussa per anni prima del terremoto, anche se pochi studi indicano una data e un' ora specifiche.\",\n", + " 'qas': [{'id': '56cec548aab44d1400b88a13',\n", + " 'answers': [{'text': 'Nel 2002', 'answer_start': 0}],\n", + " 'question': \"Quando è stata pubblicata l' analisi del rischio sismico?\"},\n", + " {'id': '56cec548aab44d1400b88a14',\n", + " 'answers': [{'text': 'Chen Xuezhong', 'answer_start': 28}],\n", + " 'question': \"Chi ha pubblicato l' analisi del rischio sismico?\"},\n", + " {'id': '56d672d21c85041400947129',\n", + " 'answers': [{'text': 'Chen Xuezhong', 'answer_start': 28}],\n", + " 'question': 'Chi ha pubblicato uno studio di analisi del rischio sismico?'},\n", + " {'id': '56d672d21c8504140094712a',\n", + " 'answers': [{'text': '2002', 'answer_start': 4}],\n", + " 'question': 'In quale anno Chen Xuezhong ha pubblicato uno studio sul terremoto?'}]},\n", + " {'context': 'Il terremoto è stato il peggiore a colpire l\\' area del Sichuan in oltre 30 anni. A seguito del terremoto, esperti e il pubblico in generale hanno chiesto informazioni per sapere se il terremoto avrebbe potuto essere previsto in anticipo e se lo studio delle statistiche relative al terremoto avrebbe potuto portare a una migliore previsione dei terremoti in futuro. La predizione del terremoto non è ancora una scienza consolidata; non c\\' era consenso all\\' interno della comunità scientifica sul fatto che la \"previsione\" del terremoto sia possibile.',\n", + " 'qas': [{'id': '56cec5d1aab44d1400b88a23',\n", + " 'answers': [{'text': 'oltre 30 anni', 'answer_start': 66}],\n", + " 'question': 'Da quanto tempo si è verificato un terremoto di simile entità?'},\n", + " {'id': '56d6713f1c8504140094711f',\n", + " 'answers': [{'text': '30 anni', 'answer_start': 72}],\n", + " 'question': \"Da quanto tempo da quando l' area del Sichuan ha subito un grave terremoto?\"},\n", + " {'id': '56d6713f1c85041400947122',\n", + " 'answers': [{'text': 'statistiche', 'answer_start': 258}],\n", + " 'question': 'La gente voleva sapere se lo studio di quale matematica poteva produrre previsioni migliori?'}]},\n", + " {'context': 'Molte squadre di soccorso, tra cui quella del Taipei Fire Department di Taiwan, sono state segnalate pronte ad unirsi allo sforzo di salvataggio nel Sichuan già mercoledì. Tuttavia, la Croce Rossa Società della Cina ha detto che (il 13 maggio)\"è stato scomodo attualmente a causa del problema del traffico alle zone più colpite più vicino all\\' epicentro\". La Società della Croce Rossa Cinese ha anche dichiarato che le zone disastrate hanno bisogno di tende, forniture mediche, acqua potabile e cibo; tuttavia ha raccomandato di donare denaro invece di altri oggetti, in quanto non era stato possibile raggiungere strade completamente danneggiate o luoghi che sono stati bloccati da frane. Le frane minacciavano continuamente il progresso di un gruppo di ricerca e salvataggio di 80 uomini, ognuno dei quali trasportava circa 40 kg di materiale di soccorso, provenienti da una brigata motorizzata di fanteria sotto il comandante Yang Wenyao, mentre cercavano di raggiungere il villaggio etnicamente tibetano di Sier ad un\\' altezza di 4000 m sul livello del mare nella contea di Pingwu.',\n", + " 'qas': [{'id': '56d5db271c85041400946e42',\n", + " 'answers': [{'text': 'frane', 'answer_start': 683}],\n", + " 'question': 'Quale problema ha continuato ad impedire alle squadre di soccorso di raggiungere le zone colpite?'}]},\n", + " {'context': \"Le piogge e gli smottamenti persistenti nella contea di Wenchuan e nella zona vicina hanno gravemente influenzato gli sforzi di salvataggio. All' inizio delle operazioni di soccorso, il 12 maggio, sono stati impiegati 20 elicotteri per la fornitura di cibo, acqua e aiuti d' emergenza, nonché per l' evacuazione dei feriti e la ricognizione delle aree colpite dal terremoto. A partire dalle 17:37 CST del 13 maggio, un totale di oltre 15.600 soldati e riservisti della milizia della regione militare di Chengdu si erano uniti alla forza di soccorso nelle zone gravemente colpite. Un comandante ha riferito da Yingxiu Town, Wenchuan, che sono stati trovati circa 3.000 sopravvissuti, mentre lo stato degli altri abitanti (circa 9.000) è rimasto poco chiaro. I 1.300 soccorritori raggiunsero l' epicentro e 300 truppe pioniere raggiunsero la sede di Wenchuan a circa 23:30 CST. Entro le 12:17 CST, 14 maggio 2008, la comunicazione nella sede di Wenchuan è stata in parte ripresa. Il pomeriggio del 14 maggio, 15 operazioni speciali 15 truppe, insieme con le forniture di soccorso e attrezzature di comunicazione, paracadutati in inaccessibile Mao County, a nord-est di Wenchuan.\",\n", + " 'qas': [{'id': '56cec79caab44d1400b88a34',\n", + " 'answers': [{'text': '20', 'answer_start': 218}],\n", + " 'question': 'Quanti elicotteri sono stati impiegati?'},\n", + " {'id': '56cec79caab44d1400b88a35',\n", + " 'answers': [{'text': '15.600', 'answer_start': 435}],\n", + " 'question': 'Quanti riservisti delle milizie si sono uniti agli sforzi di salvataggio?'},\n", + " {'id': '56cec79caab44d1400b88a36',\n", + " 'answers': [{'text': 'circa 3.000', 'answer_start': 656}],\n", + " 'question': 'Quanti sopravvissuti sono stati trovati?'},\n", + " {'id': '56cec79caab44d1400b88a37',\n", + " 'answers': [{'text': '15', 'answer_start': 435}],\n", + " 'question': 'Quante truppe paracadute nella contea di Mao?'},\n", + " {'id': '56d5ecf21c85041400946e5b',\n", + " 'answers': [{'text': '20', 'answer_start': 218}],\n", + " 'question': 'Quanti elicotteri sono stati inviati per fornire aiuti alle zone colpite?'},\n", + " {'id': '56d5ecf21c85041400946e5c',\n", + " 'answers': [{'text': '15.600', 'answer_start': 435}],\n", + " 'question': 'Entro il 13 maggio, quante truppe erano state aggiunte agli sforzi di salvataggio?'},\n", + " {'id': '56d5ecf21c85041400946e5d',\n", + " 'answers': [{'text': 'circa 3.000', 'answer_start': 656}],\n", + " 'question': 'Come sono state segnalate le persone sopravvissute nella città di Yingxiu?'},\n", + " {'id': '56d5ecf21c85041400946e5e',\n", + " 'answers': [{'text': 'circa 9.000', 'answer_start': 721}],\n", + " 'question': 'Quante persone a Yingxiu erano ancora sconosciute?'}]},\n", + " {'context': \"Il 15 maggio, il premier Wen Jiabao ha ordinato lo spiegamento di altri 90 elicotteri, di cui 60 forniti dal PLAAF e 30 forniti dall' industria dell' aviazione civile, portando così il numero totale di aeromobili impiegati nelle operazioni di soccorso dall' aviazione civile, dall' esercito e dall' aviazione civile a oltre 150, il che ha portato alla più grande operazione di non combattimento nella storia dell' Esercito popolare di liberazione.\",\n", + " 'qas': [{'id': '56cec801aab44d1400b88a3e',\n", + " 'answers': [{'text': '60', 'answer_start': 94}],\n", + " 'question': 'Quanti elicotteri sono stati forniti dal PLAAF?'},\n", + " {'id': '56cec801aab44d1400b88a3f',\n", + " 'answers': [{'text': '30', 'answer_start': 117}],\n", + " 'question': \"Quanti elicotteri sarebbero stati forniti dall' industria dell' aviazione civile?\"},\n", + " {'id': '56cec801aab44d1400b88a40',\n", + " 'answers': [{'text': 'oltre 150', 'answer_start': 318}],\n", + " 'question': \"Quanti aerei c' erano in totale?\"},\n", + " {'id': '56d60cd01c85041400946eea',\n", + " 'answers': [{'text': 'oltre 150', 'answer_start': 318}],\n", + " 'question': \"Qual è il numero totale di aeromobili utilizzati nell' operazione di soccorso?\"},\n", + " {'id': '56d60cd01c85041400946eec',\n", + " 'answers': [{'text': '60', 'answer_start': 94}],\n", + " 'question': 'Quanti elicotteri provenivano dal PLAAF?'},\n", + " {'id': '56d60cd01c85041400946eed',\n", + " 'answers': [{'text': \"industria dell' aviazione civile\",\n", + " 'answer_start': 134}],\n", + " 'question': 'Chi ha fornito gli altri 30 elicotteri?'}]},\n", + " {'context': \"Pechino ha accettato l' aiuto della Fondazione Tzu Chi di Taiwan alla fine del 13 maggio. Tzu Chi è stata la prima forza al di fuori della Repubblica Popolare Cinese a partecipare allo sforzo di salvataggio. La Cina ha dichiarato di voler accettare con gratitudine l' aiuto internazionale per far fronte al terremoto.\",\n", + " 'qas': [{'id': '56cec88baab44d1400b88a45',\n", + " 'answers': [{'text': 'Fondazione Tzu Chi', 'answer_start': 36}],\n", + " 'question': 'Quale Fondazione ha voluto aiutare Pechino?'},\n", + " {'id': '56cec88baab44d1400b88a46',\n", + " 'answers': [{'text': 'Taiwan', 'answer_start': 58}],\n", + " 'question': \"Dov' era la fondazione?\"},\n", + " {'id': '56d60ddf1c85041400946ef5',\n", + " 'answers': [{'text': 'Fondazione Tzu Chi', 'answer_start': 36}],\n", + " 'question': \"Qual è stato il primo gruppo di fuori della Cina a partecipare all' operazione di soccorso?\"},\n", + " {'id': '56d60ddf1c85041400946ef6',\n", + " 'answers': [{'text': 'aiuto internazionale', 'answer_start': 268}],\n", + " 'question': 'Che cosa ha detto la Cina di accettare?'}]},\n", + " {'context': \"Un volo merci charter diretto è stato effettuato dalla China Airlines da Taiwan Taoyuan International Airport all' aeroporto internazionale di Chengdu Shuangliu, che ha inviato circa 100 tonnellate di forniture di soccorso donate dalla Fondazione Tzu Chi e dalla Croce Rossa di Taiwan alle zone colpite. È stata chiesta l' approvazione delle autorità cinesi della Cina continentale e il volo charter è partito da Taipei alle 17:00 CST, il 15 maggio e è arrivato a Chengdu entro le 20:30 CST. Una squadra di soccorso della Croce Rossa di Taiwan avrebbe dovuto partire da Taipei con un volo charter diretto di Mandarin Airlines verso Chengdu alle 15:00 CST del 16 maggio.\",\n", + " 'qas': [{'id': '56cec8e2aab44d1400b88a55',\n", + " 'answers': [{'text': 'China Airlines', 'answer_start': 55}],\n", + " 'question': 'Chi ha effettuato un volo cargo charter diretto?'},\n", + " {'id': '56cec8e2aab44d1400b88a57',\n", + " 'answers': [{'text': '15 maggio', 'answer_start': 439}],\n", + " 'question': 'Quale data di partenza del volo cargo?'},\n", + " {'id': '56cec8e2aab44d1400b88a58',\n", + " 'answers': [{'text': '16 maggio', 'answer_start': 659}],\n", + " 'question': 'Quando è partita una squadra di soccorso?'},\n", + " {'id': '56d613011c85041400946efd',\n", + " 'answers': [{'text': 'Taiwan Taoyuan International Airport',\n", + " 'answer_start': 73}],\n", + " 'question': 'Da quale aeroporto parte il volo charter?'},\n", + " {'id': '56d613011c85041400946efe',\n", + " 'answers': [{'text': 'Chengdu', 'answer_start': 143}],\n", + " 'question': 'Dove è avvenuto il volo charter da Taiwan?'},\n", + " {'id': '56d613011c85041400946eff',\n", + " 'answers': [{'text': 'squadra di soccorso', 'answer_start': 496}],\n", + " 'question': 'Che cosa ha lasciato il team della Croce Rossa a Taipei il 16 maggio Chengdu?'}]},\n", + " {'context': \"Il 16 maggio, i gruppi di soccorso provenienti da Corea del Sud, Giappone, Singapore, Russia e Taiwan sono arrivati per unirsi allo sforzo di salvataggio. Gli Stati Uniti hanno condiviso con le autorità cinesi alcune delle sue immagini satellitari delle zone colpite dal terremoto. Durante il fine settimana, gli Stati Uniti hanno inviato in Cina due forniture di trasporto dell' aeronautica militare C-17 degli Stati Uniti, tra cui tende e generatori. Xinhua ha riferito 135.000 truppe cinesi e medici sono stati coinvolti nello sforzo di salvataggio in 58 contee e città. immagini satellitari delle aree colpite dal terremoto.\",\n", + " 'qas': [{'id': '56cec9e1aab44d1400b88a63',\n", + " 'answers': [{'text': '135.000', 'answer_start': 472}],\n", + " 'question': 'Quante truppe cinesi sono state coinvolte negli sforzi di salvataggio?'},\n", + " {'id': '56cec9e1aab44d1400b88a65',\n", + " 'answers': [{'text': '16 maggio', 'answer_start': 3}],\n", + " 'question': 'Quando sono arrivati in Cina gruppi provenienti dalla Corea del Sud, dal Giappone e da altri paesi?'},\n", + " {'id': '56d61a451c85041400946f10',\n", + " 'answers': [{'text': 'immagini satellitari', 'answer_start': 227}],\n", + " 'question': 'Che cosa ha condiviso gli Stati Uniti con la Cina?'},\n", + " {'id': '56d61a451c85041400946f11',\n", + " 'answers': [{'text': 'tende e generatori', 'answer_start': 433}],\n", + " 'question': \"Cosa è stato incluso nelle forniture dell' aeronautica militare C-17?\"},\n", + " {'id': '56d61a451c85041400946f13',\n", + " 'answers': [{'text': '135.000', 'answer_start': 472}],\n", + " 'question': 'Quante truppe e medici cinesi sono stati coinvolti negli sforzi di soccorso?'}]},\n", + " {'context': \"Internet è stato ampiamente utilizzato per trasmettere informazioni a sostegno degli sforzi di salvataggio e recupero. Ad esempio, l' agenzia di stampa ufficiale Xinhua ha istituito un centro online di richiesta di soccorso per trovare gli angoli ciechi del disaster recovery. Dopo aver saputo che gli elicotteri di soccorso avevano avuto difficoltà ad atterrare nell' area dell' epicentro di Wenchuan, uno studente ha proposto un punto di atterraggio online ed è stato scelto come primo touchdown per gli elicotteri[non in citazione dato]. I volontari hanno anche creato diversi siti web per aiutare a memorizzare le informazioni di contatto per le vittime e gli sfollati. Il 31 maggio, un elicottero di salvataggio che trasportava sopravvissuti al terremoto e membri dell' equipaggio si è schiantato a causa della nebbia e delle turbolenze nella contea di Wenchuan. Nessuno è sopravvissuto.\",\n", + " 'qas': [{'id': '56ceccc7aab44d1400b88a7d',\n", + " 'answers': [{'text': 'Internet', 'answer_start': 0}],\n", + " 'question': 'Che cosa è stato ampiamente utilizzato per trasmettere informazioni a sostegno degli sforzi di salvataggio e recupero?'},\n", + " {'id': '56ceccc7aab44d1400b88a7f',\n", + " 'answers': [{'text': 'trovare gli angoli ciechi del disaster recovery',\n", + " 'answer_start': 228}],\n", + " 'question': 'Qual era lo scopo di questo centro di richiesta di soccorso online?'},\n", + " {'id': '56ceccc7aab44d1400b88a80',\n", + " 'answers': [{'text': 'Wenchuan', 'answer_start': 393}],\n", + " 'question': 'Dove hanno avuto difficoltà di atterraggio gli elicotteri di soccorso?'},\n", + " {'id': '56d61c4e1c85041400946f1a',\n", + " 'answers': [{'text': 'Internet', 'answer_start': 0}],\n", + " 'question': 'Che cosa è stato utilizzato come aiuto alle comunicazioni negli sforzi di soccorso?'},\n", + " {'id': '56d61c4e1c85041400946f1c',\n", + " 'answers': [{'text': 'uno studente', 'answer_start': 403}],\n", + " 'question': \"Quale persona ha suggerito un atterraggio per elicotteri vicino all' epicentro?\"},\n", + " {'id': '56d61c4e1c85041400946f1d',\n", + " 'answers': [{'text': 'informazioni di contatto', 'answer_start': 618}],\n", + " 'question': 'Che tipo di informazioni sono state create sui siti web da archiviare?'}]},\n", + " {'context': \"Il 12 maggio 2009, la Cina ha celebrato il primo anniversario del terremoto con un momento di silenzio mentre le persone in tutta la nazione ricordavano i morti. Il governo ha anche aperto l' accesso alle rovine sigillate della sede della contea del Beichuan per tre giorni, dopo di che sarà congelato in tempo come un museo reliquia terremoto di stato, per ricordare alla gente del disastro terribile. Ci sono stati anche diversi concerti in tutto il paese per raccogliere fondi per i sopravvissuti al terremoto.\",\n", + " 'qas': [{'id': '56cecd53aab44d1400b88a88',\n", + " 'answers': [{'text': 'un momento di silenzio', 'answer_start': 80}],\n", + " 'question': \"Cosa è stato fatto per l' anniversario?\"},\n", + " {'id': '56cecd53aab44d1400b88a8a',\n", + " 'answers': [{'text': 'tre giorni', 'answer_start': 263}],\n", + " 'question': 'Per quanto tempo è stato aperto?'},\n", + " {'id': '56d66b221c85041400947103',\n", + " 'answers': [{'text': 'un momento di silenzio', 'answer_start': 80}],\n", + " 'question': 'Che cosa ha fatto la Cina per celebrare il primo anniversario del terremoto?'},\n", + " {'id': '56d66b221c85041400947105',\n", + " 'answers': [{'text': 'diversi concerti', 'answer_start': 423}],\n", + " 'question': 'Che tipo di evento è stato dato per raccogliere fondi per i sopravvissuti al terremoto?'}]},\n", + " {'context': 'A seguito del terremoto, le donazioni sono state effettuate da persone provenienti da tutta la Cina continentale, con cabine allestite nelle scuole, presso le banche e nelle vicinanze di distributori di benzina. La gente ha anche donato sangue, con conseguente secondo Xinhua lungo line-up Xinhua nella maggior parte delle principali città cinesi. Molti donato attraverso messaggi di testo sui telefoni cellulari per i conti istituiti da China Unicom e China Mobile 16 maggio, il governo cinese aveva stanziato un totale di 772 milioni di dollari per il terremoto di soccorso finora, in forte aumento da $ 159 milioni di dollari dal 14 maggio sangue.',\n", + " 'qas': [{'id': '56d666041c850414009470f1',\n", + " 'answers': [{'text': 'donato sangue', 'answer_start': 230}],\n", + " 'question': 'Che cosa ha causato le lunghe linee nella maggior parte delle grandi città?'},\n", + " {'id': '56d666041c850414009470f2',\n", + " 'answers': [{'text': 'cabine', 'answer_start': 118}],\n", + " 'question': 'Quali sono state le premesse cinesi per raccogliere donazioni?'},\n", + " {'id': '56d666041c850414009470f4',\n", + " 'answers': [{'text': '772 milioni di dollari', 'answer_start': 524}],\n", + " 'question': 'Quanto aveva designato il governo cinese entro il 16 maggio?'}]},\n", + " {'context': 'La Società Croce Rossa della Cina ha volato 557 tende e 2.500 trapunte del valore di 788.000 yuan (113.000 dollari USA) nella contea di Wenchuan. La Fondazione Amity ha già iniziato i lavori di soccorso nella regione e ha stanziato 143.000 USD per i soccorsi in caso di catastrofe. Il ministero degli Affari civili del Sichuan ha dichiarato di aver fornito 30.000 tende per i senzatetto rimasti senza tetto.',\n", + " 'qas': [{'id': '56cece5caab44d1400b88a9b',\n", + " 'answers': [{'text': '557', 'answer_start': 44}],\n", + " 'question': 'Quante tende sono state volate nella regione?'},\n", + " {'id': '56cece5caab44d1400b88a9c',\n", + " 'answers': [{'text': '2.500', 'answer_start': 56}],\n", + " 'question': 'Quanti trapunte sono state volate nella regione?'},\n", + " {'id': '56cece5caab44d1400b88a9d',\n", + " 'answers': [{'text': '788.000 yuan', 'answer_start': 85}],\n", + " 'question': 'Quanto valevano entrambe le forniture?'},\n", + " {'id': '56cece5caab44d1400b88a9f',\n", + " 'answers': [{'text': '30.000', 'answer_start': 357}],\n", + " 'question': 'Quante tende ha fornito il ministero del Sichuan ai senzatetto?'},\n", + " {'id': '56d663b11c850414009470e5',\n", + " 'answers': [{'text': '30.000', 'answer_start': 357}],\n", + " 'question': 'Quante tende ha fornito il Ministero degli Affari del Sichuan?'},\n", + " {'id': '56d663b11c850414009470e7',\n", + " 'answers': [{'text': 'La Fondazione Amity', 'answer_start': 146}],\n", + " 'question': 'Quali fondamenta avevano già iniziato i lavori di soccorso nella zona?'}]},\n", + " {'context': 'Il governo centrale stima che oltre 7.000 aule scolastiche non adeguatamente attrezzate siano crollate in seguito al terremoto. Da allora i cittadini cinesi hanno inventato una frase di cattura:\"tofu-dregs schoolhouses\" (Cinese:?????), per fingere sia la qualità che la quantità di queste costruzioni inferiori che hanno ucciso così tanti bambini in età scolare. A causa della politica del figlio unico, molte famiglie hanno perso l\\' unico figlio quando le scuole della regione sono crollate durante il terremoto. Di conseguenza, i funzionari provinciali e locali del Sichuan hanno abolito la restrizione per le famiglie il cui unico bambino è stato ucciso o gravemente ferito in seguito al disastro. I cosiddetti \"bambini illegali\" di età inferiore ai 18 anni possono essere registrati come sostituti legali dei loro fratelli morti; se il bambino morto fosse illegale, non si applicherebbero ulteriori sanzioni pecuniarie. Il rimborso non sarebbe tuttavia offerto per le ammende già riscosse.',\n", + " 'qas': [{'id': '56cecf68aab44d1400b88ab7',\n", + " 'answers': [{'text': '7.000', 'answer_start': 36}],\n", + " 'question': 'Quante aule scolastiche sono crollate nel terremoto?'},\n", + " {'id': '56cecf68aab44d1400b88ab8',\n", + " 'answers': [{'text': 'tofu-dregs schoolhouses', 'answer_start': 195}],\n", + " 'question': \"Quale frase d' insieme è stata inventata a seguito del crollo delle scuole?\"},\n", + " {'id': '56d64a821c85041400947077',\n", + " 'answers': [{'text': 'tofu-dregs schoolhouses', 'answer_start': 195}],\n", + " 'question': 'Che cosa ha iniziato la cittadinanza a chiamare questo tipo di scuole?'}]},\n", + " {'context': 'La sera del 18 maggio, il CCTV-1 ha ospitato un programma speciale di quattro ore chiamato The Giving of Love (cinese semplificato:???; cinese tradizionale:????), ospitato da regolari del Gala di Capodanno CCTV e dall\\' ancora di copertura 24 ore su 24 Bai Yansong. Vi hanno partecipato una vasta gamma di personaggi dello spettacolo, letterari, commerciali e politici provenienti dalla Cina continentale, Hong Kong, Singapore e Taiwan. Le donazioni della serata sono ammontate a 1,5 miliardi di yuan cinese (~208 milioni di dollari USA). Tra le donazioni, la TVCC ha dato il maggior contributo aziendale a ¥50 milioni. Quasi contemporaneamente a Taiwan, un programma tematico simile era in onda ospitato dal presidente in carica Ma Ying-jeou. A giugno, l\\' attore di Hong Kong Jackie Chan, che ha donato 1,57 milioni di dollari alle vittime, ha realizzato un video musicale insieme ad altri artisti intitolato \"Promise\"; la canzone è stata composta da Andy Lau. L\\' Artistes 512 Fund Raising Campaign, una maratona di raccolta fondi della durata di 8 ore, si è tenuta il 1° giugno a Hong Kong, alla quale hanno partecipato circa 200 musicisti e celebrità della Sinosfera. A Singapore, MediaCorp Channel 8 ha ospitato un programma dal vivo?? programma di quattro ore chiamato Il dono dell\\' amore.',\n", + " 'qas': [{'id': '56cecff4aab44d1400b88abe',\n", + " 'answers': [{'text': 'Bai Yansong', 'answer_start': 252}],\n", + " 'question': 'Quanto erano grandi le donazioni del programma?'},\n", + " {'id': '56cecff4aab44d1400b88abf',\n", + " 'answers': [{'text': '1,57 milioni di dollari', 'answer_start': 803}],\n", + " 'question': 'Quanto ha donato Jackie Chan per sostenere?'},\n", + " {'id': '56cecff4aab44d1400b88ac1',\n", + " 'answers': [{'text': \"Il dono dell' amore\", 'answer_start': 1273}],\n", + " 'question': 'Qual era il programma che CCTV-1 ha ospitato?'},\n", + " {'id': '56d6477c1c85041400947065',\n", + " 'answers': [{'text': 'The Giving of Love', 'answer_start': 91}],\n", + " 'question': 'Quali sono state le donazioni totali per il programma?'},\n", + " {'id': '56d6477c1c85041400947066',\n", + " 'answers': [{'text': 'CCTV', 'answer_start': 26}],\n", + " 'question': 'Quale azienda ha dato il massimo?'},\n", + " {'id': '56d6477c1c85041400947067',\n", + " 'answers': [{'text': '1,57 milioni', 'answer_start': 803}],\n", + " 'question': \"Quanto ha donato l' attore Jackie Chan?\"}]},\n", + " {'context': 'Gli sforzi di salvataggio compiuti dal governo cinese sono stati elogiati dai media occidentali, soprattutto in confronto al blocco degli aiuti stranieri da parte del Myanmar durante il ciclone Nargis, nonché ai precedenti risultati della Cina durante il terremoto di Tangshan del 1976. L\\' apertura della Cina durante la copertura mediatica del terremoto del Sichuan ha portato un professore presso l\\' Università di Pechino a dire:\"Questa è la prima volta[che] i media cinesi sono stati all\\' altezza degli standard internazionali\". Los Angeles Times ha elogiato la copertura mediatica della Cina del terremoto di essere \"democratico\".',\n", + " 'qas': [{'id': '56d61fc11c85041400946f24',\n", + " 'answers': [{'text': 'aiuti stranieri', 'answer_start': 138}],\n", + " 'question': 'Che cosa ha fatto blocco Myanmar dopo Cyclone Nargis?'},\n", + " {'id': '56d61fc11c85041400946f26',\n", + " 'answers': [{'text': 'Los Angeles Times', 'answer_start': 532}],\n", + " 'question': 'Chi ha elogiato la copertura mediatica cinese come democratica?'}]},\n", + " {'context': 'A causa del terremoto di magnitudo 7,9 e delle numerose forti scosse di assestamento, molti fiumi sono stati bloccati da grandi frane, che hanno portato alla formazione di \"laghi di sisma\" dietro le ostruzioni; queste enormi quantità d\\' acqua si stavano accumulando ad un ritmo molto elevato dietro le dighe naturali di frane e si temeva che le ostruzioni sarebbero infine crollate sotto il peso della massa d\\' acqua sempre crescente, potenzialmente mettendo a repentaglio la vita di milioni di persone. Al 27 maggio 2008,34 laghi si erano formati a causa del terremoto che bloccava i detriti e arginava i fiumi, e si stimava che 28 di essi fossero ancora potenzialmente pericolosi per la popolazione locale. Gli interi villaggi hanno dovuto essere evacuati a causa delle conseguenti inondazioni.',\n", + " 'qas': [{'id': '56cedd7daab44d1400b88b50',\n", + " 'answers': [{'text': '34', 'answer_start': 522}],\n", + " 'question': 'Quanti laghi di tremito si sono formati?'},\n", + " {'id': '56cedd7daab44d1400b88b51',\n", + " 'answers': [{'text': '28', 'answer_start': 630}],\n", + " 'question': 'Quanti laghi costituivano un pericolo per le persone?'},\n", + " {'id': '56d624261c85041400946f2d',\n", + " 'answers': [{'text': 'grandi frane', 'answer_start': 121}],\n", + " 'question': 'Che cosa ha bloccato molti dei fiumi della zona?'},\n", + " {'id': '56d624261c85041400946f2f',\n", + " 'answers': [{'text': '34', 'answer_start': 522}],\n", + " 'question': 'A partire dal 27 maggio quanti laghi sismici si erano formati dietro i detriti di frane?'},\n", + " {'id': '56d624261c85041400946f30',\n", + " 'answers': [{'text': 'interi villaggi', 'answer_start': 713}],\n", + " 'question': 'Che cosa doveva essere evacuato a causa di un potenziale allagamento?'}]},\n", + " {'context': \"Il più precario di questi ciarlatani era quello che si trovava nel terreno estremamente difficile del Monte Tangjia nella contea di Beichuan, Sichuan, accessibile solo a piedi o in aereo; un elicottero di sollevamento pesante Mi-26T appartenente alla China Flying Dragon Special Aviation Company è stato utilizzato per portare pesanti trattori movimento terra nella zona colpita. Questa operazione è stata accoppiata con il lavoro svolto da elicotteri PLAAF Mi-17 portando in corpi di ingegneria PLA, specialisti di esplosivi e altro personale per unire 1.200 soldati che sono arrivati sul posto a piedi. Cinque tonnellate di combustibile per azionare il macchinario sono state trasportate in aria fino al sito, dove è stata costruita una saracinesca per consentire lo scarico sicuro dell' acqua di strozzatura. A valle, più di 200.000 persone sono state evacuate dal Mianyang entro il 1 giugno in previsione dello scoppio della diga.\",\n", + " 'qas': [{'id': '56ceddf6aab44d1400b88b67',\n", + " 'answers': [{'text': 'Monte Tangjia nella contea di Beichuan, Sichuan',\n", + " 'answer_start': 102}],\n", + " 'question': 'Dove si trovava il lago più precario?'},\n", + " {'id': '56ceddf6aab44d1400b88b69',\n", + " 'answers': [{'text': 'trattori', 'answer_start': 335}],\n", + " 'question': 'Quali macchine sono state trasportate in aereo?'},\n", + " {'id': '56ceddf6aab44d1400b88b6b',\n", + " 'answers': [{'text': '200.000', 'answer_start': 828}],\n", + " 'question': 'Quante persone sono state evacuate a valle?'},\n", + " {'id': '56d62b0f1c85041400946f5e',\n", + " 'answers': [{'text': 'lo scoppio della diga', 'answer_start': 912}],\n", + " 'question': \"Qual è stata la paura che ha causato l' evacuazione di 200.000 persone dal Mianyang?\"},\n", + " {'id': '56d62b0f1c85041400946f5f',\n", + " 'answers': [{'text': 'Monte Tangjia', 'answer_start': 102}],\n", + " 'question': 'Dove si trovava il peggio dei laghi sismici?'},\n", + " {'id': '56d62b0f1c85041400946f60',\n", + " 'answers': [{'text': 'contea di Beichuan, Sichuan', 'answer_start': 122}],\n", + " 'question': 'Dove si trova il monte Tangjia?'},\n", + " {'id': '56d62b0f1c85041400946f62',\n", + " 'answers': [{'text': '1.200', 'answer_start': 554}],\n", + " 'question': 'Quanti soldati hanno dovuto recarsi nella zona a piedi?'}]},\n", + " {'context': 'Il Consiglio di Stato ha dichiarato un periodo di tre giorni di lutto nazionale per le vittime del terremoto a partire dal 19 maggio 2008, la bandiera nazionale della RPC e le bandiere regionali di Hong Kong e Macao regioni amministrative speciali ha volato a metà albero. Era la prima volta che un periodo di lutto nazionale era stato dichiarato per qualcosa di diverso dalla morte di un leader statale, e molti lo hanno definito la più grande manifestazione di lutto dalla morte di Mao Zedong. Alle 14:28 CST 28 il 19 maggio 2008, una settimana dopo il terremoto, il pubblico cinese ha tenuto un momento di silenzio. La gente taceva per tre minuti mentre la difesa aerea, la polizia e le sirene antincendio, e le corna di veicoli, navi e treni suonavano. Anche le auto e i camion sulle strade di Pechino si sono fermati. La gente spontaneamente scoppiò in allegria \"Zhongguo jiayou\"! (Vai, Cina!) e \"Sichuan jiayou\" (Let\\'s go, Sichuan!).',\n", + " 'qas': [{'id': '56cede78aab44d1400b88b7c',\n", + " 'answers': [{'text': 'dopo', 'answer_start': 547}],\n", + " 'question': 'Chi ha dichiarato il lutto?'},\n", + " {'id': '56d62ce51c85041400946f7a',\n", + " 'answers': [{'text': 'lutto nazionale', 'answer_start': 64}],\n", + " 'question': 'Che cosa ha dichiarato il Consiglio di Stato?'},\n", + " {'id': '56d62ce51c85041400946f7b',\n", + " 'answers': [{'text': 'Mao Zedong', 'answer_start': 484}],\n", + " 'question': 'Questa è stata la più grande manifestazione di lutto dalla morte di chi?'},\n", + " {'id': '56d62ce51c85041400946f7c',\n", + " 'answers': [{'text': '19 maggio 2008', 'answer_start': 123}],\n", + " 'question': 'Quando i cinesi hanno avuto un momento di silenzio?'},\n", + " {'id': '56d62ce51c85041400946f7e',\n", + " 'answers': [{'text': 'periodo di tre giorni', 'answer_start': 39}],\n", + " 'question': 'Quanto tempo è durato il lutto nazionale per le vittime del terremoto?'}]},\n", + " {'context': \"Il Comitato Organizzatore di Ningbo del comitato organizzatore della torcia olimpica di Pechino ha annunciato che la staffetta, prevista per svolgersi a Ningbo durante la mattinata nazionale, sarebbe stata sospesa per la durata del periodo di lutto. Il percorso della torcia attraverso il paese è stato ridimensionato, e c' è stato un minuto di silenzio quando la tappa successiva ha iniziato nella città di Ruijin, Jiangxi il mercoledì dopo il terremoto.\",\n", + " 'qas': [{'id': '56cedecdaab44d1400b88b8e',\n", + " 'answers': [{'text': 'Ningbo', 'answer_start': 29}],\n", + " 'question': \"Dov' era previsto il relè?\"},\n", + " {'id': '56d62e501c85041400946f95',\n", + " 'answers': [{'text': 'a Ningbo', 'answer_start': 151}],\n", + " 'question': 'Dove doveva avvenire il relè della torcia?'},\n", + " {'id': '56d62e501c85041400946f96',\n", + " 'answers': [{'text': 'Il percorso', 'answer_start': 250}],\n", + " 'question': 'Quale parte del relè ha cambiato?'},\n", + " {'id': '56d62e501c85041400946f97',\n", + " 'answers': [{'text': 'Ruijin, Jiangxi', 'answer_start': 408}],\n", + " 'question': \"Dove c' era un minuto di silenzio durante la staffetta?\"}]},\n", + " {'context': 'Molti siti web hanno convertito la loro home page in bianco e nero; Sina. com e Sohu, i principali portali internet, hanno limitato le loro homepage alle notizie e rimosso tutte le pubblicità. I siti web cinesi di condivisione video Youku e Tudou hanno mostrato uno sfondo nero e collocato più video che mostrano filmati di terremoto e reportage di notizie. La versione cinese di MSN, cn. msn. com, mostrava anche annunci pubblicitari sul terremoto e gli sforzi di soccorso.',\n", + " 'qas': []},\n", + " {'context': \"Ye Zhiping, il preside della scuola media di Sangzao a Sangzao, una delle più grandi della Contea di An, ha ricevuto il merito di un' azione proattiva che ha risparmiato la vita a tutti i 2.323 alunni presenti quando si è verificato il terremoto. Nel corso di un triennio che si è concluso nel 2007, ha seguito un' importante revisione della sua scuola. Durante quel periodo ha ottenuto più di 400.000 yuan (US$60.000) dal dipartimento di istruzione contea, denaro usato per ampliare e rafforzare i pilastri di cemento e la ringhiera balcone di tutti e quattro i piani della sua scuola, così come fissare i pavimenti in cemento.\",\n", + " 'qas': [{'id': '56cee003aab44d1400b88bb1',\n", + " 'answers': [{'text': 'Ye Zhiping', 'answer_start': 0}],\n", + " 'question': 'Chi era il preside della scuola media di Sangzao?'},\n", + " {'id': '56cee003aab44d1400b88bb3',\n", + " 'answers': [{'text': '2.323', 'answer_start': 188}],\n", + " 'question': 'Quanti studenti hanno frequentato la scuola?'},\n", + " {'id': '56cee003aab44d1400b88bb5',\n", + " 'answers': [{'text': '400.000 yuan', 'answer_start': 394}],\n", + " 'question': 'Quanto denaro è stato utilizzato per rafforzare la costruzione della scuola?'},\n", + " {'id': '56d64f7c1c85041400947095',\n", + " 'answers': [{'text': 'Ye Zhiping', 'answer_start': 0}],\n", + " 'question': 'Quale direttore scolastico ha rafforzato la sua scuola?'},\n", + " {'id': '56d64f7c1c85041400947097',\n", + " 'answers': [{'text': 'Sangzao', 'answer_start': 45}],\n", + " 'question': \"Dov' è la scuola media di Sangzao?\"}]},\n", + " {'context': \"Tuttavia, Reuters ha riferito nel mese di giugno che, ad oggi, i pubblici ministeri cinesi hanno aderito a un' indagine ufficiale in dieci scuole crollato durante il devastante terremoto di maggio per ottenere materiale di prima mano della qualità delle costruzioni nelle scuole crollato, avviare indagini preliminari e prepararsi per eventuali indagini sulla criminalità professionale. È stato inoltre riferito che, dopo il terremoto del mese scorso, si sarebbero dovuti effettuare controlli di sicurezza nelle scuole di tutta la Cina.\",\n", + " 'qas': [{'id': '56cee069aab44d1400b88bbd',\n", + " 'answers': [{'text': 'controlli di sicurezza', 'answer_start': 483}],\n", + " 'question': 'Cosa doveva essere fatto nelle scuole dopo il sisma?'},\n", + " {'id': '56d658061c850414009470a8',\n", + " 'answers': [{'text': 'criminalità professionale', 'answer_start': 360}],\n", + " 'question': 'Quali indagini stanno conducendo i pubblici ministeri?'},\n", + " {'id': '56d658061c850414009470a9',\n", + " 'answers': [{'text': 'scuole di tutta la Cina', 'answer_start': 512}],\n", + " 'question': 'Dove saranno i cinesi ad effettuare i controlli di sicurezza?'},\n", + " {'id': '56d658061c850414009470aa',\n", + " 'answers': [{'text': 'Reuters', 'answer_start': 10}],\n", + " 'question': 'Quale gruppo ha riferito che i pubblici ministeri cinesi sono stati coinvolti nelle indagini sui crolli della scuola?'}]},\n", + " {'context': 'Il New York Times ha riferito che \"funzionari di governo a Pechino e Sichuan hanno detto che stanno indagando sui crolli. Riconoscendo la debolezza dei codici edilizi nelle campagne, il 27 maggio la Commissione nazionale per lo sviluppo e la riforma ha dichiarato di aver elaborato un emendamento per migliorare gli standard di costruzione delle scuole elementari e medie nelle zone rurali. Gli esperti stanno rivedendo il progetto, ha detto la commissione. Per limitare le proteste, i funzionari hanno spinto i genitori a firmare un documento che vietava loro di organizzare proteste in cambio di denaro, ma alcuni che si rifiutavano di firmare sono stati minacciati. Gli importi dei pagamenti variavano da scuola a scuola, ma erano all\\' incirca gli stessi. Ad Hanwang, ai genitori è stato offerto un pacchetto del valore di 8.800 USD in contanti e una pensione per genitore di quasi 5.600 USD.',\n", + " 'qas': [{'id': '56d65a4b1c850414009470b0',\n", + " 'answers': [{'text': 'limitare le proteste', 'answer_start': 462}],\n", + " 'question': 'Cosa stanno cercando di fare i funzionari per protestare i genitori?'},\n", + " {'id': '56d65a4b1c850414009470b1',\n", + " 'answers': [{'text': 'denaro', 'answer_start': 598}],\n", + " 'question': 'Quali sono i funzionari che offrono ai genitori in cambio di proteste?'}]},\n", + " {'context': 'Oltre ai genitori, Liu Shaokun (???), un insegnante di scuola del Sichuan, è stato arrestato il 25 giugno 2008 per \"diffondere voci e distruggere l\\' ordine sociale\" sul terremoto del Sichuan. La famiglia di Liu è stato poi detto che egli è stato indagato su sospetto del reato di istigazione alla sovversione. Liu aveva viaggiato per lo Shifang, scattato foto di edifici scolastici crollati, e metterli online. Aveva anche espresso la sua rabbia per \"gli edifici scadenti tofu-dregs\" (?????) in un\\' intervista ai media. Gli è stato ordinato di servire un anno di rieducazione attraverso il lavoro (RTL). Secondo l\\' organizzazione per i diritti umani in Cina, Liu è stato rilasciato per scontare la sua condanna RTL al di fuori del campo di lavoro.',\n", + " 'qas': [{'id': '56cee14aaab44d1400b88bcb',\n", + " 'answers': [{'text': 'Liu Shaokun', 'answer_start': 19}],\n", + " 'question': 'Chi era un insegnante di scuola del Sichuan?'},\n", + " {'id': '56d65c671c850414009470ba',\n", + " 'answers': [{'text': 'reato di istigazione alla sovversione',\n", + " 'answer_start': 271}],\n", + " 'question': 'Perché è stato indagato?'},\n", + " {'id': '56d65c671c850414009470bb',\n", + " 'answers': [{'text': 'metterli online', 'answer_start': 394}],\n", + " 'question': 'Che cosa ha fatto Liu Shankun con le foto che ha scattato delle scuole crollate?'},\n", + " {'id': '56d65c671c850414009470bc',\n", + " 'answers': [{'text': \"in un' intervista ai media\", 'answer_start': 492}],\n", + " 'question': 'Dove ha definito gli edifici scolastici scadenti?'},\n", + " {'id': '56d65c671c850414009470bd',\n", + " 'answers': [{'text': 'un anno di rieducazione', 'answer_start': 552}],\n", + " 'question': 'Qual era la sua punizione assegnata?'}]},\n", + " {'context': 'Nel gennaio 2010, il giornale inglese con sede a Hong Kong The Standard ha riferito che lo scrittore Tan Zuoren ha tentato di documentare la costruzione scadente che potrebbe aver portato a massicce vittime nelle scuole, è stato condannato in prigione apparentemente per la sua scrittura di un articolo nel 2007 a sostegno del movimento pro-democrazia nel 1989.',\n", + " 'qas': [{'id': '56cee1e4aab44d1400b88be0',\n", + " 'answers': [{'text': 'nel 2007', 'answer_start': 303}],\n", + " 'question': 'Quando è avvenuta la condanna?'},\n", + " {'id': '56cee1e4aab44d1400b88be1',\n", + " 'answers': [{'text': 'gennaio 2010', 'answer_start': 4}],\n", + " 'question': \"Quando è stato pubblicato l' articolo sul caso?\"}]},\n", + " {'context': \"A causa della magnitudo del terremoto e dell' attenzione mediatica sulla Cina, le nazioni e le organizzazioni straniere hanno risposto immediatamente al disastro offrendo condoglianze e assistenza. Il 14 maggio, l' UNICEF ha riferito che la Cina ha chiesto formalmente il sostegno della comunità internazionale per rispondere ai bisogni delle famiglie colpite.\",\n", + " 'qas': [{'id': '56cee23caab44d1400b88be6',\n", + " 'answers': [{'text': 'che la Cina ha chiesto formalmente il sostegno della comunità internazionale',\n", + " 'answer_start': 234}],\n", + " 'question': \"Che cosa ha riferito l' UNICEF?\"},\n", + " {'id': '56d660e91c850414009470d3',\n", + " 'answers': [{'text': 'condoglianze e assistenza', 'answer_start': 171}],\n", + " 'question': 'Che cosa hanno offerto alla Cina le nazioni straniere a causa della gravità del terremoto?'},\n", + " {'id': '56d660e91c850414009470d4',\n", + " 'answers': [{'text': '14 maggio', 'answer_start': 201}],\n", + " 'question': 'Quando ha chiesto formalmente aiuto alla comunità internazionale?'},\n", + " {'id': '56d660e91c850414009470d5',\n", + " 'answers': [{'text': 'UNICEF', 'answer_start': 215}],\n", + " 'question': 'Quale organizzazione ha riferito che la Cina ha chiesto aiuto?'}]},\n", + " {'context': \"Entro il 14 maggio, il Ministero degli Affari civili ha dichiarato che 10,7 miliardi di yuan (circa 1,5 miliardi di dollari) era stato donato dal pubblico cinese. Houston Rockets centro Yao Ming, una delle icone sportive più popolari del paese, ha dato $214.000 e $71.000 alla Società Croce Rossa della Cina. L' associazione ha inoltre raccolto un totale di 26 milioni di dollari in donazioni finora raccolte. Anche altre multinazionali situate in Cina hanno annunciato ingenti donazioni.\",\n", + " 'qas': [{'id': '56cee294aab44d1400b88bed',\n", + " 'answers': [{'text': '26 milioni di dollari', 'answer_start': 358}],\n", + " 'question': 'Quanto ha raccolto la Croce Rossa in donazioni?'},\n", + " {'id': '56d6622d1c850414009470db',\n", + " 'answers': [{'text': '10,7 miliardi di yuan', 'answer_start': 71}],\n", + " 'question': 'Quanto denaro era stato donato entro il 14 maggio?'},\n", + " {'id': '56d6622d1c850414009470dd',\n", + " 'answers': [{'text': 'Yao Ming', 'answer_start': 186}],\n", + " 'question': 'Quale famoso giocatore di basket ha fatto due grandi donazioni alla crisi?'},\n", + " {'id': '56d6622d1c850414009470de',\n", + " 'answers': [{'text': '26 milioni', 'answer_start': 358}],\n", + " 'question': 'Quanto ha raccolto la Società della Croce Rossa?'}]},\n", + " {'context': 'Francis Marcus della Federazione Internazionale della Croce Rossa ha elogiato lo sforzo cinese di salvataggio come \"veloce e molto efficiente\" a Pechino martedì. Ma ha aggiunto che la portata del disastro era tale che \"non possiamo aspettarci che il governo possa fare tutto e gestire ogni aspetto delle necessità\". L\\' economista ha osservato che la Cina ha reagito al disastro \"rapidamente e con un\\' insolita apertura\", contrastandola con la risposta segreta della Birmania al ciclone Nargis, che ha devastato il paese 10 giorni prima del terremoto.',\n", + " 'qas': [{'id': '56d614dd1c85041400946f07',\n", + " 'answers': [{'text': 'apertura', 'answer_start': 410}],\n", + " 'question': 'Quale atteggiamento atipico ha mostrato la Cina?'},\n", + " {'id': '56d614dd1c85041400946f09',\n", + " 'answers': [{'text': '10 giorni', 'answer_start': 520}],\n", + " 'question': 'Quanto tempo prima del terremoto il ciclone Nargis colpì la Birmania?'},\n", + " {'id': '56d614dd1c85041400946f0a',\n", + " 'answers': [{'text': 'Federazione Internazionale della Croce Rossa',\n", + " 'answer_start': 21}],\n", + " 'question': 'Quale organizzazione ha rappresentato Francesco Marco?'}]},\n", + " {'context': 'Tutte le emittenti televisive della Cina continentale (insieme ad alcune stazioni di Hong Kong e delle comunità espatriate) hanno cancellato tutti i programmi programmati regolarmente, hanno mostrato il loro logo in scala di grigi e hanno sostituito i programmi cancellati con filmati dal vivo del CCTV-1 per più giorni dopo il terremoto. Anche i canali televisivi a pagamento (come il canale V) sono stati sospesi.',\n", + " 'qas': [{'id': '56d646091c8504140094705e',\n", + " 'answers': [{'text': 'CCTV-1', 'answer_start': 298}],\n", + " 'question': 'Qual è stata la fonte dei feed live?'}]},\n", + " {'context': \"Sebbene il governo cinese sia stato inizialmente elogiato per la sua risposta al terremoto (soprattutto rispetto al blocco degli aiuti da parte della giunta militare di governo del Myanmar durante il ciclone Nargis), ha poi visto un' erosione della fiducia nello scandalo della costruzione scolastica.\",\n", + " 'qas': [{'id': '56d6488e1c8504140094706f',\n", + " 'answers': [{'text': 'Myanmar', 'answer_start': 181}],\n", + " 'question': 'Quale governo ha bloccato gli aiuti dopo il ciclone Nargis?'},\n", + " {'id': '56d6488e1c85041400947070',\n", + " 'answers': [{'text': 'scandalo della costruzione scolastica',\n", + " 'answer_start': 263}],\n", + " 'question': \"Su quale scandalo ha perso nell' opinione pubblica il governo cinese?\"},\n", + " {'id': '56d6488e1c85041400947071',\n", + " 'answers': [{'text': 'risposta al terremoto', 'answer_start': 69}],\n", + " 'question': 'A cosa è stato lodato per la prima volta il governo cinese?'}]},\n", + " {'context': \"Il 29 maggio 2008, i funzionari governativi hanno iniziato a ispezionare le rovine di migliaia di scuole crollate, alla ricerca di indizi sul perché si sono sgretolate. Migliaia di genitori in tutta la provincia hanno accusato funzionari locali e costruttori di tagliare angoli nella costruzione della scuola, citando che dopo il terremoto altri edifici vicini sono stati poco danneggiati. All' indomani del terremoto, molti governi locali hanno promesso di indagare formalmente sui crolli della scuola, ma a partire dal 17 luglio 2008 in tutto il Sichuan, i genitori di bambini perduti nelle scuole crollate si sono lamentati di non aver ancora ricevuto alcuna segnalazione. Funzionari locali li ha esortati a non protestare, ma i genitori hanno dimostrato e chiesto un' indagine. Inoltre, i censori hanno scoraggiato la pubblicazione sui media di storie di scuole costruite in modo inadeguato e vi è stato un incidente in cui la polizia ha allontanato i manifestanti.\",\n", + " 'qas': []},\n", + " {'context': 'Il AP ha riferito che \"I media controllati dallo Stato ha ampiamente ignorato la questione, apparentemente sotto le istruzioni dell\\' ufficio di propaganda. Genitori e volontari che hanno interrogato le autorità sono stati detenuti e minacciati.',\n", + " 'qas': [{'id': '56d650911c8504140094709f',\n", + " 'answers': [{'text': 'media controllati dallo Stato',\n", + " 'answer_start': 25}],\n", + " 'question': 'Chi ha ignorato la questione della scuola?'},\n", + " {'id': '56d650911c850414009470a2',\n", + " 'answers': [{'text': 'Il AP', 'answer_start': 0}],\n", + " 'question': 'Quale fonte mediatica ha riferito che ciò accade?'}]},\n", + " {'context': 'Il 15 maggio 2008 Geoffery York del Globeandmail. com ha riferito che gli edifici di costruzione scadente sono comunemente chiamati \"edifici tofu\" perché i costruttori tagliano gli angoli sostituendo barre d\\' acciaio con fili di ferro sottili per il rinforzo del calcestruzzo, utilizzando cemento di qualità inferiore, se ce ne sono, e utilizzando meno mattoni di quanto dovrebbero.',\n", + " 'qas': []},\n", + " {'context': \"Rimangono tuttavia ancora delle domande, in quanto alcuni funzionari di governo corrotti non sono ancora stati consegnati alla giustizia, mentre le numerose famiglie che hanno perso l' unico figlio, cercano ancora di ottenere un risarcimento e di rendere giustizia a quanto era accaduto. Secondo il Times, molti genitori sono stati avvertiti dal governo di non mettere in scena una protesta sotto la minaccia dell' arresto.\",\n", + " 'qas': []}]}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "squad_it_dataset[\"train\"][0]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "7cd7c824", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "cc416914d0ab499bbecefcd1e952bf7d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Generating train split: 0 examples [00:00, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "25ea3a8600314d008c3f42d80558d2d9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Generating test split: 0 examples [00:00, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "DatasetDict({\n", + " train: Dataset({\n", + " features: ['title', 'paragraphs'],\n", + " num_rows: 442\n", + " })\n", + " test: Dataset({\n", + " features: ['title', 'paragraphs'],\n", + " num_rows: 48\n", + " })\n", + "})" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data_files = {\"train\": \"SQuAD_it-train.json\", \"test\": \"SQuAD_it-test.json\"}\n", + "squad_it_dataset = load_dataset(\"json\", data_files=data_files, field=\"data\")\n", + "squad_it_dataset" + ] + }, + { + "cell_type": "markdown", + "id": "a6738f0f", + "metadata": {}, + "source": [ + "### Loading a remote dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "96f79b67", + "metadata": {}, + "outputs": [], + "source": [ + "url = \"https://github.com/crux82/squad-it/raw/master/\"\n", + "data_files = {\n", + " \"train\": url + \"SQuAD_it-train.json.gz\",\n", + " \"test\": url + \"SQuAD_it-test.json.gz\",\n", + "}\n", + "squad_it_dataset = load_dataset(\"json\", data_files=data_files, field=\"data\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f690ba16", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DatasetDict({\n", + " train: Dataset({\n", + " features: ['title', 'paragraphs'],\n", + " num_rows: 442\n", + " })\n", + " test: Dataset({\n", + " features: ['title', 'paragraphs'],\n", + " num_rows: 48\n", + " })\n", + "})" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "squad_it_dataset" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/main.py b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/main.py new file mode 100644 index 0000000..5dbee95 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/main.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from hugging-face-course-learning!") + + +if __name__ == "__main__": + main() diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/pyproject.toml b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/pyproject.toml new file mode 100644 index 0000000..c2c6eb7 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "hugging-face-course-learning" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "accelerate>=1.14.0", + "datasets>=5.0.0", + "evaluate>=0.4.6", + "ipykernel>=7.3.0", + "ipywidgets>=8.1.8", + "jupyter>=1.1.1", + "nltk>=3.9.4", + "rouge-score>=0.1.2", + "sacrebleu>=2.6.0", + "scikit-learn>=1.9.0", + "seqeval>=1.2.2", + "torch>=2.12.0", + "transformers[sentencepiece,torch]>=5.12.0", +] + +[tool.pyright] +venvPath = "." +venv = ".venv" +extraPaths = ["."] +pythonVersion = "3.11" diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/pyrightconfig.json b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/pyrightconfig.json new file mode 100644 index 0000000..c0002df --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/pyrightconfig.json @@ -0,0 +1,6 @@ +{ + "venvPath": ".", + "venv": ".venv", + "extraPaths": ["."], + "pythonVersion": "3.11" +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-lib/__init__.py b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-lib/pipeline.py b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-lib/pipeline.py new file mode 100644 index 0000000..9439bec --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-lib/pipeline.py @@ -0,0 +1,8 @@ +from transformers import pipeline + +classifier = pipeline("text-classification") + +result = classifier( + ["I've been waiting for a HuggingFace course my whole life.", "I hate this so much!"] +) +print(result) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/__init__.py b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/casual_language_model.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/casual_language_model.ipynb new file mode 100644 index 0000000..03378c8 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/casual_language_model.ipynb @@ -0,0 +1,1322 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "88e0ee21", + "metadata": {}, + "source": [ + "## 加载数据集" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "aa65425f", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "eli5 = load_dataset(\"dany0407/eli5_category\", split=\"train[:5000]\")\n", + "eli5 = eli5.train_test_split(test_size=0.2)\n", + "eli5 = eli5.flatten() # 目标是 answers.text: []" + ] + }, + { + "cell_type": "markdown", + "id": "615387bb", + "metadata": {}, + "source": [ + "## 平铺数据集" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "5a588aa0", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "478e7672708c4ef68b7487bcb172c433", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map (num_proc=4): 0%| | 0/4000 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1312 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1212 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1100 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1793 > 1024). Running this sequence through the model will result in indexing errors\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "56cfcad364f74198a91906fc7928467f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map (num_proc=4): 0%| | 0/1000 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1576 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1197 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1710 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (2727 > 1024). Running this sequence through the model will result in indexing errors\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DatasetDict({\n", + " train: Dataset({\n", + " features: ['q_id', 'title', 'selftext', 'category', 'subreddit', 'answers.a_id', 'answers.text', 'answers.score', 'answers.text_urls', 'title_urls', 'selftext_urls', 'input_ids', 'attention_mask'],\n", + " num_rows: 4000\n", + " })\n", + " test: Dataset({\n", + " features: ['q_id', 'title', 'selftext', 'category', 'subreddit', 'answers.a_id', 'answers.text', 'answers.score', 'answers.text_urls', 'title_urls', 'selftext_urls', 'input_ids', 'attention_mask'],\n", + " num_rows: 1000\n", + " })\n", + "})\n", + "{\n", + " \"q_id\": \"74wntd\",\n", + " \"title\": \"Why does cold water help burns?\",\n", + " \"selftext\": \"You would intuitively think that the burn damage is irreversible and not affected by the temperature of the skin after the burn.\",\n", + " \"category\": \"Biology\",\n", + " \"subreddit\": \"explainlikeimfive\",\n", + " \"answers.a_id\": [\n", + " \"do1mrrq\",\n", + " \"do1nu7x\"\n", + " ],\n", + " \"answers.text\": [\n", + " \"There's a few reasons that cold water is recommended for burns. First, depending on the burn and what caused it, there can be ongoing damage occurring- kind of like how scrambled eggs keep cooking for a bit even after you take them out of the pan; residual heat can still be doing harm after you've moved away from the source of the damage. If you were burned by something other than direct heat, like an acid, chemical or other damaging material, the water can help remove any traces remaining, **DEPENDING ON THE CHEMICAL**. You may want to *avoid* water in some situations, but that's usually going to be in scenarios where you should be trained and wearing safety gear. Additionally, cold water helps soothe the pain by numbing it. Also, it can help reduce and prevent immediate swelling.\",\n", + " \"Have you ever heard that you should take your steak off the barbecue just a little bit before it is as rare/well done as you like it? Because the heat within the meat will continue to cook it after it has been taken off the grill. Your meat works exactly the same. The cells have so much heat that they transfer it deeper into your tissue, further damaging it. Cold water helps take that heat away before it has a chance to conduct deeper into you. Running water is used because burns are playgrounds for bacteria, and standing water collects bacteria really well, but if the water is always running out of the tap, nothing can collect in it, and thus on you.\"\n", + " ],\n", + " \"answers.score\": [\n", + " 7,\n", + " 4\n", + " ],\n", + " \"answers.text_urls\": [\n", + " [],\n", + " []\n", + " ],\n", + " \"title_urls\": [\n", + " \"url\"\n", + " ],\n", + " \"selftext_urls\": [\n", + " \"url\"\n", + " ],\n", + " \"input_ids\": [\n", + " 1858,\n", + " 338,\n", + " 257,\n", + " 1178,\n", + " 3840,\n", + " 326,\n", + " 4692,\n", + " 1660,\n", + " 318,\n", + " 7151,\n", + " 329,\n", + " 20246,\n", + " 13,\n", + " 3274,\n", + " 11,\n", + " 6906,\n", + " 319,\n", + " 262,\n", + " 4245,\n", + " 290,\n", + " 644,\n", + " 4073,\n", + " 340,\n", + " 11,\n", + " 612,\n", + " 460,\n", + " 307,\n", + " 7044,\n", + " 2465,\n", + " 14963,\n", + " 12,\n", + " 1611,\n", + " 286,\n", + " 588,\n", + " 703,\n", + " 34351,\n", + " 9653,\n", + " 1394,\n", + " 10801,\n", + " 329,\n", + " 257,\n", + " 1643,\n", + " 772,\n", + " 706,\n", + " 345,\n", + " 1011,\n", + " 606,\n", + " 503,\n", + " 286,\n", + " 262,\n", + " 3425,\n", + " 26,\n", + " 29598,\n", + " 4894,\n", + " 460,\n", + " 991,\n", + " 307,\n", + " 1804,\n", + " 4419,\n", + " 706,\n", + " 345,\n", + " 1053,\n", + " 3888,\n", + " 1497,\n", + " 422,\n", + " 262,\n", + " 2723,\n", + " 286,\n", + " 262,\n", + " 2465,\n", + " 13,\n", + " 1002,\n", + " 345,\n", + " 547,\n", + " 11544,\n", + " 416,\n", + " 1223,\n", + " 584,\n", + " 621,\n", + " 1277,\n", + " 4894,\n", + " 11,\n", + " 588,\n", + " 281,\n", + " 7408,\n", + " 11,\n", + " 5931,\n", + " 393,\n", + " 584,\n", + " 14790,\n", + " 2587,\n", + " 11,\n", + " 262,\n", + " 1660,\n", + " 460,\n", + " 1037,\n", + " 4781,\n", + " 597,\n", + " 20675,\n", + " 5637,\n", + " 11,\n", + " 12429,\n", + " 46162,\n", + " 10619,\n", + " 2751,\n", + " 6177,\n", + " 3336,\n", + " 5870,\n", + " 3620,\n", + " 20151,\n", + " 1174,\n", + " 13,\n", + " 921,\n", + " 743,\n", + " 765,\n", + " 284,\n", + " 1635,\n", + " 27080,\n", + " 9,\n", + " 1660,\n", + " 287,\n", + " 617,\n", + " 7445,\n", + " 11,\n", + " 475,\n", + " 326,\n", + " 338,\n", + " 3221,\n", + " 1016,\n", + " 284,\n", + " 307,\n", + " 287,\n", + " 13858,\n", + " 810,\n", + " 345,\n", + " 815,\n", + " 307,\n", + " 8776,\n", + " 290,\n", + " 5762,\n", + " 3747,\n", + " 7733,\n", + " 13,\n", + " 12032,\n", + " 11,\n", + " 4692,\n", + " 1660,\n", + " 5419,\n", + " 523,\n", + " 20388,\n", + " 262,\n", + " 2356,\n", + " 416,\n", + " 997,\n", + " 4623,\n", + " 340,\n", + " 13,\n", + " 4418,\n", + " 11,\n", + " 340,\n", + " 460,\n", + " 1037,\n", + " 4646,\n", + " 290,\n", + " 2948,\n", + " 7103,\n", + " 29844,\n", + " 13,\n", + " 8192,\n", + " 345,\n", + " 1683,\n", + " 2982,\n", + " 326,\n", + " 345,\n", + " 815,\n", + " 1011,\n", + " 534,\n", + " 26320,\n", + " 572,\n", + " 262,\n", + " 36646,\n", + " 655,\n", + " 257,\n", + " 1310,\n", + " 1643,\n", + " 878,\n", + " 340,\n", + " 318,\n", + " 355,\n", + " 4071,\n", + " 14,\n", + " 4053,\n", + " 1760,\n", + " 355,\n", + " 345,\n", + " 588,\n", + " 340,\n", + " 30,\n", + " 4362,\n", + " 262,\n", + " 4894,\n", + " 1626,\n", + " 262,\n", + " 6174,\n", + " 481,\n", + " 2555,\n", + " 284,\n", + " 4255,\n", + " 340,\n", + " 706,\n", + " 340,\n", + " 468,\n", + " 587,\n", + " 2077,\n", + " 572,\n", + " 262,\n", + " 29901,\n", + " 13,\n", + " 3406,\n", + " 6174,\n", + " 2499,\n", + " 3446,\n", + " 262,\n", + " 976,\n", + " 13,\n", + " 383,\n", + " 4778,\n", + " 423,\n", + " 523,\n", + " 881,\n", + " 4894,\n", + " 326,\n", + " 484,\n", + " 4351,\n", + " 340,\n", + " 9211,\n", + " 656,\n", + " 534,\n", + " 10712,\n", + " 11,\n", + " 2252,\n", + " 14790,\n", + " 340,\n", + " 13,\n", + " 10250,\n", + " 1660,\n", + " 5419,\n", + " 1011,\n", + " 326,\n", + " 4894,\n", + " 1497,\n", + " 878,\n", + " 340,\n", + " 468,\n", + " 257,\n", + " 2863,\n", + " 284,\n", + " 3189,\n", + " 9211,\n", + " 656,\n", + " 345,\n", + " 13,\n", + " 18162,\n", + " 1660,\n", + " 318,\n", + " 973,\n", + " 780,\n", + " 20246,\n", + " 389,\n", + " 24817,\n", + " 82,\n", + " 329,\n", + " 11492,\n", + " 11,\n", + " 290,\n", + " 5055,\n", + " 1660,\n", + " 26609,\n", + " 11492,\n", + " 1107,\n", + " 880,\n", + " 11,\n", + " 475,\n", + " 611,\n", + " 262,\n", + " 1660,\n", + " 318,\n", + " 1464,\n", + " 2491,\n", + " 503,\n", + " 286,\n", + " 262,\n", + " 9814,\n", + " 11,\n", + " 2147,\n", + " 460,\n", + " 2824,\n", + " 287,\n", + " 340,\n", + " 11,\n", + " 290,\n", + " 4145,\n", + " 319,\n", + " 345,\n", + " 13\n", + " ],\n", + " \"attention_mask\": [\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1\n", + " ]\n", + "}\n" + ] + } + ], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(\"distilbert/distilgpt2\")\n", + "def preprocess_function(examples):\n", + " return tokenizer([\" \".join(x) for x in examples[\"answers.text\"]])\n", + "\n", + "tokenized_eli5 = eli5.map(preprocess_function, batched=True, num_proc=4)\n", + "print(tokenized_eli5)\n", + "\n", + "import json\n", + "print(json.dumps(tokenized_eli5[\"train\"][0], ensure_ascii=False, indent=2))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "7953c784", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b6d3d7056f734a3eb3dfbb4da073656e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map (num_proc=4): 0%| | 0/4000 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1312 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1212 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1100 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1793 > 1024). Running this sequence through the model will result in indexing errors\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3b0ce46507564b87a0465d13c4f9cca7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map (num_proc=4): 0%| | 0/1000 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1576 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1197 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (1710 > 1024). Running this sequence through the model will result in indexing errors\n", + "[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (2727 > 1024). Running this sequence through the model will result in indexing errors\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'input_ids': [1858, 338, 257, 1178, 3840, 326, 4692, 1660, 318, 7151, 329, 20246, 13, 3274, 11, 6906, 319, 262, 4245, 290, 644, 4073, 340, 11, 612, 460, 307, 7044, 2465, 14963, 12, 1611, 286, 588, 703, 34351, 9653, 1394, 10801, 329, 257, 1643, 772, 706, 345, 1011, 606, 503, 286, 262, 3425, 26, 29598, 4894, 460, 991, 307, 1804, 4419, 706, 345, 1053, 3888, 1497, 422, 262, 2723, 286, 262, 2465, 13, 1002, 345, 547, 11544, 416, 1223, 584, 621, 1277, 4894, 11, 588, 281, 7408, 11, 5931, 393, 584, 14790, 2587, 11, 262, 1660, 460, 1037, 4781, 597, 20675, 5637, 11, 12429, 46162, 10619, 2751, 6177, 3336, 5870, 3620, 20151, 1174, 13, 921, 743, 765, 284, 1635, 27080, 9, 1660, 287, 617, 7445, 11, 475, 326, 338, 3221, 1016, 284, 307, 287, 13858, 810, 345, 815, 307, 8776, 290, 5762, 3747, 7733, 13, 12032, 11, 4692, 1660, 5419, 523, 20388, 262, 2356, 416, 997, 4623, 340, 13, 4418, 11, 340, 460, 1037, 4646, 290, 2948, 7103, 29844, 13, 8192, 345, 1683, 2982, 326, 345, 815, 1011, 534, 26320, 572, 262, 36646, 655, 257, 1310, 1643, 878, 340, 318, 355, 4071, 14, 4053, 1760, 355, 345, 588, 340, 30, 4362, 262, 4894, 1626, 262, 6174, 481, 2555, 284, 4255, 340, 706, 340, 468, 587, 2077, 572, 262, 29901, 13, 3406, 6174, 2499, 3446, 262, 976, 13, 383, 4778, 423, 523, 881, 4894, 326, 484, 4351, 340, 9211, 656, 534, 10712, 11, 2252, 14790, 340, 13, 10250, 1660, 5419, 1011, 326, 4894, 1497, 878, 340, 468, 257, 2863, 284, 3189, 9211, 656, 345, 13, 18162, 1660, 318, 973, 780, 20246, 389, 24817, 82, 329, 11492, 11, 290, 5055, 1660, 26609, 11492, 1107, 880, 11, 475, 611, 262, 1660, 318, 1464, 2491, 503, 286, 262, 9814, 11, 2147, 460, 2824, 287, 340, 11, 290, 4145, 319, 345, 13], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}\n" + ] + } + ], + "source": [ + "tokenized_eli5 = eli5.map(preprocess_function, batched=True, num_proc=4, remove_columns=eli5[\"train\"].column_names)\n", + "print(tokenized_eli5[\"train\"][0])" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a16ce402", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There's a few reasons that cold water is recommended for burns. First, depending on the burn and what caused it, there can be ongoing damage occurring- kind of like how scrambled eggs keep cooking for a bit even after you take them out of the pan; residual heat can still be doing harm after you've moved away from the source of the damage. If you were burned by something other than direct heat, like an acid, chemical or other damaging material, the water can help remove any traces remaining, **DEPENDING ON THE CHEMICAL**. You may want to *avoid* water in some situations, but that's usually going to be in scenarios where you should be trained and wearing safety gear. Additionally, cold water helps soothe the pain by numbing it. Also, it can help reduce and prevent immediate swelling. Have you ever heard that you should take your steak off the barbecue just a little bit before it is as rare/well done as you like it? Because the heat within the meat will continue to cook it after it has been taken off the grill. Your meat works exactly the same. The cells have so much heat that they transfer it deeper into your tissue, further damaging it. Cold water helps take that heat away before it has a chance to conduct deeper into you. Running water is used because burns are playgrounds for bacteria, and standing water collects bacteria really well, but if the water is always running out of the tap, nothing can collect in it, and thus on you.\n" + ] + } + ], + "source": [ + "input_text = tokenizer.decode(tokenized_eli5[\"train\"][0][\"input_ids\"])\n", + "print(input_text)" + ] + }, + { + "cell_type": "markdown", + "id": "277319ca", + "metadata": {}, + "source": [ + "## 数据集分块" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "bcc0c267", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "735c8f4c4e2d4f67857a1c4219b107a8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map (num_proc=4): 0%| | 0/4000 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "77ea847ac7bf40eba0bab640df411cdd", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map (num_proc=4): 0%| | 0/1000 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'input_ids': [1858, 338, 257, 1178, 3840, 326, 4692, 1660, 318, 7151, 329, 20246, 13, 3274, 11, 6906, 319, 262, 4245, 290, 644, 4073, 340, 11, 612, 460, 307, 7044, 2465, 14963, 12, 1611, 286, 588, 703, 34351, 9653, 1394, 10801, 329, 257, 1643, 772, 706, 345, 1011, 606, 503, 286, 262, 3425, 26, 29598, 4894, 460, 991, 307, 1804, 4419, 706, 345, 1053, 3888, 1497, 422, 262, 2723, 286, 262, 2465, 13, 1002, 345, 547, 11544, 416, 1223, 584, 621, 1277, 4894, 11, 588, 281, 7408, 11, 5931, 393, 584, 14790, 2587, 11, 262, 1660, 460, 1037, 4781, 597, 20675, 5637, 11, 12429, 46162, 10619, 2751, 6177, 3336, 5870, 3620, 20151, 1174, 13, 921, 743, 765, 284, 1635, 27080, 9, 1660, 287, 617, 7445, 11, 475, 326, 338, 3221], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'labels': [1858, 338, 257, 1178, 3840, 326, 4692, 1660, 318, 7151, 329, 20246, 13, 3274, 11, 6906, 319, 262, 4245, 290, 644, 4073, 340, 11, 612, 460, 307, 7044, 2465, 14963, 12, 1611, 286, 588, 703, 34351, 9653, 1394, 10801, 329, 257, 1643, 772, 706, 345, 1011, 606, 503, 286, 262, 3425, 26, 29598, 4894, 460, 991, 307, 1804, 4419, 706, 345, 1053, 3888, 1497, 422, 262, 2723, 286, 262, 2465, 13, 1002, 345, 547, 11544, 416, 1223, 584, 621, 1277, 4894, 11, 588, 281, 7408, 11, 5931, 393, 584, 14790, 2587, 11, 262, 1660, 460, 1037, 4781, 597, 20675, 5637, 11, 12429, 46162, 10619, 2751, 6177, 3336, 5870, 3620, 20151, 1174, 13, 921, 743, 765, 284, 1635, 27080, 9, 1660, 287, 617, 7445, 11, 475, 326, 338, 3221]}\n", + "input_ids= [1858, 338, 257, 1178, 3840, 326, 4692, 1660, 318, 7151, 329, 20246, 13, 3274, 11, 6906, 319, 262, 4245, 290, 644, 4073, 340, 11, 612, 460, 307, 7044, 2465, 14963, 12, 1611, 286, 588, 703, 34351, 9653, 1394, 10801, 329, 257, 1643, 772, 706, 345, 1011, 606, 503, 286, 262, 3425, 26, 29598, 4894, 460, 991, 307, 1804, 4419, 706, 345, 1053, 3888, 1497, 422, 262, 2723, 286, 262, 2465, 13, 1002, 345, 547, 11544, 416, 1223, 584, 621, 1277, 4894, 11, 588, 281, 7408, 11, 5931, 393, 584, 14790, 2587, 11, 262, 1660, 460, 1037, 4781, 597, 20675, 5637, 11, 12429, 46162, 10619, 2751, 6177, 3336, 5870, 3620, 20151, 1174, 13, 921, 743, 765, 284, 1635, 27080, 9, 1660, 287, 617, 7445, 11, 475, 326, 338, 3221]\n", + "input_ids len= 128\n", + "labels= [1858, 338, 257, 1178, 3840, 326, 4692, 1660, 318, 7151, 329, 20246, 13, 3274, 11, 6906, 319, 262, 4245, 290, 644, 4073, 340, 11, 612, 460, 307, 7044, 2465, 14963, 12, 1611, 286, 588, 703, 34351, 9653, 1394, 10801, 329, 257, 1643, 772, 706, 345, 1011, 606, 503, 286, 262, 3425, 26, 29598, 4894, 460, 991, 307, 1804, 4419, 706, 345, 1053, 3888, 1497, 422, 262, 2723, 286, 262, 2465, 13, 1002, 345, 547, 11544, 416, 1223, 584, 621, 1277, 4894, 11, 588, 281, 7408, 11, 5931, 393, 584, 14790, 2587, 11, 262, 1660, 460, 1037, 4781, 597, 20675, 5637, 11, 12429, 46162, 10619, 2751, 6177, 3336, 5870, 3620, 20151, 1174, 13, 921, 743, 765, 284, 1635, 27080, 9, 1660, 287, 617, 7445, 11, 475, 326, 338, 3221]\n", + "labels len= 128\n", + "input_ids==labels? True\n" + ] + } + ], + "source": [ + "block_size = 128\n", + "\n", + "def group_texts(examples):\n", + " concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}\n", + " total_length = len(concatenated_examples[list(examples.keys())[0]])\n", + " if total_length >= block_size:\n", + " total_length = (total_length // block_size) * block_size\n", + " \n", + " result = {\n", + " k: [t[i: i + block_size] for i in range(0, total_length, block_size)]\n", + " for k, t in concatenated_examples.items()\n", + " }\n", + " result[\"labels\"] = result[\"input_ids\"].copy()\n", + " return result\n", + "\n", + "lm_dataset = tokenized_eli5.map(group_texts, batched=True, num_proc=4)\n", + "print(lm_dataset[\"train\"][0], )\n", + "print(\"input_ids=\", lm_dataset[\"train\"][0][\"input_ids\"])\n", + "print(\"input_ids len=\", len(lm_dataset[\"train\"][0][\"input_ids\"]))\n", + "print(\"labels=\", lm_dataset[\"train\"][0][\"labels\"])\n", + "print(\"labels len=\", len(lm_dataset[\"train\"][0][\"labels\"]))\n", + "\n", + "print(\"input_ids==labels? \", lm_dataset[\"train\"][0][\"input_ids\"] == lm_dataset[\"train\"][0][\"labels\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "c4958e7b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "text= There's a few reasons that cold water is recommended for burns. First, depending on the burn and what caused it, there can be ongoing damage occurring- kind of like how scrambled eggs keep cooking for a bit even after you take them out of the pan; residual heat can still be doing harm after you've moved away from the source of the damage. If you were burned by something other than direct heat, like an acid, chemical or other damaging material, the water can help remove any traces remaining, **DEPENDING ON THE CHEMICAL**. You may want to *avoid* water in some situations, but that's usually\n" + ] + } + ], + "source": [ + "text = tokenizer.decode(lm_dataset[\"train\"][0][\"input_ids\"])\n", + "print(\"text=\", text)" + ] + }, + { + "cell_type": "markdown", + "id": "432e39ff", + "metadata": {}, + "source": [ + "## 将数据集转换为模型输入" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "2e8ab86e", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import DataCollatorForLanguageModeling\n", + "\n", + "# GPT-2 / DistilGPT2 有个特点:它原本没有专门的 pad_token\n", + "# 设置 pad_token 和 eos_token 相同\n", + "# 如果 batch 里有样本需要补齐长度,就用 eos_token 当作 padding token\n", + "tokenizer.pad_token = tokenizer.eos_token # eos = end of sequence\n", + "data_collator = DataCollatorForLanguageModeling(\n", + " tokenizer=tokenizer,\n", + " mlm=False # masked language modeling, true=BERT-style, false=GPT-2-style\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "f87b4438", + "metadata": {}, + "source": [ + "## 训练" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "e9ea1083", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "50b9dec2562348fcb58bf649a7da6f17", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/76 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] The tokenizer has new PAD/BOS/EOS tokens that differ from the model config and generation config. The model config and generation config were aligned accordingly, being updated with the tokenizer's values. Updated tokens: {'pad_token_id': 50256}.\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='200' max='200' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [200/200 02:20, Epoch 0/1]\n", + " </div>\n", + " <table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Step</th>\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>50</td>\n", + " <td>4.011947</td>\n", + " <td>3.894948</td>\n", + " </tr>\n", + " <tr>\n", + " <td>100</td>\n", + " <td>3.981206</td>\n", + " <td>3.878609</td>\n", + " </tr>\n", + " <tr>\n", + " <td>150</td>\n", + " <td>3.944783</td>\n", + " <td>3.869316</td>\n", + " </tr>\n", + " <tr>\n", + " <td>200</td>\n", + " <td>3.949249</td>\n", + " <td>3.867325</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "TrainOutput(global_step=200, training_loss=3.9759389686584474, metrics={'train_runtime': 140.8586, 'train_samples_per_second': 22.718, 'train_steps_per_second': 1.42, 'total_flos': 104518700236800.0, 'train_loss': 3.9759389686584474, 'epoch': 0.3058103975535168})" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from transformers import AutoModelForCausalLM, TrainingArguments, Trainer\n", + "\n", + "model = AutoModelForCausalLM.from_pretrained(\"distilbert/distilgpt2\")\n", + "\n", + "training_args = TrainingArguments(\n", + " output_dir=\"my_awesome_eli5_clm-model\",\n", + " max_steps=200, # 先别完整 3 epoch\n", + " per_device_train_batch_size=16, # 可以试 16,看内存\n", + " eval_strategy=\"steps\",\n", + " eval_steps=50,\n", + " save_strategy=\"no\",\n", + " logging_steps=10,\n", + " learning_rate=2e-5,\n", + " weight_decay=0.01,\n", + " push_to_hub=False,\n", + " dataloader_pin_memory=False, # 消掉 MPS pin_memory warning\n", + ")\n", + "\n", + "trainer = Trainer(\n", + " model=model,\n", + " args=training_args,\n", + " train_dataset=lm_dataset[\"train\"],\n", + " eval_dataset=lm_dataset[\"test\"],\n", + " data_collator=data_collator,\n", + " processing_class=tokenizer,\n", + ")\n", + "\n", + "trainer.train()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "90cda8a7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='328' max='328' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [328/328 00:11]\n", + " </div>\n", + " " + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " <th>Step</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>3.949249</td>\n", + " <td>3.867325</td>\n", + " <td>200</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Perplexity: 47.81\n" + ] + } + ], + "source": [ + "import math\n", + "\n", + "eval_results = trainer.evaluate()\n", + "print(f\"Perplexity: {math.exp(eval_results['eval_loss']):.2f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "cf1c75d9", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ce524306db8b4502b7b0d836077d085b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1e0e109212e240a29aa0aea3c6fa9771", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Processing Files (0 / 0): | | 0.00B / 0.00B " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1be361b414864911806eb2e1b28b814b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "New Data Upload: | | 0.00B / 0.00B " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "CommitInfo(commit_url='https://huggingface.co/hedonwang/my_awesome_eli5_clm-model/commit/147c2fc05c36740c96eea2ab572ee1c4c3cece70', commit_message='End of training', commit_description='', oid='147c2fc05c36740c96eea2ab572ee1c4c3cece70', pr_url=None, repo_url=RepoUrl('https://huggingface.co/hedonwang/my_awesome_eli5_clm-model', endpoint='https://huggingface.co', repo_type='model', repo_id='hedonwang/my_awesome_eli5_clm-model'), pr_revision=None, pr_num=None)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "trainer.push_to_hub()" + ] + }, + { + "cell_type": "markdown", + "id": "58e4b124", + "metadata": {}, + "source": [ + "## 推理" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "c84425cf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] Both `max_new_tokens` (=256) and `max_length`(=50) seem to have been set. `max_new_tokens` will take precedence. Please refer to the documentation for more information. (https://huggingface.co/docs/transformers/main/en/main_classes/text_generation)\n", + "[transformers] Ignoring clean_up_tokenization_spaces=True for BPE tokenizer GPT2Tokenizer. The clean_up_tokenization post-processing step is designed for WordPiece tokenizers and is destructive for BPE (it strips spaces before punctuation). Set clean_up_tokenization_spaces=False to suppress this warning, or set clean_up_tokenization_spaces_for_bpe_even_though_it_will_corrupt_output=True to force cleanup anyway.\n" + ] + }, + { + "data": { + "text/plain": [ + "[{'generated_text': \"Somatic hypermutation allows the immune system to absorb the damage it causes. As it happens, our immune system is not constantly responding to threats, like a virus or bacteria or something like that. And the immune system is always acting as it does. So, if you're a fan of the original NES, you can make some more money. It's a little bit like the original NES. So, you can make some more money. The reason is because the NES doesn't have a Super Nintendo console. It's just a console, which does not have a Super Nintendo console. It's a console that does not have a Super Nintendo console. It's not used to do things like that. It does not have a Super Nintendo console. It's just a console that does not have a Super Nintendo console. It's just a console that does not have a Super Nintendo console. It's just a console that does not have a Super Nintendo console. It's just a console that does not have a Super Nintendo console. It's just a console that does not have a Super Nintendo console. It's just a console that simply does not have a Super Nintendo console. It's just a console that does not have a Super Nintendo console. It's just a console that does not have a Super Nintendo console. It's just\"}]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "prompt = \"Somatic hypermutation allows the immune system to\"\n", + "\n", + "from transformers import pipeline\n", + "\n", + "generator = pipeline(\"text-generation\", model=model, tokenizer=tokenizer)\n", + "\n", + "generator(prompt)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31679700", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9bd366a77a434e01affb2dae5452d727", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "model.safetensors: 0%| | 0.00/328M [00:00<?, ?B/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from transformers import AutoModelForCausalLM\n", + "\n", + "model = AutoModelForCausalLM.from_pretrained(\"hedonwang/my_awesome_eli5_clm-model\")\n", + "outputs = model.generate(inputs, max_new_tokens=100, do_sample=True, top_k=50, top_p=0.95)\n", + "tokenizer.batch_decode(outputs, skip_special_tokens=True)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/question_answering.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/question_answering.ipynb new file mode 100644 index 0000000..7a53653 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/question_answering.ipynb @@ -0,0 +1,554 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 41, + "id": "381f16fd", + "metadata": {}, + "outputs": [], + "source": [ + "from huggingface_hub import notebook_login\n", + "\n", + "notebook_login()" + ] + }, + { + "cell_type": "markdown", + "id": "a040431a", + "metadata": {}, + "source": [ + "### Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "a1087599", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "squad_raw = load_dataset(\"rajpurkar/squad\", split=\"train[:5000]\")" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "3e012f5d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': '56ce362faab44d1400b885c7',\n", + " 'title': 'Sino-Tibetan_relations_during_the_Ming_dynasty',\n", + " 'context': \"With the death of Zhengde and ascension of Jiajing, the politics at court shifted in favor of the Neo-Confucian establishment which not only rejected the Portuguese embassy of Fernão Pires de Andrade (d. 1523), but had a predisposed animosity towards Tibetan Buddhism and lamas. Evelyn S. Rawski, a professor in the Department of History of the University of Pittsburgh, writes that the Ming's unique relationship with Tibetan prelates essentially ended with Jiajing's reign while Ming influence in the Amdo region was supplanted by the Mongols.\",\n", + " 'question': 'What establishment did the politics at court shift in favor of? ',\n", + " 'answers': {'text': ['Neo-Confucian establishment'], 'answer_start': [98]}}" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "squad = squad_raw.train_test_split(test_size=0.2)\n", + "squad[\"train\"][0]" + ] + }, + { + "cell_type": "markdown", + "id": "e2ba7479", + "metadata": {}, + "source": [ + "### Tokenizer" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "e3a3cbcd", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(\"distilbert/distilbert-base-uncased\")" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "75ca1b8b", + "metadata": {}, + "outputs": [], + "source": [ + "def preprocess_function(examples):\n", + " questions = [q.strip() for q in examples[\"question\"]]\n", + " inputs = tokenizer(\n", + " questions,\n", + " examples[\"context\"],\n", + " max_length=384,\n", + " truncation=\"only_second\",\n", + " return_offsets_mapping=True,\n", + " padding=\"max_length\",\n", + " )\n", + " \n", + " offset_mapping = inputs.pop(\"offset_mapping\")\n", + " answers = examples[\"answers\"]\n", + " start_positions = []\n", + " end_positions = []\n", + " \n", + " for i, offset in enumerate(offset_mapping):\n", + " answer = answers[i]\n", + " start_char = answer[\"answer_start\"][0]\n", + " end_char = answer[\"answer_start\"][0] + len(answer[\"text\"][0])\n", + " sequence_ids = inputs.sequence_ids(i)\n", + "\n", + " # Find the start and end of the answer in the text\n", + " idx = 0\n", + " while sequence_ids[idx] != 1:\n", + " idx += 1\n", + " context_start = idx\n", + " while sequence_ids[idx] == 1:\n", + " idx += 1\n", + " context_end = idx - 1\n", + " \n", + " # If the answer is not fully inside a token, label it (0, 0)\n", + " if offset[context_start][0] > end_char or offset[context_end][1] < start_char:\n", + " start_positions.append(0)\n", + " end_positions.append(0)\n", + " else:\n", + " # Otherwise it's the start and end token positions\n", + " idx = context_start\n", + " while idx <= context_end and offset[idx][0] <= start_char:\n", + " idx += 1\n", + " start_positions.append(idx - 1)\n", + " \n", + " idx = context_end\n", + " while idx >= context_start and offset[idx][1] >= end_char:\n", + " idx -= 1\n", + " end_positions.append(idx + 1)\n", + " \n", + " inputs[\"start_positions\"] = start_positions\n", + " inputs[\"end_positions\"] = end_positions\n", + " return inputs" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "fdb94cae", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "dfed65976c534c99b16a8b3d3939ddd3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/4000 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "66e7db8befe64d399dd252fd29de1f3b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/1000 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tokenized_squad = squad.map(preprocess_function, batched=True, remove_columns=squad[\"train\"].column_names)" + ] + }, + { + "cell_type": "markdown", + "id": "d4d0377d", + "metadata": {}, + "source": [ + "### DataCollator" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "d0253797", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import DefaultDataCollator\n", + "\n", + "data_collator = DefaultDataCollator()" + ] + }, + { + "cell_type": "markdown", + "id": "697cc47a", + "metadata": {}, + "source": [ + "### Train" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "d4053acd", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9d8831d5f14248af8f97713ed1222c92", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/100 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mDistilBertForQuestionAnswering LOAD REPORT\u001b[0m from: distilbert/distilbert-base-uncased\n", + "Key | Status | \n", + "------------------------+------------+-\n", + "vocab_transform.weight | UNEXPECTED | \n", + "vocab_transform.bias | UNEXPECTED | \n", + "vocab_layer_norm.weight | UNEXPECTED | \n", + "vocab_projector.bias | UNEXPECTED | \n", + "vocab_layer_norm.bias | UNEXPECTED | \n", + "qa_outputs.bias | MISSING | \n", + "qa_outputs.weight | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + } + ], + "source": [ + "from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer\n", + "\n", + "model = AutoModelForQuestionAnswering.from_pretrained(\"distilbert/distilbert-base-uncased\")" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "a57a91cf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='750' max='750' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [750/750 07:16, Epoch 3/3]\n", + " </div>\n", + " <table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Epoch</th>\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>1</td>\n", + " <td>No log</td>\n", + " <td>2.519198</td>\n", + " </tr>\n", + " <tr>\n", + " <td>2</td>\n", + " <td>2.784180</td>\n", + " <td>1.809715</td>\n", + " </tr>\n", + " <tr>\n", + " <td>3</td>\n", + " <td>2.784180</td>\n", + " <td>1.717561</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9b5d09ca190c405cb5a847870d084d61", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "57675eb303ed4923b8611ca1470448c4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "text/plain": [ + "TrainOutput(global_step=750, training_loss=2.3368919677734374, metrics={'train_runtime': 437.0768, 'train_samples_per_second': 27.455, 'train_steps_per_second': 1.716, 'total_flos': 1175877900288000.0, 'train_loss': 2.3368919677734374, 'epoch': 3.0})" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "training_args = TrainingArguments(\n", + " output_dir=\"my_awesome_qa_model\",\n", + " eval_strategy=\"epoch\",\n", + " learning_rate=2e-5,\n", + " per_device_train_batch_size=16,\n", + " per_device_eval_batch_size=16,\n", + " num_train_epochs=3,\n", + " weight_decay=0.01,\n", + " push_to_hub=True,\n", + ")\n", + "\n", + "trainer = Trainer(\n", + " model=model,\n", + " args=training_args,\n", + " train_dataset=tokenized_squad[\"train\"],\n", + " eval_dataset=tokenized_squad[\"test\"],\n", + " data_collator=data_collator,\n", + " processing_class=tokenizer,\n", + ")\n", + "\n", + "trainer.train()" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "5c00c38e", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e3e286c186b44b91bea6160999605e5a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f248a8dd622347b79b76dbff19566578", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Processing Files (0 / 0): | | 0.00B / 0.00B " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1c2ef7bf62094f4289648a0244c815b7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "New Data Upload: | | 0.00B / 0.00B " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "CommitInfo(commit_url='https://huggingface.co/hedonwang/my_awesome_qa_model/commit/37821e3b3862de2ea5c6c713fb8e4e5cb6e7eab5', commit_message='End of training', commit_description='', oid='37821e3b3862de2ea5c6c713fb8e4e5cb6e7eab5', pr_url=None, repo_url=RepoUrl('https://huggingface.co/hedonwang/my_awesome_qa_model', endpoint='https://huggingface.co', repo_type='model', repo_id='hedonwang/my_awesome_qa_model'), pr_revision=None, pr_num=None)" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "trainer.push_to_hub()" + ] + }, + { + "cell_type": "markdown", + "id": "7916ed25", + "metadata": {}, + "source": [ + "### Inference" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "4b5f11b4", + "metadata": {}, + "outputs": [], + "source": [ + "question = \"How many programming languages does BLOOM support?\"\n", + "context = \"BLOOM has 176 billion parameters and can generate text in 46 languages natural languages and 13 programming languages.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "faba570f", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(\"my_awesome_qa_model\")\n", + "inputs = tokenizer(question, context, return_tensors=\"pt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "3d6f347c", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c8009dfeaf19462490b4a1c5ab17f57c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/102 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "''" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import torch\n", + "from transformers import AutoModelForQuestionAnswering\n", + "\n", + "model = AutoModelForQuestionAnswering.from_pretrained(\"my_awesome_qa_model\")\n", + "\n", + "with torch.no_grad():\n", + " outputs = model(**inputs)\n", + "\n", + "answer_start_index = outputs.start_logits.argmax()\n", + "answer_end_index = outputs.end_logits.argmax()\n", + "\n", + "predict_answer_tokens = inputs.input_ids[0, answer_start_index: answer_end_index + 1]\n", + "tokenizer.decode(predict_answer_tokens)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/sequence_classification.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/sequence_classification.ipynb new file mode 100644 index 0000000..5917c31 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/sequence_classification.ipynb @@ -0,0 +1,409 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d9c5e47b", + "metadata": {}, + "source": [ + "## Sequence Classification" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3e53f3e9", + "metadata": {}, + "outputs": [], + "source": [ + "from huggingface_hub import notebook_login\n", + "\n", + "notebook_login()" + ] + }, + { + "cell_type": "markdown", + "id": "70e3df4f", + "metadata": {}, + "source": [ + "### Load the dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "23f45b1d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'text': 'I love sci-fi and am willing to put up with a lot. Sci-fi movies/TV are usually underfunded, under-appreciated and misunderstood. I tried to like this, I really did, but it is to good TV sci-fi as Babylon 5 is to Star Trek (the original). Silly prosthetics, cheap cardboard sets, stilted dialogues, CG that doesn\\'t match the background, and painfully one-dimensional characters cannot be overcome with a \\'sci-fi\\' setting. (I\\'m sure there are those of you out there who think Babylon 5 is good sci-fi TV. It\\'s not. It\\'s clichéd and uninspiring.) While US viewers might like emotion and character development, sci-fi is a genre that does not take itself seriously (cf. Star Trek). It may treat important issues, yet not as a serious philosophy. It\\'s really difficult to care about the characters here as they are not simply foolish, just missing a spark of life. Their actions and reactions are wooden and predictable, often painful to watch. The makers of Earth KNOW it\\'s rubbish as they have to always say \"Gene Roddenberry\\'s Earth...\" otherwise people would not continue watching. Roddenberry\\'s ashes must be turning in their orbit as this dull, cheap, poorly edited (watching it without advert breaks really brings this home) trudging Trabant of a show lumbers into space. Spoiler. So, kill off a main character. And then bring him back as another actor. Jeeez! Dallas all over again.',\n", + " 'label': 0}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datasets import load_dataset\n", + "\n", + "imdb = load_dataset(\"stanfordnlp/imdb\")\n", + "imdb[\"test\"][0]" + ] + }, + { + "cell_type": "markdown", + "id": "9f9d5ad0", + "metadata": {}, + "source": [ + "### Preprocess" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "25b645d6", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(\"distilbert/distilbert-base-uncased\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3a9980df", + "metadata": {}, + "outputs": [], + "source": [ + "def preprocess_function(examples):\n", + " return tokenizer(examples[\"text\"], truncation=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "426f9169", + "metadata": {}, + "outputs": [], + "source": [ + "tokenized_imdb = imdb.map(preprocess_function, batched=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b80ab9cc", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import DataCollatorWithPadding\n", + "\n", + "data_collator = DataCollatorWithPadding(tokenizer=tokenizer)" + ] + }, + { + "cell_type": "markdown", + "id": "dc1c07c8", + "metadata": {}, + "source": [ + "### Evaluate" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4a2df512", + "metadata": {}, + "outputs": [], + "source": [ + "import evaluate\n", + "\n", + "accuracy = evaluate.load(\"accuracy\")\n", + "\n", + "import numpy as np\n", + "\n", + "def compute_metrics(eval_pred):\n", + " predictions, labels= eval_pred\n", + " predictions = np.argmax(predictions, axis=1)\n", + " return accuracy.compute(predictions=predictions, references=labels)" + ] + }, + { + "cell_type": "markdown", + "id": "9afbf3c3", + "metadata": {}, + "source": [ + "### Train" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e3dc23e8", + "metadata": {}, + "outputs": [], + "source": [ + "id2label = {0: \"NEGATIVE\", 1: \"POSITIVE\"}\n", + "label2id = {\"NEGATIVE\": 0, \"POSITIVE\": 1}" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "95001665", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "16f43b5a99f84536bf838f5bae287832", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/100 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mDistilBertForSequenceClassification LOAD REPORT\u001b[0m from: distilbert/distilbert-base-uncased\n", + "Key | Status | \n", + "------------------------+------------+-\n", + "vocab_layer_norm.bias | UNEXPECTED | \n", + "vocab_projector.bias | UNEXPECTED | \n", + "vocab_transform.bias | UNEXPECTED | \n", + "vocab_transform.weight | UNEXPECTED | \n", + "vocab_layer_norm.weight | UNEXPECTED | \n", + "classifier.bias | MISSING | \n", + "pre_classifier.weight | MISSING | \n", + "pre_classifier.bias | MISSING | \n", + "classifier.weight | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='50' max='50' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [50/50 08:36, Epoch 0/1]\n", + " </div>\n", + " <table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Step</th>\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " <th>Accuracy</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>25</td>\n", + " <td>0.670536</td>\n", + " <td>0.611853</td>\n", + " <td>0.817760</td>\n", + " </tr>\n", + " <tr>\n", + " <td>50</td>\n", + " <td>0.528012</td>\n", + " <td>0.483153</td>\n", + " <td>0.853840</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "TrainOutput(global_step=50, training_loss=0.6083996391296387, metrics={'train_runtime': 517.1128, 'train_samples_per_second': 1.547, 'train_steps_per_second': 0.097, 'total_flos': 105518562241920.0, 'train_loss': 0.6083996391296387, 'epoch': 0.03198976327575176})" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer\n", + "\n", + "model = AutoModelForSequenceClassification.from_pretrained(\"distilbert/distilbert-base-uncased\", num_labels=2, id2label=id2label, label2id=label2id)\n", + "\n", + "small_train = tokenized_imdb[\"train\"].shuffle(seed=42).select(range(1000))\n", + "small_eval = tokenized_imdb[\"test\"].shuffle(seed=42).select(range(300))\n", + "\n", + "training_args = TrainingArguments(\n", + " output_dir=\"my_awesome_imdb_beat-model\",\n", + " learning_rate=2e-5,\n", + " per_device_train_batch_size=16,\n", + " per_device_eval_batch_size=32,\n", + " max_steps=50,\n", + " eval_strategy=\"steps\",\n", + " eval_steps=25,\n", + " save_strategy=\"no\",\n", + " load_best_model_at_end=False,\n", + " push_to_hub=False,\n", + " logging_steps=10,\n", + " dataloader_pin_memory=False,\n", + " report_to=\"none\",\n", + ")\n", + "\n", + "trainer = Trainer(\n", + " model=model,\n", + " args=training_args,\n", + " train_dataset=tokenized_imdb[\"train\"],\n", + " eval_dataset=tokenized_imdb[\"test\"],\n", + " processing_class=tokenizer,\n", + " data_collator=data_collator,\n", + " compute_metrics=compute_metrics,\n", + ")\n", + "\n", + "trainer.train()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "02edaf14", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "08798ad6ed704ff28841a7c7d9de14f0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a5963bbce14b4b97bf79bacac123170f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Processing Files (0 / 0): | | 0.00B / 0.00B " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d26559a83f974bea9454119cabaea4c5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "New Data Upload: | | 0.00B / 0.00B " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "CommitInfo(commit_url='https://huggingface.co/hedonwang/my_awesome_imdb_beat-model/commit/03881691b338f89a5bac06a7f457bf9d1e384b05', commit_message='End of training', commit_description='', oid='03881691b338f89a5bac06a7f457bf9d1e384b05', pr_url=None, repo_url=RepoUrl('https://huggingface.co/hedonwang/my_awesome_imdb_beat-model', endpoint='https://huggingface.co', repo_type='model', repo_id='hedonwang/my_awesome_imdb_beat-model'), pr_revision=None, pr_num=None)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "trainer.push_to_hub()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "4a88f512", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "15da34321f0a4d2dbc45d2b407937485", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/104 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "[{'label': 'POSITIVE', 'score': 0.5393671989440918}]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from transformers import pipeline\n", + "\n", + "classifier = pipeline(\"sentiment-analysis\", model=\"my_awesome_imdb_beat-model\")\n", + "classifier(\"I've been waiting for a Hugging Face course my whole life.\")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/summarization.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/summarization.ipynb new file mode 100644 index 0000000..43f07aa --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/summarization.ipynb @@ -0,0 +1,2671 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "69793251", + "metadata": {}, + "outputs": [], + "source": [ + "from huggingface_hub import notebook_login\n", + "\n", + "notebook_login()" + ] + }, + { + "cell_type": "markdown", + "id": "70246bb3", + "metadata": {}, + "source": [ + "### Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b60f3884", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Dataset({\n", + " features: ['text', 'summary', 'title'],\n", + " num_rows: 1237\n", + "})" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datasets import load_dataset\n", + "\n", + "billsum_raw = load_dataset(\"FiscalNote/billsum\", split=\"ca_test\")\n", + "billsum_raw" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "fc1c5850", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'text': 'The people of the State of California do enact as follows:\\n\\n\\nSECTION 1.\\nThis act shall be known, and may be cited, as the Identity Theft Resolution Act.\\nSEC. 2.\\nSection 1785.16.2 of the Civil Code is amended to read:\\n1785.16.2.\\n(a) No creditor may sell a consumer debt to a debt collector, as defined in 15 U.S.C. Sec. 1692a, if the consumer is a victim of identity theft, as defined in Section 1798.2, and with respect to that debt, the creditor has received notice pursuant to subdivision (k) of Section 1785.16 or paragraph (2) of subdivision (g) of Section 1788.18.\\n(b) Subdivision (a) does not apply to a creditor’s sale of a debt to a subsidiary or affiliate of the creditor, if, with respect to that debt, the subsidiary or affiliate does not take any action to collect the debt.\\n(c) For the purposes of this section, the requirement in 15 U.S.C. Sec. 1692a, that a person must use an instrumentality of interstate commerce or the mails in the collection of any debt to be considered a debt collector, does not apply.\\nSEC. 3.\\nSection 1788.18 of the Civil Code is amended to read:\\n1788.18.\\n(a) Upon receipt from a debtor of all of the following, a debt collector shall cease collection activities until completion of the review provided in subdivision (d):\\n(1) A copy of a police report filed by the debtor alleging that the debtor is the victim of an identity theft crime, including, but not limited to, a violation of Section 530.5 of the Penal Code, for the specific debt being collected by the debt collector.\\n(2) The debtor’s written statement that the debtor claims to be the victim of identity theft with respect to the specific debt being collected by the debt collector.\\n(b) The written statement described in paragraph (2) of subdivision (a) shall consist of any of the following:\\n(1) A Federal Trade Commission’s Affidavit of Identity Theft.\\n(2) A written statement that contains the content of the Identity Theft Victim’s Fraudulent Account Information Request offered to the public by the California Office of Privacy Protection.\\n(3) A written statement that certifies that the representations are true, correct, and contain no material omissions of fact to the best knowledge and belief of the person submitting the certification. A person submitting the certification who declares as true any material matter pursuant to this subdivision that he or she knows to be false is guilty of a misdemeanor. The statement shall contain or be accompanied by the following, to the extent that an item listed below is relevant to the debtor’s allegation of identity theft with respect to the debt in question:\\n(A) A statement that the debtor is a victim of identity theft.\\n(B) A copy of the debtor’s driver’s license or identification card, as issued by the state.\\n(C) Any other identification document that supports the statement of identity theft.\\n(D) Specific facts supporting the claim of identity theft, if available.\\n(E) Any explanation showing that the debtor did not incur the debt.\\n(F) Any available correspondence disputing the debt after transaction information has been provided to the debtor.\\n(G) Documentation of the residence of the debtor at the time of the alleged debt. This may include copies of bills and statements, such as utility bills, tax statements, or other statements from businesses sent to the debtor, showing that the debtor lived at another residence at the time the debt was incurred.\\n(H) A telephone number for contacting the debtor concerning any additional information or questions, or direction that further communications to the debtor be in writing only, with the mailing address specified in the statement.\\n(I) To the extent the debtor has information concerning who may have incurred the debt, the identification of any person whom the debtor believes is responsible.\\n(J) An express statement that the debtor did not authorize the use of the debtor’s name or personal information for incurring the debt.\\n(K) The certification required pursuant to this paragraph shall be sufficient if it is in substantially the following form:\\n“I certify the representations made are true, correct, and\\ncontain no material omissions of fact.\\n_____ (Date and Place) _____ _____ (Signature) _____ ”\\n(c) If a debtor notifies a debt collector orally that he or she is a victim of identity theft, the debt collector shall notify the debtor, orally or in writing, that the debtor’s claim must be in writing. If a debtor notifies a debt collector in writing that he or she is a victim of identity theft, but omits information required pursuant to subdivision (a) or, if applicable, the certification required pursuant to paragraph (3) of subdivision (b), if the debt collector does not cease collection activities, the debt collector shall provide written notice to the debtor of the additional information that is required, or the certification required pursuant to paragraph (3) of subdivision (b), as applicable, or send the debtor a copy of the Federal Trade Commission’s Affidavit of Identity Theft form.\\n(d) Within 10 business days of receiving the complete statement and information described in subdivision (a), the debt collector shall, if it furnished adverse information about the debtor to a consumer credit reporting agency, notify the consumer credit reporting agency that the account is disputed, and initiate a review considering all of the information provided by the debtor and other information available to the debt collector in its file or from the creditor. The debt collector shall send notice of its determination to the debtor no later than 10 business days after concluding the review. The debt collector may recommence debt collection activities only upon making a good faith determination that the information does not establish that the debtor is not responsible for the specific debt in question. The debt collector’s determination shall be made in a manner consistent with the provisions of subsection (1) of Section 1692 of Title 15 of the United States Code, as incorporated by Section 1788.17 of this code. The debt collector shall notify the debtor in writing of that determination and the basis for that determination before proceeding with any further collection activities. The debt collector’s determination shall be based on all of the information provided by the debtor and other information available to the debt collector in its file or from the creditor.\\n(e) No inference or presumption that the debt is valid or invalid, or that the debtor is liable or not liable for the debt, shall arise if the debt collector decides after the review described in subdivision (d) to cease or recommence the debt collection activities. The exercise or nonexercise of rights under this section is not a waiver of any other right or defense of the debtor or debt collector.\\n(f) The statement and supporting documents that comply with subdivision (a) may also satisfy, to the extent those documents meet the requirements of, the notice requirement of paragraph (5) of subdivision (c) of Section 1798.93.\\n(g) A debt collector who ceases collection activities under this section and does not recommence those collection activities shall do all of the following:\\n(1) If the debt collector has furnished adverse information to a consumer credit reporting agency, notify the agency to delete that information no later than 10 business days after making its determination.\\n(2) Notify the creditor no later than 10 business days after making its determination that debt collection activities have been terminated based upon the debtor’s claim of identity theft.\\n(h) A debt collector who has possession of documents that the debtor is entitled to request from a creditor pursuant to Section 530.8 of the Penal Code is authorized to provide those documents to the debtor.\\n(i) Notwithstanding subdivision (h) of Section 1788.2, for the purposes of this section, “debtor” means a natural person, firm, association, organization, partnership, business trust, company, corporation, or limited liability company from which a debt collector seeks to collect a debt that is due and owing or alleged to be due and owing from the person or entity. The remedies provided by this title shall apply equally to violations of this section.',\n", + " 'summary': 'Existing law requires a debt collector that receives a copy of a police report filed by the debtor alleging that the debtor is the victim of an identity theft crime and a written statement in which the debtor claims to be the victim of identity theft to cease collection activities until completion of a review. Existing law requires the debt collector to review and consider all of the information provided by the debtor and other available information and authorizes the debt collector to recommence debt collection activities only upon making a good faith determination that the information does not establish that the debtor is not responsible for the specific debt in question.\\nThis bill, the Identity Theft Resolution Act, would require the debt collector, upon receipt of the police report and written statement described above, if it furnished adverse information about the debtor to a consumer credit reporting agency, to notify the consumer credit reporting agency that the account is disputed, and initiate a review, as specified, within 10 business days. The bill would require the debt collector to send notice of its determination to the debtor no later than 10 business days after concluding the review. The bill would require a debt collector that does not recommence collection activities under these provisions to notify the creditor, no later than 10 business days after making its determination, and if it furnished adverse information to a consumer credit reporting agency, to notify the agency to delete that information no later than 10 business days after making its determination. The bill would also prohibit a creditor from selling a consumer debt to a debt collector if the creditor has received notice that the debt collector has terminated debt collection activities, as described above.',\n", + " 'title': 'An act to amend Sections 1785.16.2 and 1788.18 of the Civil Code, relating to debt collection.'}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "billsum = billsum_raw.train_test_split(test_size=0.2)\n", + "billsum[\"train\"][0]" + ] + }, + { + "cell_type": "markdown", + "id": "d0240e9f", + "metadata": {}, + "source": [ + "### Preprocess" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "88c2bf22", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "363cb68a82174b9797e34fee18cf0237", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "config.json: 0%| | 0.00/1.21k [00:00<?, ?B/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9039fe8e74a149f8bd1ee40a14285a83", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "tokenizer_config.json: 0%| | 0.00/2.32k [00:00<?, ?B/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "017b918b8c72424fa15c75c9cd43b51d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "spiece.model: 0%| | 0.00/792k [00:00<?, ?B/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9037a681dc3d4153bf4cb1a0d01e9a5d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "tokenizer.json: 0%| | 0.00/1.39M [00:00<?, ?B/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "checkpoint = \"google-t5/t5-small\"\n", + "tokenizer = AutoTokenizer.from_pretrained(checkpoint)\n", + "\n", + "prefix = \"summarize: \"\n", + "\n", + "def preprocess_function(examples):\n", + " inputs = [prefix + doc for doc in examples[\"text\"]]\n", + " model_inputs = tokenizer(inputs, max_length=1024, truncation=True)\n", + "\n", + " labels = tokenizer(text_target=examples[\"summary\"], max_length=128, truncation=True)\n", + "\n", + " model_inputs[\"labels\"] = labels[\"input_ids\"]\n", + " return model_inputs" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "597d77ba", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fa577c11beb94ea3a7a2e9f2fa8a50cb", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/989 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "61fc7648c5264c72bccfd82c3d57ad92", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/248 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "{'text': 'The people of the State of California do enact as follows:\\n\\n\\nSECTION 1.\\nThis act shall be known, and may be cited, as the Identity Theft Resolution Act.\\nSEC. 2.\\nSection 1785.16.2 of the Civil Code is amended to read:\\n1785.16.2.\\n(a) No creditor may sell a consumer debt to a debt collector, as defined in 15 U.S.C. Sec. 1692a, if the consumer is a victim of identity theft, as defined in Section 1798.2, and with respect to that debt, the creditor has received notice pursuant to subdivision (k) of Section 1785.16 or paragraph (2) of subdivision (g) of Section 1788.18.\\n(b) Subdivision (a) does not apply to a creditor’s sale of a debt to a subsidiary or affiliate of the creditor, if, with respect to that debt, the subsidiary or affiliate does not take any action to collect the debt.\\n(c) For the purposes of this section, the requirement in 15 U.S.C. Sec. 1692a, that a person must use an instrumentality of interstate commerce or the mails in the collection of any debt to be considered a debt collector, does not apply.\\nSEC. 3.\\nSection 1788.18 of the Civil Code is amended to read:\\n1788.18.\\n(a) Upon receipt from a debtor of all of the following, a debt collector shall cease collection activities until completion of the review provided in subdivision (d):\\n(1) A copy of a police report filed by the debtor alleging that the debtor is the victim of an identity theft crime, including, but not limited to, a violation of Section 530.5 of the Penal Code, for the specific debt being collected by the debt collector.\\n(2) The debtor’s written statement that the debtor claims to be the victim of identity theft with respect to the specific debt being collected by the debt collector.\\n(b) The written statement described in paragraph (2) of subdivision (a) shall consist of any of the following:\\n(1) A Federal Trade Commission’s Affidavit of Identity Theft.\\n(2) A written statement that contains the content of the Identity Theft Victim’s Fraudulent Account Information Request offered to the public by the California Office of Privacy Protection.\\n(3) A written statement that certifies that the representations are true, correct, and contain no material omissions of fact to the best knowledge and belief of the person submitting the certification. A person submitting the certification who declares as true any material matter pursuant to this subdivision that he or she knows to be false is guilty of a misdemeanor. The statement shall contain or be accompanied by the following, to the extent that an item listed below is relevant to the debtor’s allegation of identity theft with respect to the debt in question:\\n(A) A statement that the debtor is a victim of identity theft.\\n(B) A copy of the debtor’s driver’s license or identification card, as issued by the state.\\n(C) Any other identification document that supports the statement of identity theft.\\n(D) Specific facts supporting the claim of identity theft, if available.\\n(E) Any explanation showing that the debtor did not incur the debt.\\n(F) Any available correspondence disputing the debt after transaction information has been provided to the debtor.\\n(G) Documentation of the residence of the debtor at the time of the alleged debt. This may include copies of bills and statements, such as utility bills, tax statements, or other statements from businesses sent to the debtor, showing that the debtor lived at another residence at the time the debt was incurred.\\n(H) A telephone number for contacting the debtor concerning any additional information or questions, or direction that further communications to the debtor be in writing only, with the mailing address specified in the statement.\\n(I) To the extent the debtor has information concerning who may have incurred the debt, the identification of any person whom the debtor believes is responsible.\\n(J) An express statement that the debtor did not authorize the use of the debtor’s name or personal information for incurring the debt.\\n(K) The certification required pursuant to this paragraph shall be sufficient if it is in substantially the following form:\\n“I certify the representations made are true, correct, and\\ncontain no material omissions of fact.\\n_____ (Date and Place) _____ _____ (Signature) _____ ”\\n(c) If a debtor notifies a debt collector orally that he or she is a victim of identity theft, the debt collector shall notify the debtor, orally or in writing, that the debtor’s claim must be in writing. If a debtor notifies a debt collector in writing that he or she is a victim of identity theft, but omits information required pursuant to subdivision (a) or, if applicable, the certification required pursuant to paragraph (3) of subdivision (b), if the debt collector does not cease collection activities, the debt collector shall provide written notice to the debtor of the additional information that is required, or the certification required pursuant to paragraph (3) of subdivision (b), as applicable, or send the debtor a copy of the Federal Trade Commission’s Affidavit of Identity Theft form.\\n(d) Within 10 business days of receiving the complete statement and information described in subdivision (a), the debt collector shall, if it furnished adverse information about the debtor to a consumer credit reporting agency, notify the consumer credit reporting agency that the account is disputed, and initiate a review considering all of the information provided by the debtor and other information available to the debt collector in its file or from the creditor. The debt collector shall send notice of its determination to the debtor no later than 10 business days after concluding the review. The debt collector may recommence debt collection activities only upon making a good faith determination that the information does not establish that the debtor is not responsible for the specific debt in question. The debt collector’s determination shall be made in a manner consistent with the provisions of subsection (1) of Section 1692 of Title 15 of the United States Code, as incorporated by Section 1788.17 of this code. The debt collector shall notify the debtor in writing of that determination and the basis for that determination before proceeding with any further collection activities. The debt collector’s determination shall be based on all of the information provided by the debtor and other information available to the debt collector in its file or from the creditor.\\n(e) No inference or presumption that the debt is valid or invalid, or that the debtor is liable or not liable for the debt, shall arise if the debt collector decides after the review described in subdivision (d) to cease or recommence the debt collection activities. The exercise or nonexercise of rights under this section is not a waiver of any other right or defense of the debtor or debt collector.\\n(f) The statement and supporting documents that comply with subdivision (a) may also satisfy, to the extent those documents meet the requirements of, the notice requirement of paragraph (5) of subdivision (c) of Section 1798.93.\\n(g) A debt collector who ceases collection activities under this section and does not recommence those collection activities shall do all of the following:\\n(1) If the debt collector has furnished adverse information to a consumer credit reporting agency, notify the agency to delete that information no later than 10 business days after making its determination.\\n(2) Notify the creditor no later than 10 business days after making its determination that debt collection activities have been terminated based upon the debtor’s claim of identity theft.\\n(h) A debt collector who has possession of documents that the debtor is entitled to request from a creditor pursuant to Section 530.8 of the Penal Code is authorized to provide those documents to the debtor.\\n(i) Notwithstanding subdivision (h) of Section 1788.2, for the purposes of this section, “debtor” means a natural person, firm, association, organization, partnership, business trust, company, corporation, or limited liability company from which a debt collector seeks to collect a debt that is due and owing or alleged to be due and owing from the person or entity. The remedies provided by this title shall apply equally to violations of this section.',\n", + " 'summary': 'Existing law requires a debt collector that receives a copy of a police report filed by the debtor alleging that the debtor is the victim of an identity theft crime and a written statement in which the debtor claims to be the victim of identity theft to cease collection activities until completion of a review. Existing law requires the debt collector to review and consider all of the information provided by the debtor and other available information and authorizes the debt collector to recommence debt collection activities only upon making a good faith determination that the information does not establish that the debtor is not responsible for the specific debt in question.\\nThis bill, the Identity Theft Resolution Act, would require the debt collector, upon receipt of the police report and written statement described above, if it furnished adverse information about the debtor to a consumer credit reporting agency, to notify the consumer credit reporting agency that the account is disputed, and initiate a review, as specified, within 10 business days. The bill would require the debt collector to send notice of its determination to the debtor no later than 10 business days after concluding the review. The bill would require a debt collector that does not recommence collection activities under these provisions to notify the creditor, no later than 10 business days after making its determination, and if it furnished adverse information to a consumer credit reporting agency, to notify the agency to delete that information no later than 10 business days after making its determination. The bill would also prohibit a creditor from selling a consumer debt to a debt collector if the creditor has received notice that the debt collector has terminated debt collection activities, as described above.',\n", + " 'title': 'An act to amend Sections 1785.16.2 and 1788.18 of the Civil Code, relating to debt collection.',\n", + " 'input_ids': [21603,\n", + " 10,\n", + " 37,\n", + " 151,\n", + " 13,\n", + " 8,\n", + " 1015,\n", + " 13,\n", + " 1826,\n", + " 103,\n", + " 3,\n", + " 35,\n", + " 2708,\n", + " 38,\n", + " 6963,\n", + " 10,\n", + " 180,\n", + " 3073,\n", + " 9562,\n", + " 1300,\n", + " 100,\n", + " 1810,\n", + " 1522,\n", + " 36,\n", + " 801,\n", + " 6,\n", + " 11,\n", + " 164,\n", + " 36,\n", + " 3,\n", + " 11675,\n", + " 6,\n", + " 38,\n", + " 8,\n", + " 28521,\n", + " 37,\n", + " 89,\n", + " 17,\n", + " 19957,\n", + " 1983,\n", + " 5,\n", + " 180,\n", + " 3073,\n", + " 5,\n", + " 1682,\n", + " 5568,\n", + " 3,\n", + " 27640,\n", + " 20519,\n", + " 23913,\n", + " 13,\n", + " 8,\n", + " 7707,\n", + " 3636,\n", + " 19,\n", + " 21012,\n", + " 12,\n", + " 608,\n", + " 10,\n", + " 3,\n", + " 27640,\n", + " 20519,\n", + " 23913,\n", + " 5,\n", + " 41,\n", + " 9,\n", + " 61,\n", + " 465,\n", + " 998,\n", + " 127,\n", + " 164,\n", + " 1789,\n", + " 3,\n", + " 9,\n", + " 3733,\n", + " 2814,\n", + " 12,\n", + " 3,\n", + " 9,\n", + " 2814,\n", + " 12501,\n", + " 6,\n", + " 38,\n", + " 4802,\n", + " 16,\n", + " 627,\n", + " 412,\n", + " 5,\n", + " 134,\n", + " 5,\n", + " 254,\n", + " 5,\n", + " 14969,\n", + " 5,\n", + " 898,\n", + " 4508,\n", + " 9,\n", + " 6,\n", + " 3,\n", + " 99,\n", + " 8,\n", + " 3733,\n", + " 19,\n", + " 3,\n", + " 9,\n", + " 7584,\n", + " 13,\n", + " 4810,\n", + " 14806,\n", + " 6,\n", + " 38,\n", + " 4802,\n", + " 16,\n", + " 5568,\n", + " 1003,\n", + " 3916,\n", + " 5,\n", + " 4482,\n", + " 11,\n", + " 28,\n", + " 1445,\n", + " 12,\n", + " 24,\n", + " 2814,\n", + " 6,\n", + " 8,\n", + " 998,\n", + " 127,\n", + " 65,\n", + " 1204,\n", + " 2103,\n", + " 19890,\n", + " 288,\n", + " 12,\n", + " 27444,\n", + " 41,\n", + " 157,\n", + " 61,\n", + " 13,\n", + " 5568,\n", + " 1003,\n", + " 4433,\n", + " 5,\n", + " 2938,\n", + " 42,\n", + " 8986,\n", + " 6499,\n", + " 13,\n", + " 27444,\n", + " 41,\n", + " 122,\n", + " 61,\n", + " 13,\n", + " 5568,\n", + " 1003,\n", + " 4060,\n", + " 5,\n", + " 2606,\n", + " 5,\n", + " 41,\n", + " 115,\n", + " 61,\n", + " 3325,\n", + " 26,\n", + " 23,\n", + " 6610,\n", + " 41,\n", + " 9,\n", + " 61,\n", + " 405,\n", + " 59,\n", + " 1581,\n", + " 12,\n", + " 3,\n", + " 9,\n", + " 998,\n", + " 127,\n", + " 22,\n", + " 7,\n", + " 1048,\n", + " 13,\n", + " 3,\n", + " 9,\n", + " 2814,\n", + " 12,\n", + " 3,\n", + " 9,\n", + " 20438,\n", + " 42,\n", + " 8484,\n", + " 13,\n", + " 8,\n", + " 998,\n", + " 127,\n", + " 6,\n", + " 3,\n", + " 99,\n", + " 6,\n", + " 28,\n", + " 1445,\n", + " 12,\n", + " 24,\n", + " 2814,\n", + " 6,\n", + " 8,\n", + " 20438,\n", + " 42,\n", + " 8484,\n", + " 405,\n", + " 59,\n", + " 240,\n", + " 136,\n", + " 1041,\n", + " 12,\n", + " 2868,\n", + " 8,\n", + " 2814,\n", + " 5,\n", + " 41,\n", + " 75,\n", + " 61,\n", + " 242,\n", + " 8,\n", + " 3659,\n", + " 13,\n", + " 48,\n", + " 1375,\n", + " 6,\n", + " 8,\n", + " 5971,\n", + " 16,\n", + " 627,\n", + " 412,\n", + " 5,\n", + " 134,\n", + " 5,\n", + " 254,\n", + " 5,\n", + " 14969,\n", + " 5,\n", + " 898,\n", + " 4508,\n", + " 9,\n", + " 6,\n", + " 24,\n", + " 3,\n", + " 9,\n", + " 568,\n", + " 398,\n", + " 169,\n", + " 46,\n", + " 15205,\n", + " 485,\n", + " 13,\n", + " 1413,\n", + " 5540,\n", + " 12794,\n", + " 42,\n", + " 8,\n", + " 4842,\n", + " 7,\n", + " 16,\n", + " 8,\n", + " 1232,\n", + " 13,\n", + " 136,\n", + " 2814,\n", + " 12,\n", + " 36,\n", + " 1702,\n", + " 3,\n", + " 9,\n", + " 2814,\n", + " 12501,\n", + " 6,\n", + " 405,\n", + " 59,\n", + " 1581,\n", + " 5,\n", + " 180,\n", + " 3073,\n", + " 5,\n", + " 1877,\n", + " 5568,\n", + " 1003,\n", + " 4060,\n", + " 5,\n", + " 2606,\n", + " 13,\n", + " 8,\n", + " 7707,\n", + " 3636,\n", + " 19,\n", + " 21012,\n", + " 12,\n", + " 608,\n", + " 10,\n", + " 1003,\n", + " 4060,\n", + " 5,\n", + " 2606,\n", + " 5,\n", + " 41,\n", + " 9,\n", + " 61,\n", + " 3,\n", + " 10688,\n", + " 8958,\n", + " 45,\n", + " 3,\n", + " 9,\n", + " 2814,\n", + " 127,\n", + " 13,\n", + " 66,\n", + " 13,\n", + " 8,\n", + " 826,\n", + " 6,\n", + " 3,\n", + " 9,\n", + " 2814,\n", + " 12501,\n", + " 1522,\n", + " 18682,\n", + " 1232,\n", + " 1087,\n", + " 552,\n", + " 6929,\n", + " 13,\n", + " 8,\n", + " 1132,\n", + " 937,\n", + " 16,\n", + " 27444,\n", + " 41,\n", + " 26,\n", + " 61,\n", + " 10,\n", + " 5637,\n", + " 71,\n", + " 2405,\n", + " 13,\n", + " 3,\n", + " 9,\n", + " 2095,\n", + " 934,\n", + " 5132,\n", + " 57,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 1854,\n", + " 3896,\n", + " 24,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 19,\n", + " 8,\n", + " 7584,\n", + " 13,\n", + " 46,\n", + " 4810,\n", + " 14806,\n", + " 5447,\n", + " 6,\n", + " 379,\n", + " 6,\n", + " 68,\n", + " 59,\n", + " 1643,\n", + " 12,\n", + " 6,\n", + " 3,\n", + " 9,\n", + " 12374,\n", + " 13,\n", + " 5568,\n", + " 12210,\n", + " 12100,\n", + " 13,\n", + " 8,\n", + " 4511,\n", + " 138,\n", + " 3636,\n", + " 6,\n", + " 21,\n", + " 8,\n", + " 806,\n", + " 2814,\n", + " 271,\n", + " 4759,\n", + " 57,\n", + " 8,\n", + " 2814,\n", + " 12501,\n", + " 5,\n", + " 6499,\n", + " 37,\n", + " 2814,\n", + " 127,\n", + " 22,\n", + " 7,\n", + " 1545,\n", + " 2493,\n", + " 24,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 3213,\n", + " 12,\n", + " 36,\n", + " 8,\n", + " 7584,\n", + " 13,\n", + " 4810,\n", + " 14806,\n", + " 28,\n", + " 1445,\n", + " 12,\n", + " 8,\n", + " 806,\n", + " 2814,\n", + " 271,\n", + " 4759,\n", + " 57,\n", + " 8,\n", + " 2814,\n", + " 12501,\n", + " 5,\n", + " 41,\n", + " 115,\n", + " 61,\n", + " 37,\n", + " 1545,\n", + " 2493,\n", + " 3028,\n", + " 16,\n", + " 8986,\n", + " 6499,\n", + " 13,\n", + " 27444,\n", + " 41,\n", + " 9,\n", + " 61,\n", + " 1522,\n", + " 5608,\n", + " 13,\n", + " 136,\n", + " 13,\n", + " 8,\n", + " 826,\n", + " 10,\n", + " 5637,\n", + " 71,\n", + " 5034,\n", + " 6550,\n", + " 3527,\n", + " 22,\n", + " 7,\n", + " 71,\n", + " 89,\n", + " 89,\n", + " 23,\n", + " 26,\n", + " 9,\n", + " 5566,\n", + " 13,\n", + " 28521,\n", + " 37,\n", + " 89,\n", + " 17,\n", + " 5,\n", + " 6499,\n", + " 71,\n", + " 1545,\n", + " 2493,\n", + " 24,\n", + " 2579,\n", + " 8,\n", + " 738,\n", + " 13,\n", + " 8,\n", + " 28521,\n", + " 37,\n", + " 89,\n", + " 17,\n", + " 12060,\n", + " 2998,\n", + " 22,\n", + " 7,\n", + " 7672,\n", + " 26,\n", + " 16291,\n", + " 6288,\n", + " 2784,\n", + " 15374,\n", + " 1860,\n", + " 12,\n", + " 8,\n", + " 452,\n", + " 57,\n", + " 8,\n", + " 1826,\n", + " 2126,\n", + " 13,\n", + " 17865,\n", + " 8009,\n", + " 5,\n", + " 10153,\n", + " 71,\n", + " 1545,\n", + " 2493,\n", + " 24,\n", + " 12276,\n", + " 15821,\n", + " 24,\n", + " 8,\n", + " 6497,\n", + " 7,\n", + " 33,\n", + " 1176,\n", + " 6,\n", + " 2024,\n", + " 6,\n", + " 11,\n", + " 3480,\n", + " 150,\n", + " 1037,\n", + " 3,\n", + " 32,\n", + " 5451,\n", + " 7,\n", + " 13,\n", + " 685,\n", + " 12,\n", + " 8,\n", + " 200,\n", + " 1103,\n", + " 11,\n", + " 7750,\n", + " 13,\n", + " 8,\n", + " 568,\n", + " 3,\n", + " 14975,\n", + " 8,\n", + " 5151,\n", + " 5,\n", + " 71,\n", + " 568,\n", + " 3,\n", + " 14975,\n", + " 8,\n", + " 5151,\n", + " 113,\n", + " 15884,\n", + " 7,\n", + " 38,\n", + " 1176,\n", + " 136,\n", + " 1037,\n", + " 1052,\n", + " 19890,\n", + " 288,\n", + " 12,\n", + " 48,\n", + " 27444,\n", + " 24,\n", + " 3,\n", + " 88,\n", + " 42,\n", + " 255,\n", + " 4054,\n", + " 12,\n", + " 36,\n", + " 6136,\n", + " 19,\n", + " 10945,\n", + " 13,\n", + " 3,\n", + " 9,\n", + " 1817,\n", + " 1778,\n", + " 15,\n", + " 152,\n", + " 127,\n", + " 5,\n", + " 37,\n", + " 2493,\n", + " 1522,\n", + " 3480,\n", + " 42,\n", + " 36,\n", + " 3,\n", + " 10102,\n", + " 57,\n", + " 8,\n", + " 826,\n", + " 6,\n", + " 12,\n", + " 8,\n", + " 5996,\n", + " 24,\n", + " 46,\n", + " 2118,\n", + " 2616,\n", + " 666,\n", + " 19,\n", + " 2193,\n", + " 12,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 22,\n", + " 7,\n", + " 1854,\n", + " 122,\n", + " 257,\n", + " 13,\n", + " 4810,\n", + " 14806,\n", + " 28,\n", + " 1445,\n", + " 12,\n", + " 8,\n", + " 2814,\n", + " 16,\n", + " 822,\n", + " 10,\n", + " 41,\n", + " 188,\n", + " 61,\n", + " 71,\n", + " 2493,\n", + " 24,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 19,\n", + " 3,\n", + " 9,\n", + " 7584,\n", + " 13,\n", + " 4810,\n", + " 14806,\n", + " 5,\n", + " 41,\n", + " 279,\n", + " 61,\n", + " 71,\n", + " 2405,\n", + " 13,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 22,\n", + " 7,\n", + " 2535,\n", + " 22,\n", + " 7,\n", + " 3344,\n", + " 42,\n", + " 10356,\n", + " 895,\n", + " 6,\n", + " 38,\n", + " 4683,\n", + " 57,\n", + " 8,\n", + " 538,\n", + " 5,\n", + " 41,\n", + " 254,\n", + " 61,\n", + " 2372,\n", + " 119,\n", + " 10356,\n", + " 1708,\n", + " 24,\n", + " 4951,\n", + " 8,\n", + " 2493,\n", + " 13,\n", + " 4810,\n", + " 14806,\n", + " 5,\n", + " 41,\n", + " 308,\n", + " 61,\n", + " 18070,\n", + " 6688,\n", + " 3956,\n", + " 8,\n", + " 1988,\n", + " 13,\n", + " 4810,\n", + " 14806,\n", + " 6,\n", + " 3,\n", + " 99,\n", + " 347,\n", + " 5,\n", + " 41,\n", + " 427,\n", + " 61,\n", + " 2372,\n", + " 7295,\n", + " 2924,\n", + " 24,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 410,\n", + " 59,\n", + " 18663,\n", + " 8,\n", + " 2814,\n", + " 5,\n", + " 41,\n", + " 371,\n", + " 61,\n", + " 2372,\n", + " 347,\n", + " 17215,\n", + " 24955,\n", + " 53,\n", + " 8,\n", + " 2814,\n", + " 227,\n", + " 5878,\n", + " 251,\n", + " 65,\n", + " 118,\n", + " 937,\n", + " 12,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 5,\n", + " 41,\n", + " 517,\n", + " 61,\n", + " 11167,\n", + " 257,\n", + " 13,\n", + " 8,\n", + " 6198,\n", + " 13,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 44,\n", + " 8,\n", + " 97,\n", + " 13,\n", + " 8,\n", + " 3,\n", + " 12554,\n", + " 2814,\n", + " 5,\n", + " 100,\n", + " 164,\n", + " 560,\n", + " 8167,\n", + " 13,\n", + " 7200,\n", + " 11,\n", + " 6643,\n", + " 6,\n", + " 224,\n", + " 38,\n", + " 6637,\n", + " 7200,\n", + " 6,\n", + " 1104,\n", + " 6643,\n", + " 6,\n", + " 42,\n", + " 119,\n", + " 6643,\n", + " 45,\n", + " 1623,\n", + " 1622,\n", + " 12,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 6,\n", + " 2924,\n", + " 24,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 4114,\n", + " 44,\n", + " 430,\n", + " 6198,\n", + " 44,\n", + " 8,\n", + " 97,\n", + " 8,\n", + " 2814,\n", + " 47,\n", + " 3,\n", + " 20890,\n", + " 5,\n", + " 41,\n", + " 566,\n", + " 61,\n", + " 71,\n", + " 6596,\n", + " 381,\n", + " 21,\n", + " 3,\n", + " 22094,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 6238,\n", + " 136,\n", + " 1151,\n", + " 251,\n", + " 42,\n", + " 746,\n", + " 6,\n", + " 42,\n", + " 2212,\n", + " 24,\n", + " 856,\n", + " 5030,\n", + " 12,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 36,\n", + " 16,\n", + " 913,\n", + " 163,\n", + " 6,\n", + " 28,\n", + " 8,\n", + " 15114,\n", + " 1115,\n", + " 7173,\n", + " 16,\n", + " 8,\n", + " 2493,\n", + " 5,\n", + " 41,\n", + " 196,\n", + " 61,\n", + " 304,\n", + " 8,\n", + " 5996,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 65,\n", + " 251,\n", + " 6238,\n", + " 113,\n", + " 164,\n", + " 43,\n", + " 3,\n", + " 20890,\n", + " 8,\n", + " 2814,\n", + " 6,\n", + " 8,\n", + " 10356,\n", + " 13,\n", + " 136,\n", + " 568,\n", + " 4068,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 7228,\n", + " 19,\n", + " 1966,\n", + " 5,\n", + " 41,\n", + " 683,\n", + " 61,\n", + " 389,\n", + " 3980,\n", + " 2493,\n", + " 24,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 410,\n", + " 59,\n", + " 27765,\n", + " 8,\n", + " 169,\n", + " 13,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 22,\n", + " 7,\n", + " 564,\n", + " 42,\n", + " 525,\n", + " 251,\n", + " 21,\n", + " 18663,\n", + " 1007,\n", + " 8,\n", + " 2814,\n", + " 5,\n", + " 41,\n", + " 439,\n", + " 61,\n", + " 37,\n", + " 5151,\n", + " 831,\n", + " 19890,\n", + " 288,\n", + " 12,\n", + " 48,\n", + " 8986,\n", + " 1522,\n", + " 36,\n", + " 6684,\n", + " 3,\n", + " 99,\n", + " 34,\n", + " 19,\n", + " 16,\n", + " 15284,\n", + " 8,\n", + " 826,\n", + " 607,\n", + " 10,\n", + " 105,\n", + " 196,\n", + " 3,\n", + " 31492,\n", + " 8,\n", + " 6497,\n", + " 7,\n", + " 263,\n", + " 33,\n", + " 1176,\n", + " 6,\n", + " 2024,\n", + " 6,\n", + " 11,\n", + " 3480,\n", + " 150,\n", + " 1037,\n", + " 3,\n", + " 32,\n", + " 5451,\n", + " 7,\n", + " 13,\n", + " 685,\n", + " 5,\n", + " 31020,\n", + " 41,\n", + " 308,\n", + " 342,\n", + " 11,\n", + " 3399,\n", + " 61,\n", + " 31020,\n", + " 31020,\n", + " 41,\n", + " 22582,\n", + " 27440,\n", + " 61,\n", + " 31020,\n", + " 3,\n", + " 153,\n", + " 41,\n", + " 75,\n", + " 61,\n", + " 156,\n", + " 3,\n", + " 9,\n", + " 2814,\n", + " 127,\n", + " 59,\n", + " 15821,\n", + " 3,\n", + " 9,\n", + " 2814,\n", + " 12501,\n", + " 42,\n", + " 1427,\n", + " 24,\n", + " 3,\n", + " 88,\n", + " 42,\n", + " 255,\n", + " 19,\n", + " ...],\n", + " 'attention_mask': [1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " ...],\n", + " 'labels': [17061,\n", + " 53,\n", + " 973,\n", + " 2311,\n", + " 3,\n", + " 9,\n", + " 2814,\n", + " 12501,\n", + " 24,\n", + " 911,\n", + " 7,\n", + " 3,\n", + " 9,\n", + " 2405,\n", + " 13,\n", + " 3,\n", + " 9,\n", + " 2095,\n", + " 934,\n", + " 5132,\n", + " 57,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 1854,\n", + " 3896,\n", + " 24,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 19,\n", + " 8,\n", + " 7584,\n", + " 13,\n", + " 46,\n", + " 4810,\n", + " 14806,\n", + " 5447,\n", + " 11,\n", + " 3,\n", + " 9,\n", + " 1545,\n", + " 2493,\n", + " 16,\n", + " 84,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 3213,\n", + " 12,\n", + " 36,\n", + " 8,\n", + " 7584,\n", + " 13,\n", + " 4810,\n", + " 14806,\n", + " 12,\n", + " 18682,\n", + " 1232,\n", + " 1087,\n", + " 552,\n", + " 6929,\n", + " 13,\n", + " 3,\n", + " 9,\n", + " 1132,\n", + " 5,\n", + " 17061,\n", + " 53,\n", + " 973,\n", + " 2311,\n", + " 8,\n", + " 2814,\n", + " 12501,\n", + " 12,\n", + " 1132,\n", + " 11,\n", + " 1099,\n", + " 66,\n", + " 13,\n", + " 8,\n", + " 251,\n", + " 937,\n", + " 57,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 11,\n", + " 119,\n", + " 347,\n", + " 251,\n", + " 11,\n", + " 27765,\n", + " 7,\n", + " 8,\n", + " 2814,\n", + " 12501,\n", + " 12,\n", + " 3,\n", + " 60,\n", + " 287,\n", + " 51,\n", + " 1433,\n", + " 2814,\n", + " 1232,\n", + " 1087,\n", + " 163,\n", + " 1286,\n", + " 492,\n", + " 3,\n", + " 9,\n", + " 207,\n", + " 3251,\n", + " 11444,\n", + " 24,\n", + " 8,\n", + " 251,\n", + " 405,\n", + " 59,\n", + " 4797,\n", + " 24,\n", + " 8,\n", + " 2814,\n", + " 127,\n", + " 19,\n", + " 59,\n", + " 1966,\n", + " 1]}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tokenized_billsum = billsum.map(preprocess_function, batched=True)\n", + "tokenized_billsum[\"train\"][0]" + ] + }, + { + "cell_type": "markdown", + "id": "120ac344", + "metadata": {}, + "source": [ + "### DataCollator" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "18c2d7c1", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import DataCollatorForSeq2Seq\n", + "\n", + "data_collator = DataCollatorForSeq2Seq(tokenizer, model=checkpoint)" + ] + }, + { + "cell_type": "markdown", + "id": "6885315f", + "metadata": {}, + "source": [ + "### Evaluate" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "aec18c6c", + "metadata": {}, + "outputs": [], + "source": [ + "import evaluate\n", + "\n", + "rouge = evaluate.load(\"rouge\")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "fd7ed2c2", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "def compute_metrics(eval_pred):\n", + " predictions, labels = eval_pred\n", + "\n", + " if isinstance(predictions, tuple):\n", + " predictions = predictions[0]\n", + "\n", + " predictions = np.where(predictions != -100, predictions, tokenizer.pad_token_id)\n", + " labels = np.where(labels != -100, labels, tokenizer.pad_token_id)\n", + "\n", + " decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)\n", + " decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)\n", + "\n", + " result = rouge.compute(predictions=decoded_preds, references=decoded_labels, use_stemmer=True)\n", + "\n", + " prediction_lens = [np.count_nonzero(pred != tokenizer.pad_token_id) for pred in predictions]\n", + " result[\"gen_len\"] = np.mean(prediction_lens)\n", + "\n", + " return {k: round(v, 4) for k, v in result.items()}" + ] + }, + { + "cell_type": "markdown", + "id": "429c8d4f", + "metadata": {}, + "source": [ + "### Train" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "aeea3b6f", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "21393336d4f0467ba1a8b00775d0c77c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/131 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='248' max='248' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [248/248 11:39, Epoch 4/4]\n", + " </div>\n", + " <table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Epoch</th>\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " <th>Rouge1</th>\n", + " <th>Rouge2</th>\n", + " <th>Rougel</th>\n", + " <th>Rougelsum</th>\n", + " <th>Gen Len</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>1</td>\n", + " <td>No log</td>\n", + " <td>2.796954</td>\n", + " <td>0.126000</td>\n", + " <td>0.036900</td>\n", + " <td>0.105000</td>\n", + " <td>0.104700</td>\n", + " <td>19.064500</td>\n", + " </tr>\n", + " <tr>\n", + " <td>2</td>\n", + " <td>No log</td>\n", + " <td>2.588528</td>\n", + " <td>0.132700</td>\n", + " <td>0.045600</td>\n", + " <td>0.111400</td>\n", + " <td>0.111400</td>\n", + " <td>19.000000</td>\n", + " </tr>\n", + " <tr>\n", + " <td>3</td>\n", + " <td>No log</td>\n", + " <td>2.524350</td>\n", + " <td>0.139100</td>\n", + " <td>0.048300</td>\n", + " <td>0.114900</td>\n", + " <td>0.114900</td>\n", + " <td>19.000000</td>\n", + " </tr>\n", + " <tr>\n", + " <td>4</td>\n", + " <td>No log</td>\n", + " <td>2.507337</td>\n", + " <td>0.139700</td>\n", + " <td>0.048900</td>\n", + " <td>0.114700</td>\n", + " <td>0.114700</td>\n", + " <td>19.000000</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e927b5277d46428eb577a8ce5fee638b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "text/plain": [ + "TrainOutput(global_step=248, training_loss=3.022557412424395, metrics={'train_runtime': 701.5058, 'train_samples_per_second': 5.639, 'train_steps_per_second': 0.354, 'total_flos': 1070824333246464.0, 'train_loss': 3.022557412424395, 'epoch': 4.0})" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from transformers import AutoModelForSeq2SeqLM, Seq2SeqTrainingArguments, Seq2SeqTrainer\n", + "\n", + "model = AutoModelForSeq2SeqLM.from_pretrained(checkpoint)\n", + "\n", + "training_args = Seq2SeqTrainingArguments(\n", + " output_dir=\"my_awesome_billsum_model\",\n", + " eval_strategy=\"epoch\",\n", + " learning_rate=2e-5,\n", + " per_device_train_batch_size=16,\n", + " per_device_eval_batch_size=16,\n", + " weight_decay=0.01,\n", + " save_total_limit=3,\n", + " num_train_epochs=4,\n", + " predict_with_generate=True,\n", + " fp16=False, #change to bf16=True for XPU\n", + " push_to_hub=False,\n", + ")\n", + "\n", + "trainer = Seq2SeqTrainer(\n", + " model=model,\n", + " args=training_args,\n", + " train_dataset=tokenized_billsum[\"train\"],\n", + " eval_dataset=tokenized_billsum[\"test\"],\n", + " processing_class=tokenizer,\n", + " data_collator=data_collator,\n", + " compute_metrics=compute_metrics,\n", + ")\n", + "\n", + "trainer.train()" + ] + }, + { + "cell_type": "markdown", + "id": "44bb19d2", + "metadata": {}, + "source": [ + "### Inference" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "632a89c5", + "metadata": {}, + "outputs": [], + "source": [ + "text = \"summarize: The Inflation Reduction Act lowers prescription drug costs, health care costs, and energy costs. It's the most aggressive action on tackling the climate crisis in American history, which will lift up American workers and create good-paying, union jobs across the country. It'll lower the deficit and ask the ultra-wealthy and corporations to pay their fair share. And no one making under $400,000 per year will pay a penny more in taxes.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "a5813f67", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "eb11a8971844417baf4175a40f40c5b2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/131 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] Both `max_new_tokens` (=100) and `max_length`(=20) seem to have been set. `max_new_tokens` will take precedence. Please refer to the documentation for more information. (https://huggingface.co/docs/transformers/main/en/main_classes/text_generation)\n" + ] + }, + { + "data": { + "text/plain": [ + "\"the Inflation Reduction Act lowers prescription drug costs, health care costs, and energy costs. it's the most aggressive action on tackling the climate crisis in American history. it'll ask the ultra-wealthy and corporations to pay their fair share.\"" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from transformers import AutoModelForSeq2SeqLM\n", + "\n", + "model_dir = \"my_awesome_billsum_model/checkpoint-248\"\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(model_dir)\n", + "inputs = tokenizer(text, return_tensors=\"pt\").input_ids\n", + "\n", + "model = AutoModelForSeq2SeqLM.from_pretrained(model_dir)\n", + "outputs = model.generate(inputs, max_new_tokens=100, do_sample=False)\n", + "tokenizer.decode(outputs[0], skip_special_tokens=True)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/token_classification.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/token_classification.ipynb new file mode 100644 index 0000000..b36d815 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/token_classification.ipynb @@ -0,0 +1,617 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 39, + "id": "b458dc0d", + "metadata": {}, + "outputs": [], + "source": [ + "from huggingface_hub import notebook_login\n", + "\n", + "notebook_login()" + ] + }, + { + "cell_type": "markdown", + "id": "53f7e253", + "metadata": {}, + "source": [ + "### Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "d0114463", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Repo card metadata block was not found. Setting CardData to empty.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id': '0', 'tokens': ['@paulwalk', 'It', \"'s\", 'the', 'view', 'from', 'where', 'I', \"'m\", 'living', 'for', 'two', 'weeks', '.', 'Empire', 'State', 'Building', '=', 'ESB', '.', 'Pretty', 'bad', 'storm', 'here', 'last', 'evening', '.'], 'ner_tags': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0]}\n" + ] + } + ], + "source": [ + "from datasets import load_dataset\n", + "\n", + "wnut = load_dataset(\"flaitenberger/wnut_17\")\n", + "print(wnut[\"train\"][0])" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "0b59ba59", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['O',\n", + " 'B-corporation',\n", + " 'I-corporation',\n", + " 'B-creative-work',\n", + " 'I-creative-work',\n", + " 'B-group',\n", + " 'I-group',\n", + " 'B-location',\n", + " 'I-location',\n", + " 'B-person',\n", + " 'I-person',\n", + " 'B-product',\n", + " 'I-product']" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "label_list = wnut[\"train\"].features[f\"ner_tags\"].feature.names\n", + "label_list" + ] + }, + { + "cell_type": "markdown", + "id": "f29bdc78", + "metadata": {}, + "source": [ + "### Tokenizer" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "8fda222b", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(\"distilbert/distilbert-base-uncased\")" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "c83a9ac5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[CLS]',\n", + " '@',\n", + " 'paul',\n", + " '##walk',\n", + " 'it',\n", + " \"'\",\n", + " 's',\n", + " 'the',\n", + " 'view',\n", + " 'from',\n", + " 'where',\n", + " 'i',\n", + " \"'\",\n", + " 'm',\n", + " 'living',\n", + " 'for',\n", + " 'two',\n", + " 'weeks',\n", + " '.',\n", + " 'empire',\n", + " 'state',\n", + " 'building',\n", + " '=',\n", + " 'es',\n", + " '##b',\n", + " '.',\n", + " 'pretty',\n", + " 'bad',\n", + " 'storm',\n", + " 'here',\n", + " 'last',\n", + " 'evening',\n", + " '.',\n", + " '[SEP]']" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example = wnut[\"train\"][0]\n", + "tokenized_input = tokenizer(example[\"tokens\"], is_split_into_words=True)\n", + "tokens = tokenizer.convert_ids_to_tokens(tokenized_input[\"input_ids\"])\n", + "tokens" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "b5bc4d6f", + "metadata": {}, + "outputs": [], + "source": [ + "def tokenize_and_align_labels(examples):\n", + " tokenized_inputs = tokenizer(examples[\"tokens\"], truncation=True, is_split_into_words=True)\n", + "\n", + " labels = []\n", + " for i, label in enumerate(examples[\"ner_tags\"]):\n", + " word_ids = tokenized_inputs.word_ids(batch_index=i) # Map tokens to their respective word.\n", + " previous_word_idx = None\n", + " label_ids = []\n", + " for word_idx in word_ids: # Set the special tokens to -100.\n", + " if word_idx is None:\n", + " label_ids.append(-100)\n", + " elif word_idx != previous_word_idx:\n", + " label_ids.append(label[word_idx])\n", + " else:\n", + " label_ids.append(-100)\n", + " previous_word_idx = word_idx\n", + " labels.append(label_ids)\n", + " tokenized_inputs[\"labels\"] = labels\n", + " return tokenized_inputs" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "9fba84cf", + "metadata": {}, + "outputs": [], + "source": [ + "tokenized_wnut = wnut.map(tokenize_and_align_labels, batched=True)" + ] + }, + { + "cell_type": "markdown", + "id": "82dd8def", + "metadata": {}, + "source": [ + "### DataCollator" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "07551a34", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import DataCollatorForTokenClassification\n", + "\n", + "data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)" + ] + }, + { + "cell_type": "markdown", + "id": "55656408", + "metadata": {}, + "source": [ + "### Evaluate" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "deddad6f", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2dc95d8c91b3464e9a409864726f3d32", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Downloading builder script: 0.00B [00:00, ?B/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import evaluate\n", + "\n", + "seqeval = evaluate.load(\"seqeval\")" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "cd8ed845", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "labels = [label_list[i] for i in example[f\"ner_tags\"]]\n", + "\n", + "def compute_metrics(p):\n", + " predictions, labels = p\n", + " predictions = np.argmax(predictions, axis=2)\n", + "\n", + " true_predictions = [\n", + " [label_list[p] for (p, l) in zip(prediction, label) if l != -100]\n", + " for prediction, label in zip(predictions, labels)\n", + " ]\n", + " true_labels = [\n", + " [label_list[l] for (p, l) in zip(prediction, label) if l != -100]\n", + " for prediction, label in zip(predictions, labels)\n", + " ]\n", + "\n", + " results = seqeval.compute(predictions=true_predictions, references=true_labels)\n", + " return {\n", + " \"precision\": results[\"overall_precision\"],\n", + " \"recall\": results[\"overall_recall\"],\n", + " \"f1\": results[\"overall_f1\"],\n", + " \"accuracy\": results[\"overall_accuracy\"],\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "d786c7e4", + "metadata": {}, + "source": [ + "### Train" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "66cd36c5", + "metadata": {}, + "outputs": [], + "source": [ + "id2label = {\n", + " 0: \"O\",\n", + " 1: \"B-corporation\",\n", + " 2: \"I-corporation\",\n", + " 3: \"B-creative-work\",\n", + " 4: \"I-creative-work\",\n", + " 5: \"B-group\",\n", + " 6: \"I-group\",\n", + " 7: \"B-location\",\n", + " 8: \"I-location\",\n", + " 9: \"B-person\",\n", + " 10: \"I-person\",\n", + " 11: \"B-product\",\n", + " 12: \"I-product\",\n", + "}\n", + "label2id = {\n", + " \"O\": 0,\n", + " \"B-corporation\": 1,\n", + " \"I-corporation\": 2,\n", + " \"B-creative-work\": 3,\n", + " \"I-creative-work\": 4,\n", + " \"B-group\": 5,\n", + " \"I-group\": 6,\n", + " \"B-location\": 7,\n", + " \"I-location\": 8,\n", + " \"B-person\": 9,\n", + " \"I-person\": 10,\n", + " \"B-product\": 11,\n", + " \"I-product\": 12,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "37a10c3e", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e6c4a7a93a204636902f8e635225cd24", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/100 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mDistilBertForTokenClassification LOAD REPORT\u001b[0m from: distilbert/distilbert-base-uncased\n", + "Key | Status | \n", + "------------------------+------------+-\n", + "vocab_projector.bias | UNEXPECTED | \n", + "vocab_transform.bias | UNEXPECTED | \n", + "vocab_layer_norm.weight | UNEXPECTED | \n", + "vocab_transform.weight | UNEXPECTED | \n", + "vocab_layer_norm.bias | UNEXPECTED | \n", + "classifier.bias | MISSING | \n", + "classifier.weight | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + } + ], + "source": [ + "from transformers import AutoModelForTokenClassification, Trainer, TrainingArguments\n", + "\n", + "model = AutoModelForTokenClassification.from_pretrained(\n", + " \"distilbert/distilbert-base-uncased\", num_labels=13, id2label=id2label, label2id=label2id\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "2e8e4146", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='50' max='50' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [50/50 00:14, Epoch 0/1]\n", + " </div>\n", + " <table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Step</th>\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " <th>Precision</th>\n", + " <th>Recall</th>\n", + " <th>F1</th>\n", + " <th>Accuracy</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>25</td>\n", + " <td>0.173569</td>\n", + " <td>0.304489</td>\n", + " <td>0.192308</td>\n", + " <td>0.022026</td>\n", + " <td>0.039526</td>\n", + " <td>0.934515</td>\n", + " </tr>\n", + " <tr>\n", + " <td>50</td>\n", + " <td>0.199517</td>\n", + " <td>0.304024</td>\n", + " <td>0.178571</td>\n", + " <td>0.022026</td>\n", + " <td>0.039216</td>\n", + " <td>0.934852</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/seqeval/metrics/v1.py:57: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, msg_start, len(result))\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2e856097d49e4ae4870ba0388da5a2a9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b8e49153d298460e8059508d7e393dd3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "('my_awesome_wnut_model/tokenizer_config.json',\n", + " 'my_awesome_wnut_model/tokenizer.json')" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "training_args = TrainingArguments(\n", + " output_dir=\"my_awesome_wnut_model\",\n", + " learning_rate=2e-5,\n", + " per_device_train_batch_size=16,\n", + " per_device_eval_batch_size=32,\n", + " max_steps=50,\n", + " eval_strategy=\"steps\",\n", + " eval_steps=25,\n", + " load_best_model_at_end=False,\n", + " push_to_hub=False,\n", + " logging_steps=10,\n", + " dataloader_pin_memory=False,\n", + " report_to=\"none\",\n", + ")\n", + "\n", + "small_train = tokenized_wnut[\"train\"].shuffle(seed=42).select(range(1000))\n", + "small_eval = tokenized_wnut[\"test\"].shuffle(seed=42).select(range(300))\n", + "\n", + "trainer = Trainer(\n", + " model=model,\n", + " args=training_args,\n", + " train_dataset=small_train,\n", + " eval_dataset=small_eval,\n", + " data_collator=data_collator,\n", + " processing_class=tokenizer,\n", + " compute_metrics=compute_metrics,\n", + ")\n", + "\n", + "trainer.train()\n", + "trainer.save_model(\"my_awesome_wnut_model\")\n", + "tokenizer.save_pretrained(\"my_awesome_wnut_model\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "452c3599", + "metadata": {}, + "source": [ + "### Inference" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "11538eb9", + "metadata": {}, + "outputs": [], + "source": [ + "text = \"The Golden State Warriors are an American professional basketball team based in San Francisco.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "d0a2690b", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e4087e0630bd4714a7dd8fafb93ec352", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/102 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "[{'entity': 'B-location',\n", + " 'score': np.float32(0.19155827),\n", + " 'index': 2,\n", + " 'word': 'golden',\n", + " 'start': 4,\n", + " 'end': 10},\n", + " {'entity': 'B-location',\n", + " 'score': np.float32(0.21341935),\n", + " 'index': 13,\n", + " 'word': 'san',\n", + " 'start': 80,\n", + " 'end': 83},\n", + " {'entity': 'B-location',\n", + " 'score': np.float32(0.18377033),\n", + " 'index': 14,\n", + " 'word': 'francisco',\n", + " 'start': 84,\n", + " 'end': 93}]" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from transformers import pipeline\n", + "\n", + "classifier = pipeline(\"ner\", model=\"my_awesome_wnut_model\")\n", + "classifier(text)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/translation.ipynb b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/translation.ipynb new file mode 100644 index 0000000..f0d9dc1 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/transformer-work/translation.ipynb @@ -0,0 +1,487 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "19a5cea0", + "metadata": {}, + "outputs": [], + "source": [ + "from huggingface_hub import notebook_login\n", + "\n", + "notebook_login()" + ] + }, + { + "cell_type": "markdown", + "id": "e42a4569", + "metadata": {}, + "source": [ + "### Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "27dba04c", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "books = load_dataset(\"Helsinki-NLP/opus_books\", \"en-fr\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cf42075d", + "metadata": {}, + "outputs": [], + "source": [ + "books = books[\"train\"].train_test_split(test_size=0.2)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5aee861d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': '105673',\n", + " 'translation': {'en': '\"Why? I have no idea; but listen to me.\"',\n", + " 'fr': \"-- Pourquoi ?... je n'en sais rien, monsieur le consul, répondit le détective, mais écoutez-moi. »\"}}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "books[\"train\"][0]" + ] + }, + { + "cell_type": "markdown", + "id": "2c5ca35a", + "metadata": {}, + "source": [ + "### Tokenizer" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "feacdc90", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "checkpoint = \"google-t5/t5-small\"\n", + "tokenizer = AutoTokenizer.from_pretrained(checkpoint)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "1b6186b7", + "metadata": {}, + "outputs": [], + "source": [ + "source_lang = \"en\"\n", + "target_lang = \"fr\"\n", + "prefix = \"translate English to French: \"\n", + "\n", + "def preprocess_function(examples):\n", + " inputs = [prefix + example[source_lang] for example in examples[\"translation\"]]\n", + " targets = [example[target_lang] for example in examples[\"translation\"]]\n", + " model_inputs = tokenizer(inputs, text_target=targets, max_length=128, truncation=True)\n", + " return model_inputs" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "242b984f", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7be9de43330b4c4a970fc8005c9bfe4b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/101668 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e496ce3fb51c413a9612764e6e0b0ff0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map: 0%| | 0/25417 [00:00<?, ? examples/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tokenized_books = books.map(preprocess_function, batched=True)" + ] + }, + { + "cell_type": "markdown", + "id": "13654fde", + "metadata": {}, + "source": [ + "### Data Collator" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d6169905", + "metadata": {}, + "outputs": [], + "source": [ + "from transformers import DataCollatorForSeq2Seq\n", + "\n", + "data_collator = DataCollatorForSeq2Seq(tokenizer=tokenizer, model=checkpoint)\n" + ] + }, + { + "cell_type": "markdown", + "id": "9c24c61a", + "metadata": {}, + "source": [ + "### Evaluate" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "907bd725", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7b7ef15635414d55b03814a999afc47f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Downloading builder script: 0.00B [00:00, ?B/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import evaluate\n", + "\n", + "metric = evaluate.load(\"sacrebleu\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "2dc59fcc", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "def postprocess_text(preds, labels):\n", + " preds = [pred.strip() for pred in preds]\n", + " labels = [[label.strip()] for label in labels]\n", + " return preds, labels\n", + "\n", + "def compute_metrics(eval_preds):\n", + " preds, labels = eval_preds\n", + " if isinstance(preds, tuple):\n", + " preds = preds[0]\n", + "\n", + " preds = np.where(preds != -100, preds, tokenizer.pad_token_id)\n", + " decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)\n", + "\n", + " labels = np.where(labels != -100, labels, tokenizer.pad_token_id)\n", + " decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)\n", + "\n", + " decoded_preds, decoded_labels = postprocess_text(decoded_preds, decoded_labels)\n", + "\n", + " result = metric.compute(predictions=decoded_preds, references=decoded_labels)\n", + " result = {\"bleu\": result[\"score\"]}\n", + "\n", + " prediction_lens = [np.count_nonzero(pred != tokenizer.pad_token_id) for pred in preds]\n", + " result[\"gen_len\"] = np.mean(prediction_lens)\n", + " result = {k: round(v, 4) for k, v in result.items()}\n", + " return result" + ] + }, + { + "cell_type": "markdown", + "id": "d8d54480", + "metadata": {}, + "source": [ + "### Train" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "bd9ddf6b", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5aa86d79d56c41df8a2187e8cb44e8e0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/131 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from transformers import AutoModelForSeq2SeqLM, Seq2SeqTrainingArguments, Seq2SeqTrainer\n", + "\n", + "model = AutoModelForSeq2SeqLM.from_pretrained(checkpoint)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "03f2ffe2", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " <div>\n", + " \n", + " <progress value='126' max='126' style='width:300px; height:20px; vertical-align: middle;'></progress>\n", + " [126/126 01:12, Epoch 2/2]\n", + " </div>\n", + " <table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: left;\">\n", + " <th>Epoch</th>\n", + " <th>Training Loss</th>\n", + " <th>Validation Loss</th>\n", + " <th>Bleu</th>\n", + " <th>Gen Len</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <td>1</td>\n", + " <td>No log</td>\n", + " <td>1.991457</td>\n", + " <td>3.522400</td>\n", + " <td>18.056700</td>\n", + " </tr>\n", + " <tr>\n", + " <td>2</td>\n", + " <td>No log</td>\n", + " <td>1.977148</td>\n", + " <td>3.689400</td>\n", + " <td>18.020000</td>\n", + " </tr>\n", + " </tbody>\n", + "</table><p>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/transformers/generation/utils.py:1616: UserWarning: Using the model-agnostic default `max_length` (=21) to control the generation length. We recommend setting `max_new_tokens` to control the maximum length of the generation.\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c6e06a5e1e8c42c78571e299232a4806", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/hedon/mycode/ai/daedalus/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/.venv/lib/python3.11/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", + " super().__init__(loader)\n" + ] + }, + { + "data": { + "text/plain": [ + "TrainOutput(global_step=126, training_loss=2.1918688577318948, metrics={'train_runtime': 73.3251, 'train_samples_per_second': 27.276, 'train_steps_per_second': 1.718, 'total_flos': 50992138420224.0, 'train_loss': 2.1918688577318948, 'epoch': 2.0})" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "training_args = Seq2SeqTrainingArguments(\n", + " output_dir=\"my_awesome_opus_books_model\",\n", + " eval_strategy=\"epoch\",\n", + " learning_rate=2e-5,\n", + " per_device_train_batch_size=16,\n", + " per_device_eval_batch_size=16,\n", + " weight_decay=0.01,\n", + " save_total_limit=3,\n", + " num_train_epochs=2,\n", + " predict_with_generate=True,\n", + " fp16=False, #change to bf16=True for XPU\n", + " push_to_hub=False,\n", + ")\n", + "\n", + "small_train_dataset = tokenized_books[\"train\"].select(range(1000))\n", + "small_eval_dataset = tokenized_books[\"test\"].select(range(300))\n", + "\n", + "trainer = Seq2SeqTrainer(\n", + " model=model,\n", + " args=training_args,\n", + " train_dataset=small_train_dataset,\n", + " eval_dataset=small_eval_dataset,\n", + " processing_class=tokenizer,\n", + " data_collator=data_collator,\n", + " compute_metrics=compute_metrics,\n", + ")\n", + "\n", + "trainer.train()" + ] + }, + { + "cell_type": "markdown", + "id": "8c158354", + "metadata": {}, + "source": [ + "### Inference" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "cc4ef018", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f245c990f57a465a827667e9a043328a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Loading weights: 0%| | 0/131 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] Both `max_new_tokens` (=40) and `max_length`(=20) seem to have been set. `max_new_tokens` will take precedence. Please refer to the documentation for more information. (https://huggingface.co/docs/transformers/main/en/main_classes/text_generation)\n" + ] + }, + { + "data": { + "text/plain": [ + "\"Les légumes partagent les ressources avec des bactéries fixateurs d'azote.\"" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text = \"translate English to French: Legumes share resources with nitrogen-fixing bacteria.\"\n", + "\n", + "from transformers import AutoTokenizer\n", + "\n", + "model_path = \"my_awesome_opus_books_model/checkpoint-126\"\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(model_path)\n", + "inputs = tokenizer(text, return_tensors=\"pt\").input_ids\n", + "\n", + "from transformers import AutoModelForSeq2SeqLM\n", + "\n", + "model = AutoModelForSeq2SeqLM.from_pretrained(model_path)\n", + "outputs = model.generate(inputs, max_new_tokens=40, do_sample=True, top_k=30, top_p=0.95)\n", + "tokenizer.decode(outputs[0], skip_special_tokens=True)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hugging-face-course-learning (3.11.11)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/uv.lock b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/uv.lock new file mode 100644 index 0000000..00db3a4 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/demo/hugging-face-course-learning/uv.lock @@ -0,0 +1,3888 @@ +version = 1 +revision = 1 +requires-python = ">=3.11" +resolution-markers = [ + "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 != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "absl-py" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/c7/8de93764ad66968d19329a7e0c147a2bb3c7054c554d4a119111b8f9440f/absl_py-2.4.0.tar.gz", hash = "sha256:8c6af82722b35cf71e0f4d1d47dcaebfff286e27110a99fc359349b247dfb5d4", size = 116543 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/a6/907a406bb7d359e6a63f99c313846d9eec4f7e6f7437809e03aa00fa3074/absl_py-2.4.0-py3-none-any.whl", hash = "sha256:88476fd881ca8aab94ffa78b7b6c632a782ab3ba1cd19c9bd423abc4fb4cd28d", size = 135750 }, +] + +[[package]] +name = "accelerate" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/75/94cd5d389649578aca399e5aa822637eec18319a1dadc400ffe2f9a7493f/accelerate-1.14.0.tar.gz", hash = "sha256:41b9c4377a54e0b460a959b0defa1b736e4ca0a2373252d9a539964c2afe3c8d", size = 412167 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/db/253133d7e7cb40d3af384bb2f5c0b4a2b7fdcffbc95c688cc67a20a3c103/accelerate-1.14.0-py3-none-any.whl", hash = "sha256:e94390c2863b873be18f623f9df48a0d8fe5eff13ea7f1a00092b0a7904888c6", size = 389246 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/dd/bf526e6f0a1120dd6f2df2e97bacfe4d358f13d17a0ff5847301a1375a51/aiohttp-3.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2", size = 765225 }, + { url = "https://files.pythonhosted.org/packages/8f/e1/a2872aa55495a70f61310d411541c6ee23812d9a884e000c716e1bc3edbf/aiohttp-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f", size = 518743 }, + { url = "https://files.pythonhosted.org/packages/5b/e7/c60c7b209e509cc787de3cea0550a518538cfc08003e1c1e14c1c63fff71/aiohttp-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8", size = 514139 }, + { url = "https://files.pythonhosted.org/packages/5b/8d/614ace2f579702c9840ab1e1447fd8509e35b0b904f7196418fa2f57b25d/aiohttp-3.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04", size = 1784088 }, + { url = "https://files.pythonhosted.org/packages/49/e0/726e90f99542bf292f81a96a12cc4847deb86f3ccf62c6f4014a201f4d33/aiohttp-3.14.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8", size = 1737835 }, + { url = "https://files.pythonhosted.org/packages/0b/4b/d176d5c4db9d33dacf0543102ea59503bc1d528af4cfd0b719949ca49389/aiohttp-3.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6", size = 1842801 }, + { url = "https://files.pythonhosted.org/packages/dc/d6/5a99b563690ea0cbed912ae94a2ce33993a5709a651a3a4fe761e7dd973a/aiohttp-3.14.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af", size = 1929992 }, + { url = "https://files.pythonhosted.org/packages/76/7f/a987b14a3859094b3cea3f4825219c3e5536242564af6e3f9c2f6c994eb2/aiohttp-3.14.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730", size = 1786989 }, + { url = "https://files.pythonhosted.org/packages/f1/1a/420e5c85a3e73349372ed22ce0b6af86bfa6ce16a4b20a64a2e94608c781/aiohttp-3.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621", size = 1640129 }, + { url = "https://files.pythonhosted.org/packages/a7/80/18a592ed3be0a402cc03670bd72ee1f8563ddbe1d8d5542dbf868f274136/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee", size = 1756576 }, + { url = "https://files.pythonhosted.org/packages/ec/0b/8b3d5713373858ff71a617daf6e3b0e81ad63e79d09a3cf2f6b6b983939c/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573", size = 1754668 }, + { url = "https://files.pythonhosted.org/packages/9f/49/fd564575cf225821d7ba5a117cb8bc27213d8a7e1811162afb43ae077039/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7", size = 1817019 }, + { url = "https://files.pythonhosted.org/packages/ed/1b/e850c9ae6fc91356552ae668bb6c51e93fa29c8aef13398a10b56678557f/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf", size = 1631638 }, + { url = "https://files.pythonhosted.org/packages/eb/94/3c337ba72451a89806ace6f75bddc92bafc5b8d53d90115a512858024b63/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85", size = 1835660 }, + { url = "https://files.pythonhosted.org/packages/2b/9c/9c18cf367a0498212d9ba7daf990b504a5e8ae064cda4b504e2647c89c03/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3", size = 1775698 }, + { url = "https://files.pythonhosted.org/packages/b5/63/a251a9d2a6cb45065b2ddc0bde2b3dd10108740a9a42f632c66405a761a2/aiohttp-3.14.1-cp311-cp311-win32.whl", hash = "sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126", size = 458386 }, + { url = "https://files.pythonhosted.org/packages/17/ca/69274c51dcd6e8947d77b2806cf47a4a15f2c846e2cbeb1882547d3da283/aiohttp-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5", size = 483406 }, + { url = "https://files.pythonhosted.org/packages/2c/8a/c25904f77690c3688ec140f87591ef11a0cfe36bf3d5c0f1f38056fb62b3/aiohttp-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b", size = 452987 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/3e/61/a0c0a8f327a9c52095cdd8e312391b00d3ed64ab6c72bb5c33d8ec251cf7/aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4", size = 452771 }, + { url = "https://files.pythonhosted.org/packages/df/d9/ea367c75f16ac9c6cdc8febb25e8318fa21a2b1bc8d6514d4b2d890bface/aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271", size = 479873 }, + { url = "https://files.pythonhosted.org/packages/03/64/8d96784a7851156db8a4c6c3f6f91042fdf39fb15a4cc38c8b3c14833c45/aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847", size = 448073 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/e4/ae/dbce10533d3896d544d5053939ed75b7dc31a1b0973d959b1b5ae21028d6/aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780", size = 452662 }, + { url = "https://files.pythonhosted.org/packages/7b/d9/0bf1a19362c32f06229da5e7ddfcec91f93474d6307f7a2d3135e9c674dc/aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a", size = 479748 }, + { url = "https://files.pythonhosted.org/packages/22/0a/62e7232dc9484fbec112ceb32efb6a624cc7994ec6e2b019286f17c4e8f2/aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8", size = 447723 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/f2/1e/fbeb7af9210a67ac0f9c9bec0f8f4568497924e33137a3d5b48e1cf85f3f/aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0", size = 457773 }, + { url = "https://files.pythonhosted.org/packages/f0/2b/13e8d741a9ec5db7d900c060554cf8352ab85e44e2a4469ebb9d377bda17/aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719", size = 485001 }, + { url = "https://files.pythonhosted.org/packages/df/30/491acfa2c4d6c3ff59c49a14fc1b50be3241e25bbb0c84c09e2da4d11395/aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec", size = 453809 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/7b/3c/bb4a7cba26956cb3da4553cc2056cf67be5b5ff6e6d8fa4fbdff73bfb7ae/aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365", size = 494166 }, + { url = "https://files.pythonhosted.org/packages/8a/84/ec80c2c1f66a952555a9f86df6b33af65108a6febfa0471b69013a12f807/aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9", size = 530255 }, + { url = "https://files.pythonhosted.org/packages/2a/71/6e22be134a4061ada85a92951b842f2657f17d926b727f3f94c56ae963d6/aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6", size = 469640 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353 }, +] + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, +] + +[[package]] +name = "argon2-cffi" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argon2-cffi-bindings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, +] + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393 }, + { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328 }, + { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269 }, + { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558 }, + { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364 }, + { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637 }, + { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934 }, + { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158 }, + { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597 }, + { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231 }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, +] + +[[package]] +name = "arrow" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, +] + +[[package]] +name = "asttokens" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, +] + +[[package]] +name = "async-lru" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/1f/989ecfef8e64109a489fff357450cb73fa73a865a92bd8c272170a6922c2/async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6", size = 16332 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315", size = 8403 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548 }, +] + +[[package]] +name = "babel" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924 }, +] + +[[package]] +name = "bleach" +version = "6.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/3c/e12ac860709702bd5ebeb9b56a4fe334f1001246ee1b8f2b7ee28912df7d/bleach-6.4.0.tar.gz", hash = "sha256:4202482733d85cedd04e59fcb2f89f4e4c7c385a78d3c3c23c30446843a37452", size = 204857 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/9d/40b6267367182187139a4000b82a3b287d84d745bccd808e75d916920e9d/bleach-6.4.0-py3-none-any.whl", hash = "sha256:4b6b6a54fff2e69a3dde9d21cc6301220bee3c3cb792187d11403fd795031081", size = 165109 }, +] + +[package.optional-dependencies] +css = [ + { name = "tinycss2" }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344 }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560 }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613 }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476 }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374 }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597 }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574 }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971 }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972 }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078 }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076 }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820 }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932 }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557 }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705 }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419 }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901 }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742 }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061 }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239 }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173 }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841 }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304 }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455 }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036 }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739 }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277 }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819 }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281 }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460 }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330 }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085 }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819 }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541 }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634 }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827 }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168 }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018 }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, +] + +[[package]] +name = "cuda-bindings" +version = "13.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/6b/457ca12dad3ee9bfcc9a545cfd6b64b359ba49de40f776f6e028e678f262/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5879712accf6e14bb01aa5e67440eb84998b8d104b509cc7a6dc0b8f656a474", size = 6053539 }, + { url = "https://files.pythonhosted.org/packages/95/7a/c5e3c34a409b148f5c0f5a4ea374158f95d488862c1dffedf9aa5c639df9/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04436a9364059c84b8f9636f359eccda1cf814341f5b670c71d80d2f79dbc708", size = 6674166 }, + { url = "https://files.pythonhosted.org/packages/ce/67/5e7dba1ba576dd73da5dee894ca076ca5e959450dfff66d6d510a255d1f7/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7855c4868aabc0cfae28abbe83d56734bdfbd08f08fc234ac1912a12858bf49", size = 6025351 }, + { url = "https://files.pythonhosted.org/packages/39/2a/6d2e9047d1fb243dbaa364b01e0297534b9ed7fd27dba1c9f361519cf69b/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e32d08f71ebcdf00f0f41eab2eb37e8da94c8ed411cc9f7f7a019ce6b34abe3a", size = 6657965 }, + { url = "https://files.pythonhosted.org/packages/cc/6e/2394f8163360f8391f8f1b7e72d300a82724edb81a7b7084c799fbd4c91f/cuda_bindings-13.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9efb21c1ee64981e184b9e0ba5eb3179e5ba3d4b51665a6cb52b8ef3d01a7cbf", size = 5920504 }, + { url = "https://files.pythonhosted.org/packages/34/c2/ef9b6a63f7dc432712a462c816662e662e00d38caa9b861c8c2588195d03/cuda_bindings-13.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2732904099e0a4d4db774a5fc6d91ee95fae065b4d2ecabb4968c5fe2406c9d7", size = 6476660 }, + { url = "https://files.pythonhosted.org/packages/b1/81/bff68ce829999c1e4209c761bbf903b1c06ec570416ddb25020864ad5907/cuda_bindings-13.3.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ab2f74ed65bfef4163ba07a8db16f1085e0729291db12a2423aff84ee8278b8", size = 6013639 }, + { url = "https://files.pythonhosted.org/packages/d4/e0/c8a1f0c8f9ffdea4f5fe6dbab89b326cef4d85caf489dad39e209da89416/cuda_bindings-13.3.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd4c814d311ec08c981f6dded1dbe7d4b371067ee4f6c14cccec4bde9590f80", size = 6534419 }, + { url = "https://files.pythonhosted.org/packages/52/b8/83b1f563925b290f2d11a01a77a84013ba56052fe3653a5bef3ccfbb43d6/cuda_bindings-13.3.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3c772dfff49681541d59630c90f858e173ac926b9c593a2b7123f2a1043cc76", size = 5809771 }, + { url = "https://files.pythonhosted.org/packages/12/20/e79b4bfe98f075195afb6343d41c498f9dbd2d161d7021d4d28bceb83581/cuda_bindings-13.3.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36febb7c1079d68a981dbbd8d5a67235b399802b82075c9388624719607e52b9", size = 6358584 }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c8/26f2e4aae92f11522a96043892ba39a90eac610d5242523aa863212bc1c7/cuda_pathfinder-1.5.5-py3-none-any.whl", hash = "sha256:0228c023f95d1480f143ef5c8922d27a2ab052087a942e81dc289c9eb8f91689", size = 51671 }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364 }, +] + +[package.optional-dependencies] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand", marker = "sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "datasets" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/85/ce4f780c32f7e36d71257f1c27e8ba898ebe379cb54f211f5f2013f2c219/datasets-5.0.0.tar.gz", hash = "sha256:83dbbbdb07a33b82192b8c419deb18739b138ee2ce1a322d55ce6b100954ec1a", size = 631708 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/66/73034ad30b59f13439b75e620989dacba4c047256e358ba7c2e9ec98ea22/datasets-5.0.0-py3-none-any.whl", hash = "sha256:7dd34927a0fd7046e98aad5cb9430e699c373238a15befa7b9bf22b991a7fee6", size = 555084 }, +] + +[[package]] +name = "debugpy" +version = "1.8.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/aa/12037145b7a56eaa5b29b41872f7a21b538e807e13f32c4d3c46e59be084/debugpy-1.8.21.tar.gz", hash = "sha256:a3c53278e84c94e11bd87c53970ec391d1a67396c8b22609fcac576520e611a6", size = 1697577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/fb/cbf306d6e07a313a91e7171a98669054502840931432c227cfd505ee367f/debugpy-1.8.21-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:da456226c7b4c69e35dbe35dcee6623d912000a77816db7856a41af1c72a0264", size = 2203120 }, + { url = "https://files.pythonhosted.org/packages/aa/57/aa739bd4ad2cbf96aeb1b20b56918ddd5ae4c28b68709bfcd327f02123ee/debugpy-1.8.21-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:f68b891688e61bdc08b8d364d919ff0051e0b94657b39dcd027bc3173edb7cdc", size = 3059958 }, + { url = "https://files.pythonhosted.org/packages/a8/31/453d2c9a23d133fe2c8ec7ca1d816ded52a913487fe3ffef7c01b4b706af/debugpy-1.8.21-cp311-cp311-win32.whl", hash = "sha256:f843a8b08c2edeaf9b1582eed4f25441af21a297c22ff16bf76a662557aa9c9e", size = 5236515 }, + { url = "https://files.pythonhosted.org/packages/60/94/6660de2f2d7bf388f229335ba4637646eebabdbf38564cb439a95a9193c9/debugpy-1.8.21-cp311-cp311-win_amd64.whl", hash = "sha256:84c564d8cc701d41843b29a92814c1f1bef6798724ca9d675c284ad9f6a547d7", size = 5256138 }, + { url = "https://files.pythonhosted.org/packages/a2/df/bf625547431a9cadc9f4cbfeda38866e2b17f6aed147b625377e87834449/debugpy-1.8.21-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:9f96713896f39c3dff0ee841f47320c3f2983d33c341e009361bb0ebc79adc4e", size = 2483609 }, + { url = "https://files.pythonhosted.org/packages/bf/09/59324b903599031ff9faaec1758292409f6561a0ec2492fe4b703327705a/debugpy-1.8.21-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:c193d474f0a211191f2b4449d2d06157c689013035bd952f3b617e0ef422b176", size = 3968900 }, + { url = "https://files.pythonhosted.org/packages/14/cd/27f65b805d7fe005c44e1a36b9183ecdfbcdbf9d3e721a5115d461ecc7ee/debugpy-1.8.21-cp312-cp312-win32.whl", hash = "sha256:4743373c1cac7f9e74a1b9915bf1dbe0e900eca657ffb170ae07ac8363205ae9", size = 5336340 }, + { url = "https://files.pythonhosted.org/packages/77/1d/c84e30c0c674184948b66f076ab271c01d940618a2824c23cd035a27bc20/debugpy-1.8.21-cp312-cp312-win_amd64.whl", hash = "sha256:bd7ba9dd3daa7c2f942c6ca8d4695a16bf9ac16b63615261c7982bc74f7ed20c", size = 5374751 }, + { url = "https://files.pythonhosted.org/packages/77/6b/d817e1f8cc77aa055d37fba092e0febfdff40fe652d8d53d4cd7a86ad98d/debugpy-1.8.21-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:13678151fc401e2d68c9880b91e28714f797d40422994572b24560ef80910a88", size = 2477398 }, + { url = "https://files.pythonhosted.org/packages/48/57/412421516afc3055fa577516f00beec3d663f9b0ab330639547ae6c57720/debugpy-1.8.21-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:ecbd158386c31ffe71d46f72d44d56e66331ab9b16cad649156d514368f23ab2", size = 3962096 }, + { url = "https://files.pythonhosted.org/packages/c1/62/2c616337cf6ba7b07ebbc97f02c6c945a8e2f76b365e33ee809c32ee36d1/debugpy-1.8.21-cp313-cp313-win32.whl", hash = "sha256:2c2ae706dec41d99a9ca1f7ebc987a83e65578363be6f6b3ac9067504917fae1", size = 5336288 }, + { url = "https://files.pythonhosted.org/packages/f8/99/9175103392f84c4b1bf7622888cdc68da07f0ff7d9e581266428f6776033/debugpy-1.8.21-cp313-cp313-win_amd64.whl", hash = "sha256:aa648733047443eb1d07682c4ef287d36a54507b643ffdf38b09a3ef002c72a0", size = 5376567 }, + { url = "https://files.pythonhosted.org/packages/ce/3d/f4bbb323a548bfab2af3d6b4ffd9bf22636e55956a1285d317a1de643aad/debugpy-1.8.21-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9bb2a685287a2ac9b181cde89edcec64845cb51de7faaa75badb9a698bc24782", size = 2477209 }, + { url = "https://files.pythonhosted.org/packages/8c/2d/6e7ec524984a1702777868de49a4c53202bddac2a432a76a093469587750/debugpy-1.8.21-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:3d6922439bf33fd38a3e2c447869ebc7b97da5cd3d329ff1ef9bc06c4903437e", size = 3927115 }, + { url = "https://files.pythonhosted.org/packages/97/47/d1aa6d64005a98a9144647d99306b419396f9ad7bf1d73c119e17a81fb4d/debugpy-1.8.21-cp314-cp314-win32.whl", hash = "sha256:15d4963bd5ffa48f0da0947fd06757fa7621945048a14ad7705431566d3c0e7c", size = 5336724 }, + { url = "https://files.pythonhosted.org/packages/5f/67/b905b90d163af11878c1af8abafa4a25206335e112e284e413454543a6da/debugpy-1.8.21-cp314-cp314-win_amd64.whl", hash = "sha256:fe0744a12353406de0ae8ccff0d0a4a666f00801a3db8fd04e7a5f761cd520e8", size = 5373803 }, + { url = "https://files.pythonhosted.org/packages/95/51/67e7cf11a53e40694f720457d5b3a1cdaaa3d5a9a633e482f225456b93ff/debugpy-1.8.21-py2.py3-none-any.whl", hash = "sha256:b1e37d333663c8851516a47364ef473da127f9caebe4417e6df6f5825a7e9a92", size = 5352888 }, +] + +[[package]] +name = "decorator" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365 }, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019 }, +] + +[[package]] +name = "evaluate" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "datasets" }, + { name = "dill" }, + { name = "fsspec", extra = ["http"] }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/d0/0c17a8e6e8dc7245f22dea860557c32bae50fc4d287ae030cb0e8ab8720f/evaluate-0.4.6.tar.gz", hash = "sha256:e07036ca12b3c24331f83ab787f21cc2dbf3631813a1631e63e40897c69a3f21", size = 65716 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/af/3e990d8d4002bbc9342adb4facd59506e653da93b2417de0fa6027cb86b1/evaluate-0.4.6-py3-none-any.whl", hash = "sha256:bca85bc294f338377b7ac2f861e21c308b11b2a285f510d7d5394d5df437db29", size = 84069 }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, +] + +[[package]] +name = "fastjsonschema" +version = "2.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, +] + +[[package]] +name = "filelock" +version = "3.29.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/dc/be6cbe99670cd6e4ad387123647cb08e0c32975e223f82551e914c5568a6/filelock-3.29.4.tar.gz", hash = "sha256:10cdb3656fc44541cdf30652a93fb10ec6b05325620eb316bd26893e4201538a", size = 63028 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/37/a065dc3bd6e49423a6532c642ca7378d3f467b1ef44c2800c937af7f9739/filelock-3.29.4-py3-none-any.whl", hash = "sha256:dac1648087d5115554850d113e7dd8c83ab2d38e3435dde2d4f163847e57b767", size = 42757 }, +] + +[[package]] +name = "fqdn" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912 }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046 }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119 }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067 }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160 }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544 }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797 }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886 }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731 }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544 }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806 }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382 }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647 }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064 }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985 }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591 }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628 }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882 }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492 }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034 }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536 }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330 }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676 }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451 }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507 }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402 }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + +[[package]] +name = "hf-xet" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/2d/57fd21d84d93efb4bd0b962383790e19dd1bc053501b4264c97903b4e83e/hf_xet-1.5.1.tar.gz", hash = "sha256:51ef4500dab3764b41135ee1381a4b62ce56fc54d4c92b719b59e597d6df5bf6", size = 876636 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/ee/dd9ba7beae1005e54131b7d45263cc74c8a066d47d354e6d58ae9445a388/hf_xet-1.5.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:dbf48c0d02cf0b2e568944330c60d9120c272dabe013bd892d48e25bc6797577", size = 4069485 }, + { url = "https://files.pythonhosted.org/packages/b6/bc/9cae6cfeb4e03070874e73e5c97c66eb90369d3206b6a2b1ef5f96520888/hf_xet-1.5.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78e4e5192ad2b674c2e1160b651cb9134db974f8ae1835bdfbfb0166b894a43", size = 3838493 }, + { url = "https://files.pythonhosted.org/packages/ba/b4/d5c01e0eb6d9f2ca2dacd84d0d1b71e6cfbb2ef3208c968528e010e9b3d7/hf_xet-1.5.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6f7a04a8ad962422e225bc49fbbac99dc1806764b1f3e54dbd154bffa7593947", size = 4505658 }, + { url = "https://files.pythonhosted.org/packages/76/c5/29a7598c0c6383c523dc22186d577f4e04267a626cd95ae60f67c00bfe66/hf_xet-1.5.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d48199c2bf4f8df0adc55d31d1368b6ec0e4d4f45bc86b08038089c23db0bed8", size = 4292822 }, + { url = "https://files.pythonhosted.org/packages/04/9a/dceaf6ca69390126b86ea825fb354b93d01163199070b7bd849225de9468/hf_xet-1.5.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:97f212a88d14bbf573619a74b7fecb238de77d08fc702e54dec6f78276ca3283", size = 4491255 }, + { url = "https://files.pythonhosted.org/packages/48/a7/e5a7afaacf6c1791fdbeeac42951fb81c3d2bc482992b115dedcc86d963e/hf_xet-1.5.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f61e3665892a6c8c5e765395838b8ddf36185da835253d4bc4509a81e49fb342", size = 4711062 }, + { url = "https://files.pythonhosted.org/packages/53/49/2802f8433c9742ce281bddc1e65c02c32268ca3098d66828b05e12e45ee2/hf_xet-1.5.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f4ad3ebd4c32dd2b27099d69dc7b2df821e30767e46fb6ee6a0713778243b8ff", size = 4017205 }, + { url = "https://files.pythonhosted.org/packages/9e/5a/50c71195b9fb883659f596e7252faf4c18c58e753a9013bdbf9bac5d2250/hf_xet-1.5.1-cp313-cp313t-win_arm64.whl", hash = "sha256:8298485c1e36e7e67cbd01eeb1376619b7af43d4f1ec245caae306f890a8a32d", size = 3845426 }, + { url = "https://files.pythonhosted.org/packages/05/24/5e0c28f80371c17d49fed004597d9d132cb75c1f6f53db2cb95f459d2312/hf_xet-1.5.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:3474760d10e3bb6f92ff3f024fcb00c0b3e4001e9b035c7483e49a5dd17aa70f", size = 4069676 }, + { url = "https://files.pythonhosted.org/packages/d2/17/261ba565b6a4d960fb478f61fdf919c0be5824645aaf1c319eca660c1611/hf_xet-1.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6762d89b9e3267dfd502b29b2a327b4525f33b17e7b509a78d94e2151a30ce30", size = 3838509 }, + { url = "https://files.pythonhosted.org/packages/4e/44/7ffdc2e184b0d41fc0f683ba3936ef669ab63cf242cf36ef50e57d683668/hf_xet-1.5.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bf67e6ed10260cef62e852789dc91ebb03f382d5bdc4b1dbeb64763ea275e7d6", size = 4505881 }, + { url = "https://files.pythonhosted.org/packages/63/b6/788060d5aa4d5e671f1a31bf69624c314eb2d8babab3aa562f9e5d53444e/hf_xet-1.5.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c6b6cd08ca095058780b50b8ce4d6cbf6787bcf27841705d58a9d32246e3e47a", size = 4292995 }, + { url = "https://files.pythonhosted.org/packages/22/93/c5540cbd6b55529b7dc42f6734e88cebee21aefbea34128b66229df56c57/hf_xet-1.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e1af0de8ca6f190d4294a28b88023db64a1e2d1d719cab044baf75bec569e7a9", size = 4491570 }, + { url = "https://files.pythonhosted.org/packages/03/f3/9d8ceab30f44f36c1679b1b8683054c71a0dadc787dbf07421891742d3ca/hf_xet-1.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4f561cbbb92f80960772059864b7fb07eae879adde1b2e781ec6f86f6ac26c59", size = 4711565 }, + { url = "https://files.pythonhosted.org/packages/cd/54/27ed9a5e2cc583b4df82f75a03a4df8dbf55f5a9fa1f47f1fadfb20dbeac/hf_xet-1.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:e7dbb40617410f432182d918e37c12303fe6700fd6aa6c5964e30a535a4461d6", size = 4017343 }, + { url = "https://files.pythonhosted.org/packages/ae/12/ecb2fc8d45e767580e3a37faa97cb895608b614965567efb4f18cff67e27/hf_xet-1.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6071d5ccb4d8d2cbd5fea5cc798da4f0ba3f44e25369591c4e89a4987050e61d", size = 3845716 }, + { url = "https://files.pythonhosted.org/packages/7a/d8/5e54cf37434759d1f4f2ba9b66077ff9d4c4e1f37b6bd7975da5c40d94ab/hf_xet-1.5.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6abd35c3221eff63836618ddfb954dcf84798603f71d8e33e3ed7b04acfdbe6e", size = 4077794 }, + { url = "https://files.pythonhosted.org/packages/35/94/4b2ecfbad8f8b04701a23aefb62f540b9137d058b7e1dbef16a32676f0e9/hf_xet-1.5.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:94e761bbd266bf4c03cee73753916062665ce8365aa40ed321f45afcb934b41e", size = 3845354 }, + { url = "https://files.pythonhosted.org/packages/de/cc/f99f4bc7295023d7bd9ebbfd51f75cc530ca262c1227666268b8208f4b77/hf_xet-1.5.1-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:892e3a3a3aecc12aded8b93cf4f9cd059282c7de0732f7d55026f3abdf474350", size = 4514864 }, + { url = "https://files.pythonhosted.org/packages/cd/6e/21f7e5a2381278bd3b7b7a5a4d90038518bb6308a0c1daf5d9f8268bb178/hf_xet-1.5.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a93df2039190502835b1db8cd7e178b0b7b889fe9ab51299d5ced26e0dd879a4", size = 4303784 }, + { url = "https://files.pythonhosted.org/packages/35/0e/f992bb6927ac1cb30ef74e62268f551f338bc32b2191f7c96a44c6f7283e/hf_xet-1.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0c97106032ef70467b4f6bc2d0ccc266d7613ee076afc56516c502f87ce1c4a6", size = 4500703 }, + { url = "https://files.pythonhosted.org/packages/fb/d1/90a498d05447980b977b1669246eeeeae4cfb0ea3e7a286eaba627f91bf9/hf_xet-1.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6208adb15d192b90e4c2ad2a27ed864359b2cb0f2494eb6d7c7f3699ac02e2bf", size = 4719498 }, + { url = "https://files.pythonhosted.org/packages/6d/b6/20f99cfe97cc663a711f7b33cc21d4793e51968e9a26125b4afcd77315ba/hf_xet-1.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:f7b3002f95d1c13e24bcb4537baa8f0eb3838957067c91bb4959bc004a6435f5", size = 4026419 }, + { url = "https://files.pythonhosted.org/packages/f9/fa/77453694888f03e5a8c8852d1514a0894d8e81c622d39edbaf308ea0dcf4/hf_xet-1.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:93d090b57b211133f6c0dab0205ef5cb6d89162979ba75a74845045cc3063b8e", size = 3855178 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "hugging-face-course-learning" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "accelerate" }, + { name = "datasets" }, + { name = "evaluate" }, + { name = "ipykernel" }, + { name = "ipywidgets" }, + { name = "jupyter" }, + { name = "nltk" }, + { name = "rouge-score" }, + { name = "sacrebleu" }, + { name = "scikit-learn" }, + { name = "seqeval" }, + { name = "torch" }, + { name = "transformers", extra = ["sentencepiece", "torch"] }, +] + +[package.metadata] +requires-dist = [ + { name = "accelerate", specifier = ">=1.14.0" }, + { name = "datasets", specifier = ">=5.0.0" }, + { name = "evaluate", specifier = ">=0.4.6" }, + { name = "ipykernel", specifier = ">=7.3.0" }, + { name = "ipywidgets", specifier = ">=8.1.8" }, + { name = "jupyter", specifier = ">=1.1.1" }, + { name = "nltk", specifier = ">=3.9.4" }, + { name = "rouge-score", specifier = ">=0.1.2" }, + { name = "sacrebleu", specifier = ">=2.6.0" }, + { name = "scikit-learn", specifier = ">=1.9.0" }, + { name = "seqeval", specifier = ">=1.2.2" }, + { name = "torch", specifier = ">=2.12.0" }, + { name = "transformers", extras = ["sentencepiece", "torch"], specifier = ">=5.12.0" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/27/629cfe58c582f92ded066c4a07d1a057ff617118ab7973200f770bd853cb/huggingface_hub-1.19.0.tar.gz", hash = "sha256:fd771622182d40977272a923953ee3b1b13538f9f8a7f5d78398f10af0f1c0bd", size = 824721 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a5/558da89f66464d8d0229ff497e8b8666977de2d8cf48c28a2862ecf1250f/huggingface_hub-1.19.0-py3-none-any.whl", hash = "sha256:1dc72e1f6b4d6df6b30eb72e57d00514ef453d660f04af2b87f0e67267f31ee0", size = 693398 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455 }, +] + +[[package]] +name = "ipykernel" +version = "7.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio2" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/c4/e4a38f579de4225a561305666f7541cdabb30075def2aa1ac17bd73c1fb5/ipykernel-7.3.0.tar.gz", hash = "sha256:9acaaaf97d16355166e4085afe9d225bfbdf2b7ef520f9df3be8f2b248275e09", size = 184899 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/02/77b271f5dc58bfbc0b577c877b2365d1ffea2afe66a80c13f2312820348c/ipykernel-7.3.0-py3-none-any.whl", hash = "sha256:897eb64da762549ef610698fca5e9675195ec6ac8ec7f19d81ce1ca20c876057", size = 120583 }, +] + +[[package]] +name = "ipython" +version = "9.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "psutil", marker = "sys_platform != 'emscripten'" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/23/3a27530575643c8bb7bfc757a28e2e7ef80092afbf59a2bc5716320b6602/ipython-9.14.1.tar.gz", hash = "sha256:f913bf74df06d458e46ced84ca506c23797590d594b236fe60b14df213291e7b", size = 4433457 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/22/58818a63eaf8982b67632b1bc20585c811611b15a8da19d6012323dc76a5/ipython-9.14.1-py3-none-any.whl", hash = "sha256:5d4a9ecaa3b10e6e5f269dd0948bdb58ca9cb851899cd23e07c320d3eb11613c", size = 627770 }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 }, +] + +[[package]] +name = "ipywidgets" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, +] + +[[package]] +name = "isoduration" +version = "20.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "arrow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, +] + +[[package]] +name = "jedi" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, +] + +[[package]] +name = "json5" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/7d/05c46a96a78147ae3bf99c2f4169ce144a70220b8d6fcd56f6ec368b8ce9/json5-0.15.0.tar.gz", hash = "sha256:7424d1f1eb1d56da6e3d70643f53619862b4ce81440bdb8ecfd6f875e5ba4a71", size = 53278 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/be/59527c99478aade6bb33a68d72e6e18dd4e6ff6eacfc7d01bdb15bc76912/json5-0.15.0-py3-none-any.whl", hash = "sha256:56636a30c0e8a4665fe2179c0212f32eae3796dea89ea6f649b9436ecdb39618", size = 36570 }, +] + +[[package]] +name = "jsonpointer" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659 }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, +] + +[package.optional-dependencies] +format-nongpl = [ + { name = "fqdn" }, + { name = "idna" }, + { name = "isoduration" }, + { name = "jsonpointer" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "rfc3987-syntax" }, + { name = "uri-template" }, + { name = "webcolors" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, +] + +[[package]] +name = "jupyter" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "ipywidgets" }, + { name = "jupyter-console" }, + { name = "jupyterlab" }, + { name = "nbconvert" }, + { name = "notebook" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657 }, +] + +[[package]] +name = "jupyter-builder" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/df/db5efc4e28803a1421350445e53d85ec571a4e6928e3524ccc39f4e16584/jupyter_builder-1.1.0.tar.gz", hash = "sha256:b996e8af616900f18724fa34883169d869be2205497940bec78a3a1031eb897d", size = 971142 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/fb/53adbc72931b4429275b160044c3e1bcbc8fbb5ea6b8f318a357834842fb/jupyter_builder-1.1.0-py3-none-any.whl", hash = "sha256:d9d5280e8845f726663545c3a6db55bd205127616eeb8958481091d6cba71b6a", size = 912964 }, +] + +[[package]] +name = "jupyter-client" +version = "8.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/dc/5512503b088997c2250b8bf18258fba9d9ce5ead641183700960d3c9d342/jupyter_client-8.9.1.tar.gz", hash = "sha256:a58f730dd9e728ba16ba1d62ebccf7ffe1ebbdbce4e95cfae941b7321ae1f4fa", size = 359256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/6f/56d39bf385c5c27988aebaf0c18a2a17e960575740100973511018bd904e/jupyter_client-8.9.1-py3-none-any.whl", hash = "sha256:0b7a295bc46e8751e9adae84781f726c851c1d911bd793edc4a3bde942e3da81", size = 109828 }, +] + +[[package]] +name = "jupyter-console" +version = "6.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "pyzmq" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510 }, +] + +[[package]] +name = "jupyter-core" +version = "5.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, +] + +[[package]] +name = "jupyter-events" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema", extra = ["format-nongpl"] }, + { name = "packaging" }, + { name = "python-json-logger" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/f8/475c4241b2b75af0deaae453ed003c6c851766dbc44d332d8baf245dc931/jupyter_events-0.12.1.tar.gz", hash = "sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3", size = 62854 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/6c/6fcde0c8f616ed360ffd3587f7db9e225a7e62b583a04494d2f069cf64ea/jupyter_events-0.12.1-py3-none-any.whl", hash = "sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf", size = 19512 }, +] + +[[package]] +name = "jupyter-lsp" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/ff/1e4a61f5170a9a1d978f3ac3872449de6c01fc71eaf89657824c878b1549/jupyter_lsp-2.3.1.tar.gz", hash = "sha256:fdf8a4aa7d85813976d6e29e95e6a2c8f752701f926f2715305249a3829805a6", size = 55677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/e8/9d61dcbd1dce8ef418f06befd4ac084b4720429c26b0b1222bc218685eff/jupyter_lsp-2.3.1-py3-none-any.whl", hash = "sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81", size = 77513 }, +] + +[[package]] +name = "jupyter-server" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "argon2-cffi" }, + { name = "jinja2" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "jupyter-events" }, + { name = "jupyter-server-terminals" }, + { name = "nbconvert" }, + { name = "nbformat" }, + { name = "overrides", marker = "python_full_version < '3.12'" }, + { name = "packaging" }, + { name = "prometheus-client" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "pyzmq" }, + { name = "send2trash" }, + { name = "terminado" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "websocket-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/dc/db3a582633170186f8c8b31298d7eb26ad0eb031a1f53476c258b64eed05/jupyter_server-2.20.0.tar.gz", hash = "sha256:b5778ba337d8015a3dc2b80803ecdd5ac18d3797fddf61a50ea5fb472b4ebe14", size = 756523 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/71/8c002223e873a870f5c41dc69b0a7c922301123e4a31d5d01ecb700aef77/jupyter_server-2.20.0-py3-none-any.whl", hash = "sha256:c3b67c93c471e947c18b5026f04f21614218adb706df8f48227d3ee8e0a7cdcc", size = 393143 }, +] + +[[package]] +name = "jupyter-server-terminals" +version = "0.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "terminado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704 }, +] + +[[package]] +name = "jupyterlab" +version = "4.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-lru" }, + { name = "httpx" }, + { name = "ipykernel" }, + { name = "jinja2" }, + { name = "jupyter-builder" }, + { name = "jupyter-core" }, + { name = "jupyter-lsp" }, + { name = "jupyter-server" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "packaging" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/2a/d6af53bfd45a43a5bfe7e40ba47ee7a8921a807daf4bb708e3a295bbb54d/jupyterlab-4.6.1.tar.gz", hash = "sha256:75315982ed28427edaa62bb85eadb5105e4043a757643c910efd787fe6ed0837", size = 28179125 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/81/90ac6cc31d248e83a0d1eab343a5e6e68bc783d3f74fbe61640f42a61da4/jupyterlab-4.6.1-py3-none-any.whl", hash = "sha256:85a58546c831f3dce6cf919468c26874c9065e99c42279fb4abb8e1b552a98bb", size = 17164660 }, +] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, +] + +[[package]] +name = "jupyterlab-server" +version = "2.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "jinja2" }, + { name = "json5" }, + { name = "jsonschema" }, + { name = "jupyter-server" }, + { name = "packaging" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, +] + +[[package]] +name = "lark" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, +] + +[[package]] +name = "lxml" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/3b/aab6728cae887456f409b4d75e8a01856e4f04bd510de38052a47768b680/lxml-6.1.1.tar.gz", hash = "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40", size = 4197430 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/b0/83f481780d1548750b8ce2ec824073deef2f452d9cd1a6faff8507e3d16d/lxml-6.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:53b7d2b7a10b1c35c0a5e21e9224accf60c1bbfba523990732e521b2b73adef2", size = 8526461 }, + { url = "https://files.pythonhosted.org/packages/b9/d5/30fa0f808002c7329397bfbb24e306789c0b29f04aa5842c07b174b4216f/lxml-6.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3f333630ab480244a1bff72043e511a91eb22e7595dead8653ee5612dd8f3d", size = 4595375 }, + { url = "https://files.pythonhosted.org/packages/4f/d2/edb71cf0e561581a7c5eb2626244320eb04e9f8ce6d563184fd668b45073/lxml-6.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a4bbea04c97f6d78a48e3fbc1cb9116d2780b1b39e03a23f6eb9b603fd61f510", size = 4923654 }, + { url = "https://files.pythonhosted.org/packages/4c/77/1bc7eeb0de4577d783fb625aa092cc9357883bba35845a3666bf1259f3dc/lxml-6.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db1d75f6617a49c1c01bc7023713e0ff59ab32c9579ae62a7674c0e34f3b0b0a", size = 5067921 }, + { url = "https://files.pythonhosted.org/packages/1b/3c/c0690d74bd2bc17bc03b5b0d093569ead597dd0bfa088bf99eef8c24e19c/lxml-6.1.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a12689be69a28ddaa0ab99a5a1137da2afd5f8f16df7b5680b66f616d3eda1d", size = 5002456 }, + { url = "https://files.pythonhosted.org/packages/66/8d/d1b3271af0c0f1e27e8472a849e4d2c65bc7766884b9ad2da9e76e145c88/lxml-6.1.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b73c339ae29b90fd2d06e58ebd555a751bde9cd6bbd36cc0281b9a2c94e9d8", size = 5202776 }, + { url = "https://files.pythonhosted.org/packages/7a/45/689824ffb237fd10125ad273f32b28ff04dc6203c2822c85ff65a93df65e/lxml-6.1.1-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:752d3bbfe874715ccd0aec7f88d7fc623c0f1fd7aa7b3238a084e017bad2a009", size = 5329945 }, + { url = "https://files.pythonhosted.org/packages/5d/c0/ef73af53767e958fd87d437c170f272e2f6e6c0f854939f133a895f1e711/lxml-6.1.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:6b1761fbf9ec984e2e9d9c589ef5f5fd684b7c19f92aadd567a26c5224958db6", size = 4659237 }, + { url = "https://files.pythonhosted.org/packages/a0/5e/e1158e40397585e91cb0472374a1f63d0926a1ddeaa92f13d1a1ffe306d5/lxml-6.1.1-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d680fbcb768404c601ecb43519ecd8461f6954cb11c06a78962f666832ccfca8", size = 5265904 }, + { url = "https://files.pythonhosted.org/packages/a0/16/8687e5d1400ed1c0bc41dace232ebb7553952b618ea1f2e5fb6e2cfbbe23/lxml-6.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:162af1091cd785f2f27e62d3547ae9bc58ec5c86dd314d67021fd02463708d83", size = 5045225 }, + { url = "https://files.pythonhosted.org/packages/ca/18/d877bd1ae2e5ffdfd4836565aba350db31feb2f2656d6ce70316ed66a05e/lxml-6.1.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e9308ff8241c532df3f3e570f9a5aeed6c853f888512ba4b75638d7c11c95ef6", size = 4712721 }, + { url = "https://files.pythonhosted.org/packages/44/4d/1f44fd1d770b10dacbf6b5c6e520f4d6e0708744930f719dc04e67cab981/lxml-6.1.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f6994074ebae6ffb04447268e37dc16edc304f9859cf91acb86e0af6c1b395c", size = 5252549 }, + { url = "https://files.pythonhosted.org/packages/64/5d/1d66b84f850089254c230ef6ea6b267a5a54e2e179a5d960036a05d501d7/lxml-6.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:80c2dfadb855da477cf73373ad29a333535dedb9b12bad02c9814c8e2b43bf08", size = 5226877 }, + { url = "https://files.pythonhosted.org/packages/ad/00/84c4b5302d42a2d0184f38d538c8a197f33b52a50bd4f7bcfe990bce3036/lxml-6.1.1-cp311-cp311-win32.whl", hash = "sha256:30a89d3ac8faec007453fb541f3f46807eeec88edd5826f6e3fe001752a2c621", size = 3594072 }, + { url = "https://files.pythonhosted.org/packages/61/9d/2e2f7d876349f45e0f3e29f72da311668853d59b58d473a2dea4f0160135/lxml-6.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:abbefa31eee84842140f67acef1c828e28bba8bbf0c3bc6e5492a9af88152c28", size = 4025469 }, + { url = "https://files.pythonhosted.org/packages/b0/d5/570e6390e4110331e6208b2ba83d1482cc9146808ee118b22824a34c1070/lxml-6.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:dcb292aa7fe485ceff7af4f92e46c5af397daec5dff64871a528f0fc47a3cc5b", size = 3667640 }, + { url = "https://files.pythonhosted.org/packages/6a/6e/c4add832b6fc1e887125b96f880d7b9b70aae5248718e046b1704bcac4b9/lxml-6.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:104c09bda8d2a562824c0e319d0768ce26a779b7601e0931d33b09b53c392ef7", size = 8570821 }, + { url = "https://files.pythonhosted.org/packages/22/00/ff3009c88e65de8011630acf8ab5a09cb2becd2aaf47fba2f3449f6224e9/lxml-6.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25c6997a9a534e016695a0ba06b2f07945de682731ff01065b6d5a4474179da1", size = 4624252 }, + { url = "https://files.pythonhosted.org/packages/42/95/bb63f0fd62e554fe078e1fb3c8fe9083c14ddc7ad7fa178d10e57e071ac7/lxml-6.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c921ba5c51e4e9f63b8b00267d06566e1f63407408a0496da2d1d0bfc819c7fc", size = 4930746 }, + { url = "https://files.pythonhosted.org/packages/eb/99/0013e8d9b5960f4f041cf0b73e2f80c23eb5205b1f7bfb20203243651359/lxml-6.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:54a7f95e4de5fb94e2f9f4b9055c6ba33bf3d628fd77a1d647c5923caa2cdcdc", size = 5093723 }, + { url = "https://files.pythonhosted.org/packages/29/91/317b332636bfc7bddcff828d41b3307f50043f4b237e40849c333d80fa1a/lxml-6.1.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f2ec43df44b1f76249ee0a615334f9b5b060e1c8bd90e706dad2d14d02f383", size = 5005557 }, + { url = "https://files.pythonhosted.org/packages/42/2f/cc9bf06afe70f9c9093ae60855d9759da9db601ec4080f7473319666ffd7/lxml-6.1.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70ef8a7e102a1508f8121aae5b0867abd663f72c14f0a9c937e6554cb4587b7b", size = 5631036 }, + { url = "https://files.pythonhosted.org/packages/08/f6/af32e23e563971ffb0fb86be52bc5be5c2c118858ffc119bf6a9039b173d/lxml-6.1.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818", size = 5240367 }, + { url = "https://files.pythonhosted.org/packages/78/83/8555d40948b09ce86f1bd0c68a7ac31d07b1929f92cc1b074006c97ef2d2/lxml-6.1.1-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:27acc820660aaffa4f7c087f29120e12980f7779d56d8492d263170111284740", size = 5350171 }, + { url = "https://files.pythonhosted.org/packages/63/75/5d92da93729b7bad783689e6496049fa40927b45bec7bf183c981de3ca70/lxml-6.1.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:1db753c9115ec7100d073b744d17e25e88a8f90f5c39b2f5dd878149af59671f", size = 4694874 }, + { url = "https://files.pythonhosted.org/packages/c5/b5/3aad415a9a25b822e783f15deeb4dffccf5113030f1afa2222dd929313d9/lxml-6.1.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4f469aebd783bb741c2ecb2a681008fd26bfe5c16a9a72ed5467f834e810df2", size = 5244492 }, + { url = "https://files.pythonhosted.org/packages/f1/a1/5fcf7eb9904b80086aa47dcf0027de07b1bb990afad2e6823144c368ae04/lxml-6.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:766b010012d59470072c1816b5b6c69f1d243e5db36ea5968e94accf430a4635", size = 5048232 }, + { url = "https://files.pythonhosted.org/packages/77/74/1f601b63c7a69fcdf10fa9b148c81da8442204194f6c55509cc485c786b9/lxml-6.1.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b8d812c6011c08b8111a15e54dd990b8923692d80adf35488bee34026c35accf", size = 4777023 }, + { url = "https://files.pythonhosted.org/packages/a2/b9/7a78f51aec95b1bf780d78e12705a9f6533284f8693dc5c0e6724fa53d3f/lxml-6.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe0306bd29505a9177aac19f1877174b0e7422c222a59f70b2cd41633448c3dc", size = 5645773 }, + { url = "https://files.pythonhosted.org/packages/a5/6e/98a7b7ad54e4e74fa1f20fff776913980619d0ebe5558232d7da6580bdd8/lxml-6.1.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5ba186ad207446c65d3bb3d3e0412b032b1d9f595e59861e2354798c5703d955", size = 5233088 }, + { url = "https://files.pythonhosted.org/packages/65/d1/bc0ed2427bf609f2ee10da303a6a226f9c8bce94f945dc29a32ce55de6e4/lxml-6.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa366a1e55b8ebfe8ca8ddc3cfe75c8ebade181aeb0f661d0cb05986b647f72a", size = 5260995 }, + { url = "https://files.pythonhosted.org/packages/69/8b/6772e1a4b513fc50a8d931f19edde0e13ae6918510a1e13ff67864f3e5ed/lxml-6.1.1-cp312-cp312-win32.whl", hash = "sha256:126c93f7f56f0eda92f6d8c619edc463a4f23d9252f1c9d0405a76f25fa9f11a", size = 3596382 }, + { url = "https://files.pythonhosted.org/packages/1b/89/45198e9624762af2dfd2cb8782598477ceb29f6e59caab560388ae1f4ec1/lxml-6.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:26e6eda8d38c1fcab1090dd196ee87cbd13788e531937610e2589085de074e77", size = 3997255 }, + { url = "https://files.pythonhosted.org/packages/90/a9/7a54b6834088d9ae528a7b780584ba6a39a9457b0ac330479f20ffbc9449/lxml-6.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:6540377fbd53fe1b629172288c464fb18db11ce1fa7dc15891da10aa9dcc3e7f", size = 3659610 }, + { url = "https://files.pythonhosted.org/packages/a5/eb/7e6f37c5584ccbb2ff267f56fd0339016938c1c8684cfefab9b33ffc2f36/lxml-6.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736", size = 8559780 }, + { url = "https://files.pythonhosted.org/packages/a1/36/587c2521cf23a2cd6c9c22108aa7528f683a1f195ed7ccd23a4b1786ad36/lxml-6.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9", size = 4618006 }, + { url = "https://files.pythonhosted.org/packages/6e/ca/ab7bfe2bf4c972af5e7878262845ead3a24a929a9b04bc11c7c1ece6c82a/lxml-6.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354", size = 4924139 }, + { url = "https://files.pythonhosted.org/packages/6b/55/a0c72851dfee5ecc689f949723a73dea457758912542cb955b108eaf0d8f/lxml-6.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca", size = 5082329 }, + { url = "https://files.pythonhosted.org/packages/f0/b6/0608f7d61a3b96cc67e5648a3d906e31a5082093e10e7be65b3886289938/lxml-6.1.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099", size = 4993564 }, + { url = "https://files.pythonhosted.org/packages/4c/66/ae227524b066d29d55bf0b453d93d2d793c40218657d643dcbbca13b8faf/lxml-6.1.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6", size = 5613467 }, + { url = "https://files.pythonhosted.org/packages/a6/76/dbe4a00b50385e40194231dcfe5a12c059de7cf90e89c83407d2b085b719/lxml-6.1.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085", size = 5228304 }, + { url = "https://files.pythonhosted.org/packages/1c/01/00b1b8442ed2041793336868ba0b9ea4b13d7da7c085c6404c207a63bf79/lxml-6.1.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e", size = 5341607 }, + { url = "https://files.pythonhosted.org/packages/63/36/1ad29931e9a4638bb707869f01d423a6c815f82152138d1a40dfcfde2b95/lxml-6.1.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f", size = 4700168 }, + { url = "https://files.pythonhosted.org/packages/3c/d1/a9536cecf9be18a0dc72d32bead283a2332d1ffebd2dd3ac70ce444686e5/lxml-6.1.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c", size = 5232487 }, + { url = "https://files.pythonhosted.org/packages/0e/77/b4fb1e03bf5d130e879214d3100092e386418807fb74dd0adc4b0a48f351/lxml-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b", size = 5044231 }, + { url = "https://files.pythonhosted.org/packages/26/4c/d00daeeb0a5530c4028a9232aa1b93db3ef4ed2158c116ea73c79a9765b3/lxml-6.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2", size = 4769450 }, + { url = "https://files.pythonhosted.org/packages/ed/6a/715a3a8d156ce42f29cf014706f5410c2ff3b02267774110fc23266409fe/lxml-6.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5", size = 5635874 }, + { url = "https://files.pythonhosted.org/packages/45/37/0544bc21dde2a88f3a17b504e6fc79c0e01d25a33c2f6079724e9e72b9c7/lxml-6.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785", size = 5223987 }, + { url = "https://files.pythonhosted.org/packages/4d/f8/f6a5e8185bcb28c2befae3d31f8e3df3b811cb0f47746517a81279fcafe1/lxml-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947", size = 5250276 }, + { url = "https://files.pythonhosted.org/packages/c7/f2/1a2b9f1b7a49d45495369be7ef9ad05b262930f2eab3e3145706fca8083f/lxml-6.1.1-cp313-cp313-win32.whl", hash = "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca", size = 3596903 }, + { url = "https://files.pythonhosted.org/packages/e6/99/f4ffb024f238eec2131aaa09f3278fb6129cf892741bf68e1fc1afb8c100/lxml-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660", size = 3995869 }, + { url = "https://files.pythonhosted.org/packages/d1/53/70eb8c5c6037f27448f1e3c54ebede9545a801ae63f0a7254afca4fe8e45/lxml-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc", size = 3658490 }, + { url = "https://files.pythonhosted.org/packages/13/e2/2e325795566de01d0d7c3bb57d3c370616b2d07b01214e84eec5d3b10963/lxml-6.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0", size = 8577146 }, + { url = "https://files.pythonhosted.org/packages/93/cf/5630b5e4be7d2e6bee8efe83865c925221103cf0221303b104ce134b01e2/lxml-6.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840", size = 4623866 }, + { url = "https://files.pythonhosted.org/packages/d2/51/3904907c063451cf8d4a5c9fe0cad95fa1f4ec57f4e3884fa0731bd7a305/lxml-6.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14", size = 4950022 }, + { url = "https://files.pythonhosted.org/packages/94/cd/9c7611a51c37a2830928405817cc5d56a97f64fab83cc3f628748b135749/lxml-6.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909", size = 5086695 }, + { url = "https://files.pythonhosted.org/packages/da/d6/24e3b5906abb0b674ff2ae195bc3ce59708df2bcd17cf17703b2d7dd643a/lxml-6.1.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00", size = 5031642 }, + { url = "https://files.pythonhosted.org/packages/2d/db/6ec54f99019838bff54785c51da07f189eb4676861c5f2730962b0d8d665/lxml-6.1.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955", size = 5647338 }, + { url = "https://files.pythonhosted.org/packages/42/3d/ef4dcfffd22d27a61805d8ed9f7fb888495bc6aa88648fa07c1eaa5586b6/lxml-6.1.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13", size = 5239528 }, + { url = "https://files.pythonhosted.org/packages/62/bb/37fb3f0dff146bdcfa78eec47879273820b2a0bf350ec236ce14bd0b1c26/lxml-6.1.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7", size = 5350730 }, + { url = "https://files.pythonhosted.org/packages/90/42/43253f168388df4fae1f38c01df36ddb9bee39e2048167b54cdcbae85ea3/lxml-6.1.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245", size = 4697530 }, + { url = "https://files.pythonhosted.org/packages/eb/a8/c5a8504f81bbdfc8e7094c2c850cdb4ed6777fc4d5ddd9e5ab819f3b0d54/lxml-6.1.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5", size = 5250670 }, + { url = "https://files.pythonhosted.org/packages/77/b7/c7e76ab18744d75e21f320ebf9ff9d1ceae2b54dd431ea5a64caf26c9672/lxml-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462", size = 5084485 }, + { url = "https://files.pythonhosted.org/packages/31/31/b35c53f8ef7b7c31cacd23d3638652fff7bcd1deb6eedb709ab43b685908/lxml-6.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465", size = 4737635 }, + { url = "https://files.pythonhosted.org/packages/d9/06/31f23c813a7fe8e0cb1b175e915b08c9bf4e86d225b210feadbdbe519667/lxml-6.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a", size = 5670681 }, + { url = "https://files.pythonhosted.org/packages/1a/bc/ce619bccc89b1fd9ad8a8e1330ee3f3beff9f2ff95b712d7bbcdd6e22fc3/lxml-6.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590", size = 5238229 }, + { url = "https://files.pythonhosted.org/packages/2f/5d/b329acbbedc0b619ebc2be6cf7ee9ed07e80892c88d4dfd612c33805789a/lxml-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb", size = 5264191 }, + { url = "https://files.pythonhosted.org/packages/d6/85/be36fb1425b30db3c3f9df75fe86343ebffb79e6320bd7f588e25bfeac39/lxml-6.1.1-cp314-cp314-win32.whl", hash = "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603", size = 3657202 }, + { url = "https://files.pythonhosted.org/packages/b8/ce/3cf9a827342269f54d405a6202397de63f07c69cbd6ce7d183a3f0cba1e9/lxml-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137", size = 4064497 }, + { url = "https://files.pythonhosted.org/packages/d9/3e/1a957bde8f0760039e627f94699f82caa782c9d838d86c3d28245ee67212/lxml-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf", size = 3741991 }, + { url = "https://files.pythonhosted.org/packages/78/b2/00ed55b3a2efa4658fb795c38d1090ec9b3e8a6c3683d4441fa517f09c3b/lxml-6.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee", size = 8827545 }, + { url = "https://files.pythonhosted.org/packages/c0/73/74573db19baa618d5f266f2407898b087ff6927115b00b71e5fc1b700847/lxml-6.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c", size = 4735736 }, + { url = "https://files.pythonhosted.org/packages/16/02/6f7061f4f95f51e545d48e87647c54791d204a4e881be4156e7a26ba5338/lxml-6.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef", size = 4970291 }, + { url = "https://files.pythonhosted.org/packages/b0/02/55fc057d8283427dea7d6edb102e7a840239c77a64a983d92f62a304c0e9/lxml-6.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a", size = 5102822 }, + { url = "https://files.pythonhosted.org/packages/e4/48/8e1cf78d89d66850121d9255a2a24414c98f775da93b90cf976956c24b14/lxml-6.1.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c", size = 5027923 }, + { url = "https://files.pythonhosted.org/packages/ed/00/0632a0647612c8af24d26997b3b961397daa9d5b2581444805933629a4cb/lxml-6.1.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525", size = 5595843 }, + { url = "https://files.pythonhosted.org/packages/bc/86/ab008a7dc360711b66858d61c80a5979a70a09f2aa2b05d9698df80b803d/lxml-6.1.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca", size = 5224515 }, + { url = "https://files.pythonhosted.org/packages/75/c6/2702ff375e728e34f56d9a45339a9cf7e4427e917f542225242d63a05afa/lxml-6.1.1-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e", size = 5312511 }, + { url = "https://files.pythonhosted.org/packages/b7/57/a5807c98f87a86f10ef9ffab35516df7c0f0c4b6d5d33e9f608ab9c04a31/lxml-6.1.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038", size = 4639206 }, + { url = "https://files.pythonhosted.org/packages/1f/e1/8a0a2c35734812395f4da4eaf33748a7e5705bfb2a58b128da764339d5ec/lxml-6.1.1-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e", size = 5232404 }, + { url = "https://files.pythonhosted.org/packages/c2/e2/0e6a4dd5ad84d01d99aa7bae7cfefd4a760a0e0f8176818241de17d9b6c0/lxml-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072", size = 5083769 }, + { url = "https://files.pythonhosted.org/packages/a0/7e/161f33d463f6ffc1c7679104b65086dea120080d49dde4d238f015aaee2f/lxml-6.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52", size = 4758936 }, + { url = "https://files.pythonhosted.org/packages/f1/fb/2369825e3f6ca99305bf9f7b7085fda91c8b0922a89e54d900974aa3ef85/lxml-6.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b", size = 5620296 }, + { url = "https://files.pythonhosted.org/packages/30/90/d61e383146f74c5ab683947ea14dc7b82778838ab9b95ea73a23b60d0191/lxml-6.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2", size = 5228598 }, + { url = "https://files.pythonhosted.org/packages/76/2d/2dafd8149e94b05bb070690efd5bb2680720681e03ff03fc57d2b70a1105/lxml-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e", size = 5247845 }, + { url = "https://files.pythonhosted.org/packages/ce/68/b30e913340c380ddac9580c6e6230991fc37240ec4f64704833e4f3e2769/lxml-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1", size = 3897345 }, + { url = "https://files.pythonhosted.org/packages/3c/4e/9eb2af5335545f9fbcd7af57bcf87c6025d31eaa31b14ec184a6c8675328/lxml-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e", size = 4393350 }, + { url = "https://files.pythonhosted.org/packages/7f/2c/0f1e93c636720e8a3eb59af2bfda99d98b55891e1c53bc30c2e0e865f01b/lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c", size = 3817223 }, + { url = "https://files.pythonhosted.org/packages/b5/32/86a3f0f724a3a402d4627937a7fc27b160e45e7012b4adf47f6e1e844511/lxml-6.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:31033dc34636ea6b7d5cc11b1ddbda78a14de858ba9d3e1ed4b69a3085bc521e", size = 3930127 }, + { url = "https://files.pythonhosted.org/packages/40/44/d832e82af08723761556d004b1d04d281c09f9a8cecd7d3148548c9941a3/lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3893c14c4b6ac5b2d54ba8cf03e99fe5104e592de491f19bd6b82756c09f8004", size = 4210769 }, + { url = "https://files.pythonhosted.org/packages/6d/39/0dc5949f759ed7d951e0bb8c2f2d9d7aca1908d22352fa84a8afd2ea54af/lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c07da4cebf6889f03ebac8d238f62318e29f495de0aa18a51ea14e61ae907e2e", size = 4318163 }, + { url = "https://files.pythonhosted.org/packages/e6/fb/8ab3845fe046ba4cbf74536bcf6801a774b7caf4350de1c5d37f1f0a9e90/lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6f0ce10945fab9c4c06ce14e22af9059d1a87493a9af4501a5b0b9187e21cf2", size = 4250945 }, + { url = "https://files.pythonhosted.org/packages/68/1b/7553ab136894374ffae8851ec06f98f511cd8e66246e41b6be059d0a7289/lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f8844cd288697c6425c9beba919302241e3278871dc6519515e72b04e987abcf", size = 4401664 }, + { url = "https://files.pythonhosted.org/packages/db/a4/441aee36c6f6b249823d20fd91f9be9ab89d7c5a8ae542a4a4ca6d342d56/lxml-6.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ed21202aec73cda4d55d1ce57b389aadb90ffb044e6cd1080b8347efe1b1ec84", size = 3508989 }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631 }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058 }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287 }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940 }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887 }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692 }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471 }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923 }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572 }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077 }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "mistune" +version = "3.3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/a5/2dab368d6950e6808904dec98f54c7e726ee7be4a0c6afe00e6e011bd52d/mistune-3.3.3.tar.gz", hash = "sha256:c4c6c0c840b8637a2e9b8b6d607eb7c8f00888bf14c754409bcd339e848c2477", size = 115363 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/70/b1e4737b84163db5bb1dfde6f216dbfbf32783330a9989c965e121172830/mistune-3.3.3-py3-none-any.whl", hash = "sha256:99de1585e42dcbd826faa9e11a202727a5e202e4e4722a4c69ac1ff615793dd7", size = 63569 }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "multidict" +version = "6.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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626 }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706 }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356 }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355 }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433 }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376 }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365 }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747 }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293 }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962 }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360 }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940 }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502 }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065 }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870 }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302 }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981 }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887 }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053 }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695 }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884 }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770 }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109 }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930 }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074 }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008 }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542 }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719 }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319 }, +] + +[[package]] +name = "multiprocess" +version = "0.70.19" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/aa/714635c727dbfc251139226fa4eaf1b07f00dc12d9cd2eb25f931adaf873/multiprocess-0.70.19-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1bbf1b69af1cf64cd05f65337d9215b88079ec819cd0ea7bac4dab84e162efe7", size = 144743 }, + { url = "https://files.pythonhosted.org/packages/0f/e1/155f6abf5e6b5d9cef29b6d0167c180846157a4aca9b9bee1a217f67c959/multiprocess-0.70.19-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5be9ec7f0c1c49a4f4a6fd20d5dda4aeabc2d39a50f4ad53720f1cd02b3a7c2e", size = 144738 }, + { url = "https://files.pythonhosted.org/packages/af/cb/f421c2869d75750a4f32301cc20c4b63fab6376e9a75c8e5e655bdeb3d9b/multiprocess-0.70.19-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1c3dce098845a0db43b32a0b76a228ca059a668071cfeaa0f40c36c0b1585d45", size = 144741 }, + { url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948 }, + { url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457 }, + { url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281 }, + { url = "https://files.pythonhosted.org/packages/7f/74/d2c27e03cb84251dfe7249b8e82923643c6d48fa4883b9476b025e7dc7eb/multiprocess-0.70.19-py313-none-any.whl", hash = "sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952", size = 156414 }, + { url = "https://files.pythonhosted.org/packages/a0/61/af9115673a5870fd885247e2f1b68c4f1197737da315b520a91c757a861a/multiprocess-0.70.19-py314-none-any.whl", hash = "sha256:e8cc7fbdff15c0613f0a1f1f8744bef961b0a164c0ca29bdff53e9d2d93c5e5f", size = 160318 }, + { url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477 }, +] + +[[package]] +name = "narwhals" +version = "2.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/3c/c4ef2164a71c1a63d7f1ae411c4082c5fa872405106db60a4b7114989ad7/narwhals-2.22.1.tar.gz", hash = "sha256:d62920805a0a43b7ff8b54b0c0d3142d796f8a9301836ada37e573d6a33cbcd9", size = 647493 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/ca/36339329c4604adbcc99c899b7eb1ce1a555c499b6a6860757dc9bfed36d/narwhals-2.22.1-py3-none-any.whl", hash = "sha256:60567d774edf77db53906f89d9fbd164e66e56d66d388e1e6990f17ac33cfb53", size = 454815 }, +] + +[[package]] +name = "nbclient" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "nbformat" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/a5/b3bae4b590c0cbcada2c63a34f7580024e834a8ba213e949a2f906705787/nbclient-0.11.0.tar.gz", hash = "sha256:04a134a5b087f2c5887f228aca155db50169b8cd9334dee6942c8e927e56081a", size = 62535 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/c9/94d73e5a01c5b926c3fa2496e97d7a8dc28ed5a77c0b2ed712f1a62e6694/nbclient-0.11.0-py3-none-any.whl", hash = "sha256:ef7fa0d59d6e1d41103933d8a445a18d5de860ca6b613b87b8574accdb3c2895", size = 25288 }, +] + +[[package]] +name = "nbconvert" +version = "7.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "bleach", extra = ["css"] }, + { name = "defusedxml" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyterlab-pygments" }, + { name = "markupsafe" }, + { name = "mistune" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "pandocfilters" }, + { name = "pygments" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/b1/708e53fe2e429c103c6e6e159106bcf0357ac41aa4c28772bd8402339051/nbconvert-7.17.1.tar.gz", hash = "sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2", size = 865311 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/f8/bb0a9d5f46819c821dc1f004aa2cc29b1d91453297dbf5ff20470f00f193/nbconvert-7.17.1-py3-none-any.whl", hash = "sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8", size = 261927 }, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, +] + +[[package]] +name = "nest-asyncio2" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/73/731debf26e27e0a0323d7bda270dc2f634b398e38f040a09da1f4351d0aa/nest_asyncio2-1.7.2.tar.gz", hash = "sha256:1921d70b92cc4612c374928d081552efb59b83d91b2b789d935c665fa01729a8", size = 14743 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/3c/3179b85b0e1c3659f0369940200cd6d0fa900e6cefcc7ea0bc6dd0e29ffb/nest_asyncio2-1.7.2-py3-none-any.whl", hash = "sha256:f5dfa702f3f81f6a03857e9a19e2ba578c0946a4ad417b4c50a24d7ba641fe01", size = 7843 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/91/04e965f8e717ba0ab4bdca5c112deeab11c9e750d94c4d4602f050295d39/nltk-3.9.4-py3-none-any.whl", hash = "sha256:f2fa301c3a12718ce4a0e9305c5675299da5ad9e26068218b69d692fda84828f", size = 1552087 }, +] + +[[package]] +name = "notebook" +version = "7.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-builder" }, + { name = "jupyter-server" }, + { name = "jupyterlab" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/44/d5c65783f490298473bb1c05722e05ee2256231389559c2c5ae0a3e5d975/notebook-7.6.0.tar.gz", hash = "sha256:ea13e79e601bf273074895fdfb17dd3f2da916d3c045e0b9c47d18b16ab62481", size = 5497344 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/d1/e617c40db57ff40e75f43a7d4d1c305e3a54c053ab5cb0534a6c314664f9/notebook-7.6.0-py3-none-any.whl", hash = "sha256:98aa2811b54ac191321d5dfce12ca700f8a511a33a26e4de2fa106a357c43d6a", size = 5544575 }, +] + +[[package]] +name = "notebook-shim" +version = "0.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194 }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111 }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159 }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936 }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692 }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164 }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877 }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487 }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945 }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406 }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528 }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119 }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246 }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410 }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240 }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012 }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538 }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706 }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541 }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825 }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687 }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482 }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648 }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902 }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944 }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392 }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220 }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800 }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600 }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134 }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598 }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272 }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197 }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287 }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763 }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070 }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752 }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024 }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398 }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971 }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532 }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881 }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458 }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559 }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716 }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947 }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197 }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245 }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587 }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226 }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196 }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334 }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678 }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672 }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731 }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805 }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496 }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616 }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145 }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813 }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982 }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908 }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867 }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511 }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064 }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157 }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728 }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374 }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286 }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263 }, +] + +[[package]] +name = "nvidia-cublas" +version = "13.1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918 }, + { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758 }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827 }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200 }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449 }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060 }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632 }, +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.20.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296 }, + { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588 }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554 }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489 }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672 }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992 }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106 }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258 }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-cusparse", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760 }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980 }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568 }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937 }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344 }, + { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586 }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.29.7" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712 }, + { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000 }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933 }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748 }, +] + +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947 }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546 }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047 }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878 }, +] + +[[package]] +name = "overrides" +version = "7.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195 }, +] + +[[package]] +name = "pandas" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495 }, + { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250 }, + { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558 }, + { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611 }, + { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670 }, + { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708 }, + { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609 }, + { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596 }, + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846 }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550 }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965 }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600 }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824 }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889 }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463 }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158 }, + { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071 }, + { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690 }, + { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634 }, + { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243 }, + { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659 }, + { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880 }, + { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091 }, + { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282 }, + { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016 }, + { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210 }, + { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126 }, + { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051 }, + { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796 }, + { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741 }, + { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958 }, + { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065 }, + { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101 }, + { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553 }, + { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065 }, + { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188 }, + { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966 }, + { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755 }, + { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658 }, + { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242 }, + { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369 }, + { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306 }, + { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394 }, + { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717 }, + { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897 }, + { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855 }, + { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464 }, +] + +[[package]] +name = "pandocfilters" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, +] + +[[package]] +name = "parso" +version = "0.8.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743 }, +] + +[[package]] +name = "portalocker" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/77/65b857a69ed876e1951e88aaba60f5ce6120c33703f7cb61a3c894b8c1b6/portalocker-3.2.0.tar.gz", hash = "sha256:1f3002956a54a8c3730586c5c77bf18fae4149e07eaf1c29fc3faf4d5a3f89ac", size = 95644 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/a6/38c8e2f318bf67d338f4d629e93b0b4b9af331f455f0390ea8ce4a099b26/portalocker-3.2.0-py3-none-any.whl", hash = "sha256:3cdc5f565312224bc570c49337bd21428bba0ef363bbcf58b9ef4a9f11779968", size = 22424 }, +] + +[[package]] +name = "prometheus-client" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744 }, + { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033 }, + { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754 }, + { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573 }, + { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645 }, + { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563 }, + { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888 }, + { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253 }, + { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558 }, + { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007 }, + { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355 }, + { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057 }, + { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938 }, + { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731 }, + { url = "https://files.pythonhosted.org/packages/b8/9b/2da6dee38871c3c8772fabc2758325a5c9077d6d18c597737dc04dd884cd/propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0", size = 38966 }, + { url = "https://files.pythonhosted.org/packages/42/4e/f17363fb58c0afe05b067361cb6d86ed2d29de6506779a27547c4d183075/propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82", size = 42135 }, + { url = "https://files.pythonhosted.org/packages/c6/eb/6af6685077d22e8b33358d3c548e3282706a0b3cd85044ffba4e5dd08e3b/propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab", size = 38381 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018 }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322 }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621 }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649 }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546 }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330 }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436 }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373 }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813 }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764 }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140 }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036 }, +] + +[[package]] +name = "protobuf" +version = "7.35.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/01/9ef0afd7999eb9badb3a768b4aedd78c86d4c65cfaf1958ab276199e76b4/protobuf-7.35.1.tar.gz", hash = "sha256:ce115a26fe0c39a2c29973d914d327e516a6455464489fe3cd1e51a1b354f81a", size = 458717 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:24f857477359a85c0c235261b8ba905fd51b2562f4a64ca1df5473f29850cbf6", size = 433226 }, + { url = "https://files.pythonhosted.org/packages/37/4b/dfb89eb0e652a1ff073c39a59fb5e3a83cfe9b57a2c83fa6d78270101767/protobuf-7.35.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:11d6b0ec246892d85215b0a13ca6e0233cf5284b68f0ac02646427f4ff88a799", size = 328847 }, + { url = "https://files.pythonhosted.org/packages/0f/58/dc12f2cd484951524af6e3382c785869b9b3fb5e52ee95ae23add53ee8f9/protobuf-7.35.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:b73f9489a4b8b1c9cb1f8ed951c736392592edb24b9d6819f36d2e10b171d5b4", size = 344030 }, + { url = "https://files.pythonhosted.org/packages/e4/be/5b3cfe508bfab6761414ff944e3366eb13be4fd71efcd69450f89ba39f43/protobuf-7.35.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:74758715c53d7158fb76caf4f0cfdacc5329a4b1bb994f865d6cf302d413a1c4", size = 327130 }, + { url = "https://files.pythonhosted.org/packages/d8/bc/6d6c7ba8709c85f8f2c390b2b118d6fb08a783676a572271851bf45a7d22/protobuf-7.35.1-cp310-abi3-win32.whl", hash = "sha256:353652e4efd0bca5b5fc2656abf8307ef351f0cf938c9eba09f0e09c20a25c30", size = 428945 }, + { url = "https://files.pythonhosted.org/packages/0a/19/8d0cb6f20a1ef7b18f1c8986ad5783f22f84cce39c6ce9a6e645ea55192e/protobuf-7.35.1-cp310-abi3-win_amd64.whl", hash = "sha256:230a75ddfc2de4806e56696ce9640c1cdfdb6543b7cfce98d42a4c0a0e7bdb87", size = 439996 }, + { url = "https://files.pythonhosted.org/packages/19/c7/5f7c636ec43e0c545e28d1f1db71990108306f7bdcb89f069ba97e428e7f/protobuf-7.35.1-py3-none-any.whl", hash = "sha256:4bc97768d8fe4ad6743c8a19403e314511ed9f6d13205b687e52421c023ac1b9", size = 171659 }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595 }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082 }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476 }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062 }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893 }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589 }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664 }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087 }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383 }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210 }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228 }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284 }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090 }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859 }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560 }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997 }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972 }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266 }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737 }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "pyarrow" +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898 }, + { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915 }, + { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931 }, + { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449 }, + { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949 }, + { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986 }, + { url = "https://files.pythonhosted.org/packages/f8/d2/4d1bbba65320b21a49678d6fbdc6ff7c649251359fdcfc03568c4136231d/pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981", size = 27255371 }, + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559 }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654 }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394 }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122 }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032 }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490 }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660 }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759 }, + { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471 }, + { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981 }, + { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733 }, + { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335 }, + { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748 }, + { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554 }, + { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301 }, + { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929 }, + { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365 }, + { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819 }, + { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252 }, + { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127 }, + { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997 }, + { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720 }, + { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852 }, + { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852 }, + { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207 }, + { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117 }, + { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155 }, + { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387 }, + { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102 }, + { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118 }, + { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765 }, + { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890 }, + { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250 }, + { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-json-logger" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", size = 17573 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", size = 15021 }, +] + +[[package]] +name = "pywin32" +version = "312" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/f5/10a6e845a00fc5e7afd0a988b744f403d4d57162a28d160a093c4d9322f0/pywin32-312-cp311-cp311-win32.whl", hash = "sha256:17948aeadbdb091f0ced6ef0841620794e68327b94ee415571c1203594b7215c", size = 6362659 }, + { url = "https://files.pythonhosted.org/packages/35/c4/dcd2d62b5944b6d5db53413a5899016ccd57ffcb7278f3f81655d25d2027/pywin32-312-cp311-cp311-win_amd64.whl", hash = "sha256:d11417d84412f859b722fad0841b3614459ed0047f7542d8362e77884f6b6e8a", size = 6928825 }, + { url = "https://files.pythonhosted.org/packages/b7/56/3cbb433fe4501cdba2eb9040f56a4e1a8243faa4186b25295564d1a7a79d/pywin32-312-cp311-cp311-win_arm64.whl", hash = "sha256:b2200a054ca6d6625c4842fc56a4976a4b47f96b73dbe5538c3f813a80359f47", size = 6721875 }, + { url = "https://files.pythonhosted.org/packages/83/ff/32aa7d2ed0ab12b323aaa64f9b75e6ad4f8fd09f9ccfc28c79414d46838d/pywin32-312-cp312-cp312-win32.whl", hash = "sha256:dab4f65ac9c4e48400a2a0530c46c3c579cd5905ecd11b80692373915269208b", size = 6371877 }, + { url = "https://files.pythonhosted.org/packages/03/d9/77040d3b43df3f3be32ea289433d660d2727f5ba327bc73be835127d9d60/pywin32-312-cp312-cp312-win_amd64.whl", hash = "sha256:b457f6d628a47e8a7346ce22acb7e1a46a4a78b52e1d17e1af56871bd19a93bc", size = 6914841 }, + { url = "https://files.pythonhosted.org/packages/e3/cc/7b1ec671775756020a0ee7f4feeaf3c568f0ab86bd3900088cf986937a92/pywin32-312-cp312-cp312-win_arm64.whl", hash = "sha256:6017c58e12f6809fbb0555b75df144c2922a9ffd18e4b9b5afa863b6c1a9d950", size = 6727901 }, + { url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184 }, + { url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298 }, + { url = "https://files.pythonhosted.org/packages/84/37/c1697194092b76de9ed47ca124323f02c57ffc8a45c06f88a3d5acaf01eb/pywin32-312-cp313-cp313-win_arm64.whl", hash = "sha256:59aba5d5940842075343a5ddc6b11f1cdf0d1567fe745290359dfbcc7c2eb831", size = 6727640 }, + { url = "https://files.pythonhosted.org/packages/fc/2b/1f3cded5822fd49c02f40544cbb5f58c7cfd6b1694869fd476cb6170ee97/pywin32-312-cp314-cp314-win32.whl", hash = "sha256:a77a90fbb6881238d2ca9c6fd797b25817f3768fe78d214a90137ff055a75f5b", size = 6468928 }, + { url = "https://files.pythonhosted.org/packages/21/82/3bf86d2e2808902013132e1ce905a7da0da53790f3836c64bf44d55e24f3/pywin32-312-cp314-cp314-win_amd64.whl", hash = "sha256:a4dd3a848290ef724347b19f301045831d8e802fa4464f491b98b1e0a081432e", size = 7024157 }, + { url = "https://files.pythonhosted.org/packages/a4/0e/73f6d6800b4f27655abd9e9f6aaeaefcddb2b946e4674efa2bab184a7f7b/pywin32-312-cp314-cp314-win_arm64.whl", hash = "sha256:9fce94568364e0155e6dfb781ac5d95903be8baf28670632beab1b523f300daa", size = 6839598 }, + { url = "https://files.pythonhosted.org/packages/eb/61/caa39686032d2ebdd04ff0ab5cbe163126c0066d98e00c9018646e42393b/pywin32-312-cp315-cp315-win32.whl", hash = "sha256:5c1fbe4a937a73ae9297384a3da38518cbc694c68ad8a809b2e19acd350f03ed", size = 6471159 }, + { url = "https://files.pythonhosted.org/packages/0f/cd/7e1de64a4a6f69c04214169657ccab0d93a670ea50e35eb8f489d7378249/pywin32-312-cp315-cp315-win_amd64.whl", hash = "sha256:c2f03a0f73f804a13c2735b99392b0cd426bb4f2c4d0178e5ac966a0f21618d5", size = 7025293 }, + { url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337 }, +] + +[[package]] +name = "pywinpty" +version = "3.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/ef/2d27f30c59a67be7025b2d7858c8c2d282b74d66544b2384730b82de74fd/pywinpty-3.0.5.tar.gz", hash = "sha256:61db0db063de9865adbea66db294628f8577f608d9764a4c7d3384eeacc4e81b", size = 16223484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/5c/31feb3dd82d1b33ae0bd09ca601edb993d9da1b7f0226b3336d4b4c39e1e/pywinpty-3.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:af7a8720c78776ddd6259b71dd567944f766a6cd67f8d2887fbc4973967bacda", size = 2092466 }, + { url = "https://files.pythonhosted.org/packages/ee/fe/fe23e2229ffec0c10190cef5964f5c9b2dba179d23b69ae537b7ea90bcab/pywinpty-3.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:c2406f54f699eab75953fb75ce805f2ae55a33a957cd070890abd454fb4b7680", size = 818395 }, + { url = "https://files.pythonhosted.org/packages/45/34/942cc95ca4e26489875aa8a95192766247a687379ec29543eebe73ec945f/pywinpty-3.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:d62946adf14b15b54c0b8d785f93fe18b04da23f4ad59e2e8c4612646e9abd23", size = 2090915 }, + { url = "https://files.pythonhosted.org/packages/6a/70/5b9053004844139ea8bd86209c57ade12b134b2782f383a095784c8531ec/pywinpty-3.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:e9391c05fbfa7a992a97e831fc6849887b4014a614192e3d984a7ca59592b376", size = 815934 }, + { url = "https://files.pythonhosted.org/packages/b9/f4/2a464b9893cceb3b3f416356e94fdc3e1bca9476993927e4e6d99fe95382/pywinpty-3.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:48db1b0ad9d0a1b81dcaaa7163a99a7808deaceb0c1b2344716dc1fc090c3c4c", size = 2090471 }, + { url = "https://files.pythonhosted.org/packages/3c/2c/a138491a0afbdb50eb79395577bd326d4b0fbde7209417d1a8087ff2493a/pywinpty-3.0.5-cp313-cp313-win_arm64.whl", hash = "sha256:2c6008fb2d3774b48693b2fcb7f2cc317ade9dc581289a964ffeeaf81307c9b5", size = 815518 }, + { url = "https://files.pythonhosted.org/packages/6f/15/54400049a380582acd1282665c70fcf11e0bd3713679aca78e24c3aae738/pywinpty-3.0.5-cp313-cp313t-win_amd64.whl", hash = "sha256:22ce1b780d89821cc52daf6eac0708af22d93d000ce9c7c07e37489db8594598", size = 2089920 }, + { url = "https://files.pythonhosted.org/packages/94/0c/6f24f3c0799f502259b24bdf841a99ad2b0d59df5c2525b4e2a286d14be2/pywinpty-3.0.5-cp313-cp313t-win_arm64.whl", hash = "sha256:9c2919a81bc5cfb09b86fc5a002112b2de95ca4304a07413cbeeb746a1307a5c", size = 814520 }, + { url = "https://files.pythonhosted.org/packages/e9/23/f3cd1b1e5fc56517f54452c49f92049e7dd9ffc8a63de22a495581f50d04/pywinpty-3.0.5-cp314-cp314-win_amd64.whl", hash = "sha256:03bb3c16d691d9242267201830bcd0e64a9b663170e9042bc84b210da9de15ac", size = 2090663 }, + { url = "https://files.pythonhosted.org/packages/9d/dd/96d6cbfc6d9ddab5c1c2f92c26545ae8997446a2ba7ee2024cd43c81f49b/pywinpty-3.0.5-cp314-cp314-win_arm64.whl", hash = "sha256:89c5c6ef08997a3b4b277b214a35fe15cab4dd6d119f0140aa71df5b1168fdbc", size = 815700 }, + { url = "https://files.pythonhosted.org/packages/30/36/d98087bce0acaa4cce7f196103cfa7be3f63ce65f52473bb3e38784ae5d9/pywinpty-3.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:7b566165e0c5fdd6abe167a5ac8b954be6a843eb55a85946576d6bc1dea03d6d", size = 2090093 }, + { url = "https://files.pythonhosted.org/packages/57/fd/fe2b0db922ba052ce3976a08f3fc05d0c05047c8b4ebb6102e832b8ef563/pywinpty-3.0.5-cp314-cp314t-win_arm64.whl", hash = "sha256:24366280a8aa677323da87bec729cb3ea3b35367386cece0978bdc6e4695c690", size = 814517 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826 }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577 }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556 }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114 }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638 }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463 }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986 }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543 }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763 }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +] + +[[package]] +name = "pyzmq" +version = "27.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328 }, + { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803 }, + { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836 }, + { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038 }, + { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531 }, + { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786 }, + { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220 }, + { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155 }, + { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428 }, + { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497 }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, + { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436 }, + { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301 }, + { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197 }, + { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275 }, + { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469 }, + { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961 }, + { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282 }, + { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468 }, + { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394 }, + { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964 }, + { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029 }, + { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541 }, + { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197 }, + { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175 }, + { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427 }, + { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929 }, + { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193 }, + { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388 }, + { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316 }, + { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472 }, + { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401 }, + { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170 }, + { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265 }, + { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208 }, + { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747 }, + { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371 }, + { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862 }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/dc/c1f2df4027e82fc54b5a473e4b250f5139faca49a0fbe29a48668d228f34/regex-2026.5.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ccf5249114cc3e772ecdd88a98a86eca0fd74c61ce32a94743758c083fc05d48", size = 489445 }, + { url = "https://files.pythonhosted.org/packages/03/d2/59f01110660081cce9c0bc30ebd0b5ee250dacf658e3248ed92f01e0e8ee/regex-2026.5.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46f1326ca6e65b0879d23ca302c0f2415aad42ff0309b9c818e7949fe19a41d8", size = 291271 }, + { url = "https://files.pythonhosted.org/packages/58/b6/14b2c84ff90ddb370c81d27503f4a0fcf071496416f4855f6cc8c5d81c35/regex-2026.5.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef31cbfe458e21c6122ba8150ff060e0c7789ed0d26eb423f25472584920b555", size = 289212 }, + { url = "https://files.pythonhosted.org/packages/03/d0/4db86529117320de0c84afd90e70bb47434625875e34fcef9d8c127c5b16/regex-2026.5.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:992604d02e6d9c6d786c24a706a71ecffe1020fc1ef264044474cd81fa2c3919", size = 792310 }, + { url = "https://files.pythonhosted.org/packages/07/78/fe4800cd322f862ecffd2d553409b20d80650e5ed71b9d178f853d020b82/regex-2026.5.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9411dd64ca95477225734a93dfc8583b51916b8d5942f99d6cac21e09965451", size = 861721 }, + { url = "https://files.pythonhosted.org/packages/b5/d0/b3618a895dd8feb897c61bb2954edd265e1767d82a01d53065d5871127a3/regex-2026.5.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4a3ff360dfb836fecdb93a4598f9d6e2ac81e3e397125145c6221bf58cf4c", size = 906460 }, + { url = "https://files.pythonhosted.org/packages/33/6f/1481597e859ef19508b345eec4afd1416ed6e6b459c75a64026ef193aecf/regex-2026.5.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a661a7d270a61f7cf460caee8b9fa2d5ef9e5c681234bcb9e0fe14f488e7dfc", size = 799843 }, + { url = "https://files.pythonhosted.org/packages/73/59/955734c803f59108deccba3597ae440c76b62a652733c0006e6243758420/regex-2026.5.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f079e50a0d3cc3cd5091fa9ff45869a2e6b2cd35895731edafb0327901a8d86d", size = 773610 }, + { url = "https://files.pythonhosted.org/packages/68/8f/70c04a236d651c81881dac42ef8538bddda6121434509d0a22d9e601503b/regex-2026.5.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4ebe8f0b5ec5a5024dc4a4c59f444c4e9afc5f2abdbb8962065b75d27fb971f9", size = 781645 }, + { url = "https://files.pythonhosted.org/packages/1d/96/05c7434d88185e5d27fe54aeb74df86bd77cd79f52f0b4eae54faa8fea70/regex-2026.5.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:97cf3bc1b7d7d2306772ec07366c80d9df00ff79e79cea32898883a646d2fae2", size = 854473 }, + { url = "https://files.pythonhosted.org/packages/4e/c1/6e3d8202d981f3117004bf341ee74893ba4ba8a9fbaf4b94615846550a08/regex-2026.5.9-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0f9eede6a5cbdc02d4978090186390936e1776a7d1359b21e41014c609880bcf", size = 763311 }, + { url = "https://files.pythonhosted.org/packages/93/c7/e7737f1526b3fb32bd4c337fd6c71c3ebb5c8296fc34d11197e0955d2e35/regex-2026.5.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:01f0f5f55f4b64dacec85dc116d3c05fd23ad3ff037bbc73a2085775953c2611", size = 844593 }, + { url = "https://files.pythonhosted.org/packages/a5/27/0daffb1a535bb39f422c3d200f4ab023c71110ad66a32b366bee708baba0/regex-2026.5.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1268eddd8486dc561d08eee1156e40aa3a8fe10f4bdec8fa653b455fcbffd12c", size = 789167 }, + { url = "https://files.pythonhosted.org/packages/ce/fc/294fe4fac4f2ed67207b17471815870c1c45b3a489e08e0ac96daea16ef6/regex-2026.5.9-cp311-cp311-win32.whl", hash = "sha256:8676474c07469d6f33dd1085ca2cd45f65785f32518f2b20e36d9953ca07f994", size = 266249 }, + { url = "https://files.pythonhosted.org/packages/d0/b0/8dce459f6245bcf8f6e9f23ac9569f1a0f15c131cc0745e82b43226204cf/regex-2026.5.9-cp311-cp311-win_amd64.whl", hash = "sha256:246de9d60aa3f8538b519834dd95cbf276ea263d6a7bd5a3666dc3fa0230505b", size = 278423 }, + { url = "https://files.pythonhosted.org/packages/db/8d/f9aeff6ad63a3ef720386f2907e6d34a35a510a6e498ebad28b0fb3f6ab6/regex-2026.5.9-cp311-cp311-win_arm64.whl", hash = "sha256:d726ca3f0d76969bf1e8e477d160d3d666bbf999f6860bd314889e5345782046", size = 270420 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699 }, + { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783 }, + { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691 }, + { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747 }, + { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951 }, + { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240 }, + { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130 }, + { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999 }, + { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746 }, + { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685 }, + { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075 }, +] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, +] + +[[package]] +name = "rfc3986-validator" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, +] + +[[package]] +name = "rfc3987-syntax" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lark" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654 }, +] + +[[package]] +name = "rouge-score" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "nltk" }, + { name = "numpy" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/c5/9136736c37022a6ad27fea38f3111eb8f02fe75d067f9a985cc358653102/rouge_score-0.1.2.tar.gz", hash = "sha256:c7d4da2683e68c9abf0135ef915d63a46643666f848e558a1b9f7ead17ff0f04", size = 17400 } + +[[package]] +name = "rpds-py" +version = "2026.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/1f/a2dca5ffdbf1d475ffc4e80e4d5d720ff3a00f691795910116960ee12511/rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7", size = 342174 }, + { url = "https://files.pythonhosted.org/packages/4d/dc/323d08583c0832911768663d1944f0107fcd4088704858d84b5e06d105a0/rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911", size = 345513 }, + { url = "https://files.pythonhosted.org/packages/0b/2a/e31989834d18d2f26ec1d2774c5b1eb3331df4ea8ada525175294c94b48a/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4", size = 373783 }, + { url = "https://files.pythonhosted.org/packages/87/fe/e80107ee3639585c9941c17d6a42cd65325022f656c023191fce78c324c8/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261", size = 378316 }, + { url = "https://files.pythonhosted.org/packages/22/6f/81e3adf81acfb6fa694de2a6e4e7d8863121e3e0799e0a7725e6cf5679c4/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278", size = 499423 }, + { url = "https://files.pythonhosted.org/packages/2d/9a/41263969df0ce3d9af2a96d5005a288200af1989aed3354bfceb5fc0b21f/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9", size = 386077 }, + { url = "https://files.pythonhosted.org/packages/5e/19/7e98f468bd50346faff5b10e5297374b443bfdddacc8e9fbc65984539597/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7", size = 371315 }, + { url = "https://files.pythonhosted.org/packages/99/3c/2b973b4d371906a134b03decfea7f5d9835a2c6d263454392e15b64b5b18/rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3", size = 383502 }, + { url = "https://files.pythonhosted.org/packages/98/2a/12e2799500af0a307bca76b63361c51f9fe479223561489c29eea1f2ee41/rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da", size = 402673 }, + { url = "https://files.pythonhosted.org/packages/2d/e3/21e5872d165fe08be4f229e3d5ee9d90019c0bf0e5538de60dbd54009450/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4", size = 549964 }, + { url = "https://files.pythonhosted.org/packages/1a/d0/5ee0fe36844297de8123bee27bc12078c1a7416ad9f1b8a8ca18d6b0c0ac/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6", size = 615446 }, + { url = "https://files.pythonhosted.org/packages/b1/80/1ea5873cb683f2fbe5f21b23ea1f6d179ead19f3c5b249b7eb5dca568ef2/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93", size = 576975 }, + { url = "https://files.pythonhosted.org/packages/c9/e1/90ef639217a5ddb15b7f4f61b1c33911fd044ad03c311bafdd2bcab85582/rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a", size = 204453 }, + { url = "https://files.pythonhosted.org/packages/f2/b7/b7a1695d7af36f521fb11e80d6d3adbd744f73b921859bd3c2a2c0dc706f/rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127", size = 223219 }, + { url = "https://files.pythonhosted.org/packages/d7/a2/145afacf796e4506062825941176ad9445c2dcf2b3b6a1f13d3030a15e19/rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804", size = 219137 }, + { url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691 }, + { url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542 }, + { url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180 }, + { url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067 }, + { url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509 }, + { url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754 }, + { url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189 }, + { url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750 }, + { url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576 }, + { url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807 }, + { url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187 }, + { url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030 }, + { url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185 }, + { url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394 }, + { url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753 }, + { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012 }, + { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203 }, + { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984 }, + { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815 }, + { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545 }, + { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828 }, + { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678 }, + { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811 }, + { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382 }, + { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832 }, + { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011 }, + { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431 }, + { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710 }, + { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454 }, + { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063 }, + { url = "https://files.pythonhosted.org/packages/b6/36/7fbe9dcdaf857fb3f63c2a2284b62492d95f5e8334e947e5fb6e7f68c9be/rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e", size = 344510 }, + { url = "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", size = 339495 }, + { url = "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", size = 369454 }, + { url = "https://files.pythonhosted.org/packages/96/4a/9559a68b7ee15db09d7981212e8c2e219d2a1d6d4faa0391d813c3496a36/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda", size = 374583 }, + { url = "https://files.pythonhosted.org/packages/ef/75/8964aa7d2c6e8ac43eba8eb6e6b0fdda1f46d39f2fc3e6aa9f2cb17f485d/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8", size = 492919 }, + { url = "https://files.pythonhosted.org/packages/8f/97/6908094ac804115e65aedfd90f1b5fee4eebebd3f6c4cfc5419939267565/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53", size = 383725 }, + { url = "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", size = 367255 }, + { url = "https://files.pythonhosted.org/packages/c4/fe/f0209ca4a9ed074bc8acb44dfd0e81c3122e94c9689f5645b7973a866719/rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc", size = 379060 }, + { url = "https://files.pythonhosted.org/packages/c6/8d/f1cc54c616b9d8897de8738aac148d20afca93f68187475fe194d09a71b9/rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77", size = 395960 }, + { url = "https://files.pythonhosted.org/packages/fb/04/aafff00f73aeca2945f734f1d483c64ab8f472d0864ab02377fd8e89c3b2/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698", size = 545356 }, + { url = "https://files.pythonhosted.org/packages/fd/cc/e229663b9e4ddac5a4acbe9085dd80a71af2a5d356b8b39d6bff233f24b0/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd", size = 612319 }, + { url = "https://files.pythonhosted.org/packages/e3/7a/8a0e6d3e6cd066af108b71b43122c3fe158dd9eb86acac626593a2582eb1/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d", size = 573508 }, + { url = "https://files.pythonhosted.org/packages/87/03/2a69ab618a789cf6cf85c86bb844c62d090e700ab1a2aa676b3741b6c516/rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8", size = 202504 }, + { url = "https://files.pythonhosted.org/packages/85/62/a3892ba945f4e24c78f352e5de3c7620d8479f73f211406a97263d13c7d2/rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5", size = 220380 }, + { url = "https://files.pythonhosted.org/packages/3d/e7/c2bd44dc831931815ad11ebb5f430b5a0a4d3caa9de837107876c30c3432/rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703", size = 215976 }, + { url = "https://files.pythonhosted.org/packages/79/9c/fff7b74bce9a091ec9a012a03f9ff5f69364eaf9451060dfc4486da2ffdd/rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90", size = 346840 }, + { url = "https://files.pythonhosted.org/packages/e9/44/77bcb1168b33704908295533d27f10eb811e9e3e193e8993dc99572211d3/rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4", size = 340282 }, + { url = "https://files.pythonhosted.org/packages/87/3c/7a9081c7c9e645b39efe19e4ffbeccd80add246327cd9b888aecffd72317/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9", size = 370403 }, + { url = "https://files.pythonhosted.org/packages/f7/69/af47021eb7dad6ff3396cb001c08f0f3c4d06c20253f75be6421a59fe6b7/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f", size = 376055 }, + { url = "https://files.pythonhosted.org/packages/81/fc/a3bcf517084396a6dd258c592567a3c011ba4557f2fde23dceaf26e74f2e/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41", size = 494419 }, + { url = "https://files.pythonhosted.org/packages/c9/eb/13d529d1788135425c7bf207f8463458ca5d92e43f3f701365b83e9dffc1/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945", size = 384848 }, + { url = "https://files.pythonhosted.org/packages/8e/f4/b7ac49f30013aba8f7b9566b1dd07e81de95e708c1374b7bacc5b9bc5c9c/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f", size = 371369 }, + { url = "https://files.pythonhosted.org/packages/31/86/6260bafa622f788b07ddec0e52d810305c8b9b0b8c27f58a2ab04bf62b4f/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1", size = 379673 }, + { url = "https://files.pythonhosted.org/packages/19/c3/03f1ee79a047b48daeca157c89a18509cde22b6b951d642b9b0af1be660a/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e", size = 397500 }, + { url = "https://files.pythonhosted.org/packages/f0/95/8ed0cd8c377dca12aea498f119fe639fc474d1461545c39d2b5872eb1c0f/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538", size = 545978 }, + { url = "https://files.pythonhosted.org/packages/d3/f2/0eb57f0eaa83f8fc152a7e03de968ab77e1f00732bebc892b190c6eebde7/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db", size = 613350 }, + { url = "https://files.pythonhosted.org/packages/5b/de/e0674bdbc3ef7634989b3f854c3f34bc1f587d36e5bfdc5c378d57034619/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2", size = 576486 }, + { url = "https://files.pythonhosted.org/packages/f2/f6/21101359743cd136ada781e8210a85769578422ba460672eea0e29739200/rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e", size = 201068 }, + { url = "https://files.pythonhosted.org/packages/a6/b2/9574d4d44f7760c2aa32d92a0a4f41698e33f5b204a0bf5c9758f52c79d5/rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2", size = 220600 }, + { url = "https://files.pythonhosted.org/packages/08/ae/f23a2697e6ee6340a578b0f136be6483657bef0c6f9497b752bb5c0964bb/rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13", size = 344726 }, + { url = "https://files.pythonhosted.org/packages/c3/63/e7b3a1a5358dd32c930a1062d8e15b67fd6e8922e81df9e91706d66ee5c8/rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05", size = 339587 }, + { url = "https://files.pythonhosted.org/packages/ec/64/10a85681916ca55fffb91b0a211f84e34297c109243484dd6394660a8a7c/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba", size = 369585 }, + { url = "https://files.pythonhosted.org/packages/76/c2/baf95c7c38823e12ba34407c5f5767a89e5cf2233895e56f608167ae9493/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617", size = 375479 }, + { url = "https://files.pythonhosted.org/packages/6a/94/0aad06c72d65101e11d33528d438cda99a39ce0da99466e156158f2541d3/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9", size = 492418 }, + { url = "https://files.pythonhosted.org/packages/b5/17/de3f5a479a1f056535d7489819639d8cd591ea6281d700390b43b1abd745/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb", size = 384123 }, + { url = "https://files.pythonhosted.org/packages/46/7d/bf09bd1b145bb2671c03e1e6d1ab8651858d90d8c7dfeadd85a37a934fd8/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885", size = 367351 }, + { url = "https://files.pythonhosted.org/packages/a3/ea/1bb734f314b8be319149ddee80b18bd41372bdcfbdf88d28131c0cd37719/rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a", size = 378827 }, + { url = "https://files.pythonhosted.org/packages/4b/93/d9611e5b25e26df9a3649813ed66193ace9347a7c7fc4ab7cf70e94851c0/rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868", size = 395966 }, + { url = "https://files.pythonhosted.org/packages/c3/cb/99d77e16e5534ae1d90629bbe419ba6ee170833a6a85e3aa1cc41726fbbc/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187", size = 545680 }, + { url = "https://files.pythonhosted.org/packages/59/15/11a29755f790cef7a2f755e8e14f4f0c33f39489e1893a632a2eee59672b/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107", size = 611853 }, + { url = "https://files.pythonhosted.org/packages/68/86/0c27547e21644da938fb530f7e1a8148dd24d02db07e7a5f2567a17ce710/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba", size = 573715 }, + { url = "https://files.pythonhosted.org/packages/29/71/4d8fcf700931815594bce892255bbd973b94efaf0fc1932b0590df18d886/rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369", size = 202864 }, + { url = "https://files.pythonhosted.org/packages/eb/62/b577562de0edbb55b2be85ce5fd09c33e386b9b13eee09833af4240fd5c4/rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146", size = 220430 }, + { url = "https://files.pythonhosted.org/packages/c8/95/d6d0b2509825141eef60669a5739eec88dbc6a48053d6c92993a5704defe/rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e", size = 215877 }, + { url = "https://files.pythonhosted.org/packages/b7/bf/f3ea278f0afd615c1d0f19cb69043a41526e2bb600c2b536eb192218eb27/rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b", size = 346933 }, + { url = "https://files.pythonhosted.org/packages/9d/29/9907bdf1c5346763cf10b7f6852aad86652168c259def904cbe0082c5864/rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690", size = 340274 }, + { url = "https://files.pythonhosted.org/packages/6f/2c/8e03767b5778ef25cebf74a7a91a2c3806f8eced4c92cb7406bbe060756d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342", size = 370763 }, + { url = "https://files.pythonhosted.org/packages/2e/e1/df2a7e1ba2efd796af26194250b8d42c821b46592311595162af9ef0528d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6", size = 376467 }, + { url = "https://files.pythonhosted.org/packages/6b/de/8a0814d1946af29cb068fb259aa8622f856df1d0bab58429448726b537f5/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140", size = 496689 }, + { url = "https://files.pythonhosted.org/packages/df/f3/f19e0c852ba13694f5a79f3b719331051573cb5693feacf8a88ffffc3a71/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442", size = 385340 }, + { url = "https://files.pythonhosted.org/packages/e2/ae/7ec3a9d2d4351f99e37bcb06b6b6f954512646bfdbf9742e1de727865daf/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12", size = 372179 }, + { url = "https://files.pythonhosted.org/packages/d3/ac/9cee911dff2aaa9a5a8354f6610bf2e6a616de9197c5fff4f54f82585f1e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5", size = 379993 }, + { url = "https://files.pythonhosted.org/packages/83/6b/7c2a07ba88d1e9a936612f7a5d067467ed03d971d5a06f7d309dff044a7e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf", size = 398909 }, + { url = "https://files.pythonhosted.org/packages/97/0b/776ffcb66783637b0031f6d58d6fb55913c8b5abf00aeecd46bf933fb477/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00", size = 546584 }, + { url = "https://files.pythonhosted.org/packages/55/33/ba3bc04d7092bd553c9b2b195624992d2cc4f3de1f380b7b93cbee67bd79/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef", size = 614357 }, + { url = "https://files.pythonhosted.org/packages/8b/71/14edf065f04630b1a8472f7653cad03f6c478bcf95ea0e6aed55451e33ea/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a", size = 576533 }, + { url = "https://files.pythonhosted.org/packages/ba/76/65002b08596c389105720a8c0d22298b8dc25a4baf89b2ce431343c8b1de/rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577", size = 201204 }, + { url = "https://files.pythonhosted.org/packages/8c/97/d855d6b3c322d1f27e26f5241c42016b56cf01377ea8ed348285f54652f0/rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324", size = 220719 }, + { url = "https://files.pythonhosted.org/packages/b4/9c/f0d19ac587fd0e4ab6b72cda355e9c5a6166b01ef7e064e437aef8eb9fef/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f", size = 349791 }, + { url = "https://files.pythonhosted.org/packages/38/c7/1d49d204c9fd2ee6c537601dc4c1ba921e03363ca576bfab94a00254ac9a/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171", size = 352842 }, + { url = "https://files.pythonhosted.org/packages/ac/e5/c0b5dc93cd0d4c06ce1f438907649514e2ea077bcd911e3154a51e96c38e/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90", size = 382094 }, + { url = "https://files.pythonhosted.org/packages/0d/54/ec0e907b4ca8d541112db352409bd15f871c9b243e0c92c9b5a46ae96f01/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca", size = 388662 }, + { url = "https://files.pythonhosted.org/packages/d3/f4/921c22a4fd0f1c1ac13a3996ffbf0aa67951e2c8ad0d1d9574938a2932e8/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9", size = 504896 }, + { url = "https://files.pythonhosted.org/packages/0b/1b/a114b972cefa1ab1cdb3c7bb177cd3844a12826c507c722d3a73516dbbaf/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c", size = 391545 }, + { url = "https://files.pythonhosted.org/packages/4e/98/af9b3db77d47fcbe6c8c1f36e2c2147ec70292819e99c325f871584a1c11/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9", size = 380059 }, + { url = "https://files.pythonhosted.org/packages/c9/ba/0efd8668b97c1d26a61566386c636a7a7a09829e474fdf807caa15a2c844/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41", size = 393235 }, + { url = "https://files.pythonhosted.org/packages/62/90/8c139ee9690f73b0829f32647de6f40d826f8f443af6fa72644f96351aac/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c", size = 413008 }, + { url = "https://files.pythonhosted.org/packages/9c/97/0043896fdd7828ce09a1d9a8b06433714d0960fc4ff3fc4aa72b666b764e/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9", size = 558118 }, + { url = "https://files.pythonhosted.org/packages/f6/40/02355f0e134f783a8f9814c4680a1bd311d37671577a5964ea838573ff37/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76", size = 623138 }, + { url = "https://files.pythonhosted.org/packages/10/85/48f0abdcef5cce4e034c7a5b0ceeceba0b01bf0d942824f4bb720afe2dec/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826", size = 586486 }, +] + +[[package]] +name = "sacrebleu" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "lxml" }, + { name = "numpy" }, + { name = "portalocker" }, + { name = "regex" }, + { name = "tabulate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/ed/d7acddcff74d690c56fe26a1f7828bdde548262828d0743414ea916c40c1/sacrebleu-2.6.0.tar.gz", hash = "sha256:91499b6cd46138d95154fff1e863c2f9be57e82f0c719d8dd718d0006cf6c566", size = 1893419 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/f2/6c90ccf3ad1d09a7d662a405b274f3c93b92df59c8d6a025d26aaf34d302/sacrebleu-2.6.0-py3-none-any.whl", hash = "sha256:3edc1531575cfe4ad04ce53491a9307e234af1c3f805a1f491cbec844229a8a8", size = 100785 }, +] + +[[package]] +name = "safetensors" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/06/f955dbbb1859e3bd23c8ac6141af5106e7ad5fedec4a3a6e3d60f94b7001/safetensors-0.8.0.tar.gz", hash = "sha256:fabaf3e0f18a6618d9b36560682562157f77c2b71fcffc7b432be2baed9d753d", size = 325846 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/a0/f718cda65b05407d228f97602cf60dca269c979867aa5beb25410de26cd3/safetensors-0.8.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c554f85858e05226d3c2828e32395e677434685d6d94594a41643361c5e837f0", size = 473568 }, + { url = "https://files.pythonhosted.org/packages/f5/b1/fa7c600e7dceae12e9606c7578cbc9ff1e1ed55844883ee5c92205e86226/safetensors-0.8.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c80201d22cbf405b80647a60ada77bba06c8fba2da2743ba1e89cdcc39a81f25", size = 484562 }, + { url = "https://files.pythonhosted.org/packages/09/7d/65a7de0af421317bb36a067241e4235fff194eed60b961ed6d3f59a3fc60/safetensors-0.8.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a46e5ff292c356d6991e60942ba7f79817682d3a2cef0702136448cb9c4d235", size = 502844 }, + { url = "https://files.pythonhosted.org/packages/91/4f/3175c9d75634e0e0dda0082794193521035edd7c70a6f212bf33ca06ddf4/safetensors-0.8.0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4124502b78f03534117c848f87a39b8f31e577b15eff423bf8bfb95f2a8c30d0", size = 511823 }, + { url = "https://files.pythonhosted.org/packages/20/87/846c289e7aa2299eff406335717cf43ce8777194ece8aad75772e0411615/safetensors-0.8.0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bc0a787ba8a35be368ee3574edfa2b1ad389eebd0a72e482ae275490e3f6c98", size = 633461 }, + { url = "https://files.pythonhosted.org/packages/76/22/8d64d9df2c45d5ded401df889d0ad90882804ca172d79ec4f0df8f727fe0/safetensors-0.8.0-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040070828e36dc8e122178bbbd5830ff9e97920affb84cbe0f46442497bed358", size = 545148 }, + { url = "https://files.pythonhosted.org/packages/28/50/f203ff3a3ddfe19308efc83c5a3a29ed02bf786732ec35e68bf9162f3365/safetensors-0.8.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd6f3f93c9a0a7cc2788ee63fb763353d4bd2e89b0751bc78fcf7dda00bea774", size = 516040 }, + { url = "https://files.pythonhosted.org/packages/46/fb/cdaed17ceb2948784fd9c36b6fd3e951b608547cea81a48e8ee6f8cfdfcb/safetensors-0.8.0-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:fcdd41ec4628fee5799f807c73c353629130fbd942aa23d83c623dd6c9d52d78", size = 513832 }, + { url = "https://files.pythonhosted.org/packages/0d/49/1e15de264dcc3b77943d2d0c56a95809956883b1c2d6d585c792523f180b/safetensors-0.8.0-cp310-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e9f537aa183a38ace122d27303dcd986b26bd2a7591f9181d7f0c396f4677ca", size = 559930 }, + { url = "https://files.pythonhosted.org/packages/2a/43/bf38443278eab4b1be1fce2931e2b012ad9cb7df52ada751d0aab8f7659a/safetensors-0.8.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87eec7ffed2b809f05a398a8becb7d013f19f7837cd15d9748580d6cf30dbaf4", size = 678670 }, + { url = "https://files.pythonhosted.org/packages/72/e3/68cd3fa5b48488e84add63e04cb12f3bc28ae4638c06d4508c6e88823d0e/safetensors-0.8.0-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:4a95ae2b05d7726d751da4ebf626a2ca782b706e101bd894c95bc2450b1cffcc", size = 786679 }, + { url = "https://files.pythonhosted.org/packages/29/4b/1c19c509d56e01f4fbb3d0a2e597450f6cc04d1d56cf52defb0a62dfd715/safetensors-0.8.0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae091f16662658bdc019a4ff6cb4c085bb7d725eb5978b183ffd265863b6d2d", size = 765683 }, + { url = "https://files.pythonhosted.org/packages/27/43/41c1621732edd934d868a00d1b891584c892a7b62a9aab82ea5a0a5623ee/safetensors-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8e080062fcde23be189565e1c3305d16751a218ecf9412c8601e64204eb6f846", size = 722361 }, + { url = "https://files.pythonhosted.org/packages/8e/3f/73ccf82579412b4a71c4ca673f10b5f1f888d7cf5af7fe24f27d30307be4/safetensors-0.8.0-cp310-abi3-win32.whl", hash = "sha256:2ddf52eac562eda224f99acfa7889d02968c1fd59a5b011ae7d8137c37e9c02d", size = 342401 }, + { url = "https://files.pythonhosted.org/packages/1b/6d/3fba214c1e5e0f69991677ec3bc17023f0421776975e1de0c682dca475e2/safetensors-0.8.0-cp310-abi3-win_amd64.whl", hash = "sha256:096ec1a98435df7beb08853bb5aa9081a84f23d0adc67ed1a0a10550f608373f", size = 355540 }, + { url = "https://files.pythonhosted.org/packages/8d/fc/7eedc3510d97878876e32774eebbeb61c43f148a96e915c84229a3e967aa/safetensors-0.8.0-cp310-abi3-win_arm64.whl", hash = "sha256:f7838e5135a406ad3e02efdcb8cf2e5397d368b0154537c4fec682dbc544d452", size = 340500 }, +] + +[[package]] +name = "scikit-learn" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "narwhals" }, + { name = "numpy" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/6f/37092bdb25f712817231799fc5674d8e704066a8a70c1d2d40517e18b4ab/scikit_learn-1.9.0.tar.gz", hash = "sha256:8833266989d3a5110178a9fae30783675460724d0e1efb13b14901d2c660c557", size = 7750767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/be/e844fd9586e66540a15b71924d17a6cbc1bb749e81ddd0a796bcdba4c055/scikit_learn-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9db6f4d34e68c8899e4cab27fdf8eafe6ed21f2ba52ceb25ea250cd237f8e47b", size = 8789686 }, + { url = "https://files.pythonhosted.org/packages/42/e2/ff880f62677a17d035817d543cb0fc8727d01eccbee81c5f7fc733a9d856/scikit_learn-1.9.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f401448645a3e7bc115aa3c094097865155b34bff1cba8101857d9104e99074c", size = 8256782 }, + { url = "https://files.pythonhosted.org/packages/25/64/eb40435e1a508ab1b4e284ce43ae80f6a162e5be5e38ed5a6fab467a9ea4/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd3a8ef0c758555a3b23c03adaa858af32f7736785ded50ad5991f59c4ed03fa", size = 8992419 }, + { url = "https://files.pythonhosted.org/packages/8d/da/4810a28e473185429e45a57eebcc91fc991b33d889cc0676063e671db03d/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7e254636164090da847715a27f8e5478feb98c40a9e0ee90cbd277de9e5ceb8", size = 9281411 }, + { url = "https://files.pythonhosted.org/packages/3b/67/be3d369f40d8178ba3bd86635d132e08cb5329b023e4669d9426d84bc007/scikit_learn-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:5dc1818c77575d149e25fce9ef82dd7b7263ae372f03494158668ad632a69759", size = 8272736 }, + { url = "https://files.pythonhosted.org/packages/37/79/a733f02dc2118da7e77a134b34f39f40201a353311b011d20859d2db3556/scikit_learn-1.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:366652351f092b219c248f1e72821e841960a63d8f358f1dcfd54dc1cbdbbc28", size = 7919564 }, + { url = "https://files.pythonhosted.org/packages/ac/20/75f915ff375d6249e6550ac740fdbbd66159a068fd3af1400ff62036b07a/scikit_learn-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2bd41b0d201bc81575531b96b713d3eb5e5f50fb0b82101ff0f92294fdc236ac", size = 8741122 }, + { url = "https://files.pythonhosted.org/packages/cc/d5/2b5148f2279196775e1db2aeb85d14b70ac80e7e32b3b28e7ebeafb0901d/scikit_learn-1.9.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5be45aa4a42a68a533913a6ed736cf309de2226411c79ef8d609a5456f1939b1", size = 8261512 }, + { url = "https://files.pythonhosted.org/packages/a0/ee/5adbc77656b71f9456a2f5a7a9fdb4bcf9207a6b962889f1c2f9323afa4e/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e50ed4da51974e86e940690e9a3d82e729b62b5a49f7c9bac534d515d39d86f", size = 8837603 }, + { url = "https://files.pythonhosted.org/packages/6c/c2/63fdda36c56437eeb44aaf9493c8bcd62ce230ab1598924fc626ffbfa943/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:056c92bb67ad4c28463c2f2653d9701449201e7e7a9e94e321be0f71c4fef2b8", size = 9132097 }, + { url = "https://files.pythonhosted.org/packages/83/a4/c8e67227c680e2259c8864ae72ff48b06e16a6f51253a22167aa02a8aa4e/scikit_learn-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4306775fad04cc4b472a1b15af1ae9cede1540fbfcc17fbce3767cd8dc7ae283", size = 8211173 }, + { url = "https://files.pythonhosted.org/packages/cf/fd/3c0863792e98e67e9184aa4029288a175935eb65443afcd30d4f143450cf/scikit_learn-1.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:26e22435f63bcdcf396b574273f29f13dd531f5ea035801f5be10ba1540a4e60", size = 7867451 }, + { url = "https://files.pythonhosted.org/packages/3c/01/cf3310626b6d48d3e9be69a1223f9180360b5e6edb045f50fade723ce494/scikit_learn-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:80746d63bd4b6eaca54d36fe5feaf4d28bb38dc6f9470f81c7cad7c40155f119", size = 8705188 }, + { url = "https://files.pythonhosted.org/packages/3e/04/5acd7ae280c5f93b6ac5ef6cdec14eef4c8d1cd91d85b3292989c94d96b1/scikit_learn-1.9.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5b934c45c252844a91d69fda3a34cff5e7307e1db10d77cb10a3980312c74713", size = 8228299 }, + { url = "https://files.pythonhosted.org/packages/0c/39/ffe829a5b8ecb40a518724a997794657fdc354ada5e8fe8e64d998c0bac9/scikit_learn-1.9.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:38c3dcb9a1ffb85505ec53d54c7b4aea0cff70050425a7760c2af661ac85df05", size = 8789690 }, + { url = "https://files.pythonhosted.org/packages/1f/88/8dab5de10c638c083772a6be83a3d8106ced492f74a928c8693638e5bb50/scikit_learn-1.9.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da76d09304a4706db7cc1e3ebaa3b6b98a67365cc11d2996c4f1e58ba47df714", size = 9087723 }, + { url = "https://files.pythonhosted.org/packages/20/3f/7917ca72464038f6240ec70c29f94862d08a34a74291ae4d4ec5eb8186a0/scikit_learn-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5808d98f15c6bf6d9d96d2348c1997392a5888ce7097e664105f930c4bca1277", size = 8184330 }, + { url = "https://files.pythonhosted.org/packages/78/c7/15739eb2f61fda3c54639e9942414e5a19ad8a8d1f5a3266afad7cb7df80/scikit_learn-1.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:d77f54c017633791bc0225a43e2f8d03745fdcfe4880268fcc4df15f505dec2e", size = 7840653 }, + { url = "https://files.pythonhosted.org/packages/f4/7d/c9a35cf59b20a86fec24d306f1547b78dec194b08d367ce2a3e4854169d9/scikit_learn-1.9.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9656acd4e93f74e0b66c8a36c88830a99252dfa900044d36bc2212ae89a47162", size = 8713289 }, + { url = "https://files.pythonhosted.org/packages/3c/a7/552a7821597c632b907f7bfe8f36f9f572777af8ef8a48353041cf8e091a/scikit_learn-1.9.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:24360002ae845e7866522b0a5bbf690802e7bc388cac8663502e78aa98598aa2", size = 8245141 }, + { url = "https://files.pythonhosted.org/packages/7d/79/f4a0c4fe9711154cddabf913471153af79056382ddc612cfe5ee0ff4b72e/scikit_learn-1.9.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5162ad10a418c8a282dde04c9aa06965de3e9a65f33c1440c0ae69bb1a09d913", size = 8847671 }, + { url = "https://files.pythonhosted.org/packages/f0/af/4d72d9e475ac83719160c662619e4bf7b95c19507cd582e7d0167a3c3dae/scikit_learn-1.9.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fea2cc5677ab49d6f5bade978c866da44957b712d92e9635e8b4f723013c3cb", size = 9118104 }, + { url = "https://files.pythonhosted.org/packages/a2/d5/6a58eea2cb9abbb9b3f2bb8b2cfb3243d1152d69f442d256c7af71304769/scikit_learn-1.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:64fa347efc1c839c487433e40c5144d38c336e8a2b59c81aa8660373945c2673", size = 8290674 }, + { url = "https://files.pythonhosted.org/packages/65/5b/d4c879cf358f1187141cf90ced473f087183489090244f50c124a2ee478b/scikit_learn-1.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:1b944b6db288f6b926e3650026ddafb988929de95d11fc2cc5fa117773c9ba42", size = 7978807 }, + { url = "https://files.pythonhosted.org/packages/8a/43/bfae3121ec67ae09150d453c442c7c1cc166e9aefe056e6ab3b7728a5cfc/scikit_learn-1.9.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4ccacf04ca5f4b492158a5f28afe0ace43f81b2571e4b9a66d34848b46128949", size = 9031941 }, + { url = "https://files.pythonhosted.org/packages/75/b0/20a4546eb17f3b25d3c66df15810411c14ed5065bcfab50b53c96fb627b2/scikit_learn-1.9.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ee1a8db2c18c08e34c7412d4b10be1cac214cd4ea7dc9715a6a327eb49a37c96", size = 8613528 }, + { url = "https://files.pythonhosted.org/packages/18/3c/e440e039bb82cd19004edaaad00acbde0fb9b461083c3ecf37941c557312/scikit_learn-1.9.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:147e9329ef0e39f75d4cffa02b2aa48d827832684926cd5210d9a2cb5c57246b", size = 8855050 }, + { url = "https://files.pythonhosted.org/packages/43/26/b341b8dab5998da6270a3a42c2152c578501354d36f944b5856757035ef8/scikit_learn-1.9.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bad8f8b9950321b54c965fdcbac6c6c55e79e16646b49977bcf3668d3870a1a", size = 9097190 }, + { url = "https://files.pythonhosted.org/packages/fb/de/b650b4d69b84468cfa2e28a3ff7b8103743029e6446ce1a97fe060ef688c/scikit_learn-1.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:78fc56eafd4edb9575d2d8950d1dd152061abb573341a1cb7e099fc40f6c6666", size = 8963204 }, + { url = "https://files.pythonhosted.org/packages/ee/f3/ff83d76d7418112e5a61326443cdda87be3545dd8d6599c95b2481a4419e/scikit_learn-1.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:051075bda8b7aab87b1906ab3d4740a1e1224a19d7b3781a576736edc94e76aa", size = 8222661 }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675 }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057 }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032 }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533 }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057 }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300 }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333 }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314 }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512 }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248 }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954 }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662 }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366 }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017 }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842 }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890 }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557 }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856 }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682 }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340 }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199 }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001 }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719 }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595 }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429 }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952 }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063 }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449 }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943 }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621 }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708 }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135 }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977 }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601 }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667 }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159 }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771 }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910 }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980 }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543 }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510 }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131 }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032 }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766 }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007 }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333 }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066 }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763 }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984 }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877 }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750 }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858 }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723 }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098 }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397 }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163 }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291 }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317 }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327 }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165 }, +] + +[[package]] +name = "scipy" +version = "1.18.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "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 != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/19/ca10ead60b0acc80b2b833c2c4a4f2ff753d0f58b811f70d911c7e94a25c/scipy-1.18.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:7bd21faaf5a1a3b2eff922d02db5f191b99a6518db9078a8fb23169f6d22259a", size = 31056519 }, + { url = "https://files.pythonhosted.org/packages/96/72/1e6442a00cd2924d361aa1b642ab6373ec35c6fabf311a760be9f76e0f13/scipy-1.18.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:265915e79107de9f946b855e50d7470d5893ec3f54b342e1aa6201cbdcd8bb6b", size = 28681889 }, + { url = "https://files.pythonhosted.org/packages/9b/2d/11dd93d21e147a73ba22bd75c0b9208d3a2e0ec76d53170ce7d9029b1015/scipy-1.18.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9ab7b758be6940954a713ee466e2043e9f6e2ed965c1fce5c91039f4be3d90a9", size = 20423580 }, + { url = "https://files.pythonhosted.org/packages/9c/01/93552f75e0d2a7dd115a45e59209c51e8d514daff02fc887d2623be06fe1/scipy-1.18.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:97b6cddaaee0a779ef6b5ca83c9604b27cc16b2b8fc22c142652df8793319fb8", size = 23054441 }, + { url = "https://files.pythonhosted.org/packages/3c/23/21f5e703643d66f21faa6b4c73195bfcad70c55efcb4f1ab327cd7c4101a/scipy-1.18.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52a96e21517c7292375c0e27dd796a811f03fcea5fd4d108fdfea8145dcf17ab", size = 33968720 }, + { url = "https://files.pythonhosted.org/packages/dd/aa/1b939f6c67ed68635bb538e6752d3dacc02f66535182e939a89581a44e9c/scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f55797419e16e7f30cf88ffb3113ce0467f00cfe3f70d5c281730b21769bfc2", size = 35287115 }, + { url = "https://files.pythonhosted.org/packages/b6/ff/eec46be7e9234208f801062b53e1983085eddebd693f6c9bfb03b459830d/scipy-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ad033410e2e0672ffdc1042110cef20e1c46f8fd0616cee1d44d8d58fad8fc11", size = 35577989 }, + { url = "https://files.pythonhosted.org/packages/84/ca/210d4759c7210bb7d269437421959b39a33434e2776b60c5cb8a763bb30a/scipy-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a55985d54c769c872e64b7f4c8a81cc30ef700cc04296abbbf3705439c126de", size = 37421717 }, + { url = "https://files.pythonhosted.org/packages/2b/54/9a9edb45345bd6744da5ddfb6628e5d5185920494c6a67ec45b6381004cb/scipy-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:71ccc8faa2dd16ac310233203474a8b5cb67f10dedd54a3116d34943f4b19132", size = 36597428 }, + { url = "https://files.pythonhosted.org/packages/99/0e/33f32a2a58987e26aec0f7df252cbbad1e90ae77bdbc76f40dd4ed0cf0ea/scipy-1.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:d88363fd9d8fbd3511bd273f1a49efb2a540773ddf92a91d57498ce7dd7f3e76", size = 24351481 }, + { url = "https://files.pythonhosted.org/packages/05/52/9c0136c2de7ae0779b7b366447766cec6d9f0702c56bb8ffeb04c8fd3af4/scipy-1.18.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:09143f676d157d9f546d663504ef9c1becb819824f1afc018814176411942446", size = 31036107 }, + { url = "https://files.pythonhosted.org/packages/02/73/0291a64843270f4efb86cdcf2ee0f2048631b65ec6b405398b2b4dbf11bf/scipy-1.18.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5efe260f69417b97ddae455bfb5a95e8359f7f66ad7fa9522a60feb66f169520", size = 28663303 }, + { url = "https://files.pythonhosted.org/packages/d3/0f/10ffa0b697a572f4e0d48b92a88895d366422f019f723e7e14a84c050dac/scipy-1.18.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:68363b7eaacd8b5dd426df56d782cc156468ac79a127a1b87ca597d6e2e82197", size = 20404960 }, + { url = "https://files.pythonhosted.org/packages/7e/d2/e896cea21ba8edd6c81d4c55b1ffcc717e79698dcbebf9641b4cfb4c6622/scipy-1.18.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:c5557d8be5da8e41353fcd4d21491fdbab83b062fc579e94dc09a7c8ab4f669b", size = 23034074 }, + { url = "https://files.pythonhosted.org/packages/ea/b2/e83ea34279a52c03374477c74006256ec78df65fc877baa4617d6de1d202/scipy-1.18.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0d13bca67c096d89fb95ced0d8921807300fce0275643aef9533cc63a0773468", size = 33942038 }, + { url = "https://files.pythonhosted.org/packages/f6/af/e8fe5fb136f51e2b01678b92cb4106d10d8cd68ec147ead2e7cb0ac75398/scipy-1.18.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a46f9273dbd0eb1cefba61c9b8648b4dfe3cbc14a080176f9a73e44b8336dc7f", size = 35266390 }, + { url = "https://files.pythonhosted.org/packages/3a/49/2c5cbb907b56695fc67517811d1db234dfd83381a84814ec220aded2794d/scipy-1.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5aba46108853ddfc77906b6557aac839d2b52e900c1d72a1180adaaab58d265f", size = 35551324 }, + { url = "https://files.pythonhosted.org/packages/bb/73/eda39f7a2d306ff0ffc574afd13c0bbb6d10a603d9a413998ee269487a80/scipy-1.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b6f758e35f12757b5d95c00bc6de2438e229c2664b7a92e96f205959d9f2dfa4", size = 37404785 }, + { url = "https://files.pythonhosted.org/packages/b7/d2/ae881ee28d014f38e0ccbfd974a06a919ba9af34f1f74bf42b5301891d63/scipy-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:1afac4a847207c7ff8efd321734a50b06d0280b3b2a2c0fc2f413101747ad7c7", size = 36554943 }, + { url = "https://files.pythonhosted.org/packages/70/3a/21154e2d54eb3639c6bf4dbae2e531c68356bfe95990daa30df33b30d556/scipy-1.18.0-cp313-cp313-win_arm64.whl", hash = "sha256:c5dbddf60e58c2312316d097271a8e73d40eaf2eabfa4d95ed7d3695bbf2ce7b", size = 24350911 }, + { url = "https://files.pythonhosted.org/packages/78/b5/915a19b3de2f7430062b509653563db1633ddbb6f021b06731521115d4e2/scipy-1.18.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4c256ee70c0d1a8a2ace807e199ccd4e3f57037433842abb3fb36bc17eaa9578", size = 31036253 }, + { url = "https://files.pythonhosted.org/packages/d7/88/b72def7262e150d16be13fca37a96481138d624e700340bc3362a7588929/scipy-1.18.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:2ef3abc54a4ffc53765374b0d5728532dfdd2585ed23f6b11c206a1f0b1b9af8", size = 28673758 }, + { url = "https://files.pythonhosted.org/packages/91/02/2e636a61a525632c373cf6a9c24442a3ffb79e364d38e98b32042964ac32/scipy-1.18.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f2a6af57bd9e4a75d70e4117e78a1bbee84f79ae3fbb6d0111005d6ebcc4cb8d", size = 20415514 }, + { url = "https://files.pythonhosted.org/packages/c9/b6/2135974442f6aba159d9d39d774a1c8cb19947016725d69fecc685df45bf/scipy-1.18.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:3f1ac564d3bf6c03d861d2cd87a1bea0da2887136f7fb1bf519c05a8971452d6", size = 23034398 }, + { url = "https://files.pythonhosted.org/packages/f6/e6/ba89ec5abf6ee9257c0d1ec985573f3ae32742c24bc03e016388a40b1b15/scipy-1.18.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40395a5fcd1abee49a5c7aaa98c29db393eedc835138560a588c47ec16156690", size = 33998032 }, + { url = "https://files.pythonhosted.org/packages/7f/c4/bc41eb19b0fd0db868f4132920879019318d80cc522ad8f2bca4611af808/scipy-1.18.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ca01e8ae69f1b18e9a58d91afead31be3cef0dd905a10249dac559ee15460a0", size = 35283333 }, + { url = "https://files.pythonhosted.org/packages/53/a4/cbdeef6eb3830a8462a9d4ada814de5fc984345cc9ecf17cbec51a036f1e/scipy-1.18.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7a7f3b01647384dbc3a711e8c6778e0aabbe93959249fef5c7393396bcac0867", size = 35610216 }, + { url = "https://files.pythonhosted.org/packages/80/4d/b2b82502b65f661d1b789c1665dcdf315d5f12194e06fc0b37946294ebae/scipy-1.18.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6aa94e78ec192a30063a5e72e561c28af769dc311190b24fe91774eff1969709", size = 37418960 }, + { url = "https://files.pythonhosted.org/packages/93/3e/902d836831474b0ab5a37d16404f7bc5fafd9efba632890e271ba952635f/scipy-1.18.0-cp314-cp314-win_amd64.whl", hash = "sha256:2d8bbdc6c817f5b4006a54d799d4f5bab6f910193cbb9a1ff310833d4d270f61", size = 37288845 }, + { url = "https://files.pythonhosted.org/packages/b6/43/8d73b337a3bdb14daa0314f0434210747c02d79d729ce1777574a817dcf6/scipy-1.18.0-cp314-cp314-win_arm64.whl", hash = "sha256:18e9575f1569b2c54174e6159d32942e03731177f63dce7975f0a0c88d102f5b", size = 24988971 }, + { url = "https://files.pythonhosted.org/packages/b4/b4/f11918b0508a2787031a0499a03fbe3546f3bb5ca05d01038c45b278c09a/scipy-1.18.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f351e0dd702687d12a402b867a1b4146a256923e1c38317cbc472f6372b94707", size = 31399325 }, + { url = "https://files.pythonhosted.org/packages/7b/d1/1f287b57c0ff0ee5185dff3946d92c8017d39b0e431f0ae79a3ff1859512/scipy-1.18.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7c7a51b33ce387193c97f228320cf8e87361daa1bba750638677729598b3e677", size = 29092110 }, + { url = "https://files.pythonhosted.org/packages/ff/1a/7b74eb6c392fdcb27d414c0e7558a6d0231eb3b6d73571f479bb81ea8794/scipy-1.18.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:84031d7b052a54fae2f8632e0ec802073d385476eb9a63079bce6e23ef9283d4", size = 20833811 }, + { url = "https://files.pythonhosted.org/packages/7c/ad/f3941716320a7b9cb4d68734a903b45fe16eff5fb7da7e16f2e619304979/scipy-1.18.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:56abf29a7c067dde59be8b9a22d606a4ea1b2f2a4b756d9d903c62818f5dacce", size = 23396644 }, + { url = "https://files.pythonhosted.org/packages/22/22/1446b62ffe07f9719b7d9b1b6a4e05a772833ae8f441fe4c22c34c9b250f/scipy-1.18.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ad44305cfa24b1ba5803cbbebf033590ccbac1aa5d612d727b785325ab408b0", size = 34079318 }, + { url = "https://files.pythonhosted.org/packages/56/3b/b87da667098bb470fa30c7011b0ba351ee976dd395c78798c66e941665a3/scipy-1.18.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:945c1761b93f38d7f99ae81ae80c63e621471608c7eeead563f6df025585cd58", size = 35324320 }, + { url = "https://files.pythonhosted.org/packages/f8/a1/c7932f91909759b0267f75fdea34e91309f96b895757534b76a90b6b4344/scipy-1.18.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1a4441f15d620578772a49e5ab48c0ee1f7a0220e387110283062729136b2553", size = 35699541 }, + { url = "https://files.pythonhosted.org/packages/f7/86/5185061a1fcc41d18c5dc2463969b3a3964b31d9ac67b2fb05d4c7ff7670/scipy-1.18.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9aac6192fac56bf2ca534389d24623f07b39ff83317d58287285e7fbd622ff76", size = 37472480 }, + { url = "https://files.pythonhosted.org/packages/31/8e/f04c68e39919a010d34f2ee1367fd705b0a25a02f609d755f0bfbc0a15fc/scipy-1.18.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e40baea28ae7f5475c779741e2d90b1247c78531207b49c7030e698ff81cee3f", size = 37365390 }, + { url = "https://files.pythonhosted.org/packages/d5/19/969dc072906c84dd0a3b05dcf57ea750936087d7873549e408b35cfc3f97/scipy-1.18.0-cp314-cp314t-win_arm64.whl", hash = "sha256:368e0a705903c466aa5f08eefb39e6b1b6b2d659e7352a31fd9e2438365be0f8", size = 25279661 }, +] + +[[package]] +name = "send2trash" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610 }, +] + +[[package]] +name = "sentencepiece" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/15/2e7a025fc62d764b151ae6d0f2a92f8081755ebe8d4a64099accc6f77ba6/sentencepiece-0.2.1.tar.gz", hash = "sha256:8138cec27c2f2282f4a34d9a016e3374cd40e5c6e9cb335063db66a0a3b71fad", size = 3228515 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/15/46afbab00733d81788b64be430ca1b93011bb9388527958e26cc31832de5/sentencepiece-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6356d0986b8b8dc351b943150fcd81a1c6e6e4d439772e8584c64230e58ca987", size = 1942560 }, + { url = "https://files.pythonhosted.org/packages/fa/79/7c01b8ef98a0567e9d84a4e7a910f8e7074fcbf398a5cd76f93f4b9316f9/sentencepiece-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f8ba89a3acb3dc1ae90f65ec1894b0b9596fdb98ab003ff38e058f898b39bc7", size = 1325385 }, + { url = "https://files.pythonhosted.org/packages/bb/88/2b41e07bd24f33dcf2f18ec3b74247aa4af3526bad8907b8727ea3caba03/sentencepiece-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02593eca45440ef39247cee8c47322a34bdcc1d8ae83ad28ba5a899a2cf8d79a", size = 1253319 }, + { url = "https://files.pythonhosted.org/packages/a0/54/38a1af0c6210a3c6f95aa46d23d6640636d020fba7135cd0d9a84ada05a7/sentencepiece-0.2.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a0d15781a171d188b661ae4bde1d998c303f6bd8621498c50c671bd45a4798e", size = 1316162 }, + { url = "https://files.pythonhosted.org/packages/ef/66/fb191403ade791ad2c3c1e72fe8413e63781b08cfa3aa4c9dfc536d6e795/sentencepiece-0.2.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f5a3e0d9f445ed9d66c0fec47d4b23d12cfc858b407a03c194c1b26c2ac2a63", size = 1387785 }, + { url = "https://files.pythonhosted.org/packages/a9/2d/3bd9b08e70067b2124518b308db6a84a4f8901cc8a4317e2e4288cdd9b4d/sentencepiece-0.2.1-cp311-cp311-win32.whl", hash = "sha256:6d297a1748d429ba8534eebe5535448d78b8acc32d00a29b49acf28102eeb094", size = 999555 }, + { url = "https://files.pythonhosted.org/packages/32/b8/f709977f5fda195ae1ea24f24e7c581163b6f142b1005bc3d0bbfe4d7082/sentencepiece-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:82d9ead6591015f009cb1be1cb1c015d5e6f04046dbb8c9588b931e869a29728", size = 1054617 }, + { url = "https://files.pythonhosted.org/packages/7a/40/a1fc23be23067da0f703709797b464e8a30a1c78cc8a687120cd58d4d509/sentencepiece-0.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:39f8651bd10974eafb9834ce30d9bcf5b73e1fc798a7f7d2528f9820ca86e119", size = 1033877 }, + { url = "https://files.pythonhosted.org/packages/4a/be/32ce495aa1d0e0c323dcb1ba87096037358edee539cac5baf8755a6bd396/sentencepiece-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57cae326c8727de58c85977b175af132a7138d84c764635d7e71bbee7e774133", size = 1943152 }, + { url = "https://files.pythonhosted.org/packages/88/7e/ff23008899a58678e98c6ff592bf4d368eee5a71af96d0df6b38a039dd4f/sentencepiece-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:56dd39a3c4d6493db3cdca7e8cc68c6b633f0d4195495cbadfcf5af8a22d05a6", size = 1325651 }, + { url = "https://files.pythonhosted.org/packages/19/84/42eb3ce4796777a1b5d3699dfd4dca85113e68b637f194a6c8d786f16a04/sentencepiece-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9381351182ff9888cc80e41c632e7e274b106f450de33d67a9e8f6043da6f76", size = 1253645 }, + { url = "https://files.pythonhosted.org/packages/89/fa/d3d5ebcba3cb9e6d3775a096251860c41a6bc53a1b9461151df83fe93255/sentencepiece-0.2.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99f955df238021bf11f0fc37cdb54fd5e5b5f7fd30ecc3d93fb48b6815437167", size = 1316273 }, + { url = "https://files.pythonhosted.org/packages/04/88/14f2f4a2b922d8b39be45bf63d79e6cd3a9b2f248b2fcb98a69b12af12f5/sentencepiece-0.2.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cdfecef430d985f1c2bcbfff3defd1d95dae876fbd0173376012d2d7d24044b", size = 1387881 }, + { url = "https://files.pythonhosted.org/packages/fd/b8/903e5ccb77b4ef140605d5d71b4f9e0ad95d456d6184688073ed11712809/sentencepiece-0.2.1-cp312-cp312-win32.whl", hash = "sha256:a483fd29a34c3e34c39ac5556b0a90942bec253d260235729e50976f5dba1068", size = 999540 }, + { url = "https://files.pythonhosted.org/packages/2d/81/92df5673c067148c2545b1bfe49adfd775bcc3a169a047f5a0e6575ddaca/sentencepiece-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4cdc7c36234fda305e85c32949c5211faaf8dd886096c7cea289ddc12a2d02de", size = 1054671 }, + { url = "https://files.pythonhosted.org/packages/fe/02/c5e3bc518655d714622bec87d83db9cdba1cd0619a4a04e2109751c4f47f/sentencepiece-0.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:daeb5e9e9fcad012324807856113708614d534f596d5008638eb9b40112cd9e4", size = 1033923 }, + { url = "https://files.pythonhosted.org/packages/ba/4a/85fbe1706d4d04a7e826b53f327c4b80f849cf1c7b7c5e31a20a97d8f28b/sentencepiece-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dcd8161eee7b41aae57ded06272905dbd680a0a04b91edd0f64790c796b2f706", size = 1943150 }, + { url = "https://files.pythonhosted.org/packages/c2/83/4cfb393e287509fc2155480b9d184706ef8d9fa8cbf5505d02a5792bf220/sentencepiece-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c6c8f42949f419ff8c7e9960dbadcfbc982d7b5efc2f6748210d3dd53a7de062", size = 1325651 }, + { url = "https://files.pythonhosted.org/packages/8d/de/5a007fb53b1ab0aafc69d11a5a3dd72a289d5a3e78dcf2c3a3d9b14ffe93/sentencepiece-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:097f3394e99456e9e4efba1737c3749d7e23563dd1588ce71a3d007f25475fff", size = 1253641 }, + { url = "https://files.pythonhosted.org/packages/2c/d2/f552be5928105588f4f4d66ee37dd4c61460d8097e62d0e2e0eec41bc61d/sentencepiece-0.2.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7b670879c370d350557edabadbad1f6561a9e6968126e6debca4029e5547820", size = 1316271 }, + { url = "https://files.pythonhosted.org/packages/96/df/0cfe748ace5485be740fed9476dee7877f109da32ed0d280312c94ec259f/sentencepiece-0.2.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7f0fd2f2693309e6628aeeb2e2faf6edd221134dfccac3308ca0de01f8dab47", size = 1387882 }, + { url = "https://files.pythonhosted.org/packages/ac/dd/f7774d42a881ced8e1739f393ab1e82ece39fc9abd4779e28050c2e975b5/sentencepiece-0.2.1-cp313-cp313-win32.whl", hash = "sha256:92b3816aa2339355fda2c8c4e021a5de92180b00aaccaf5e2808972e77a4b22f", size = 999541 }, + { url = "https://files.pythonhosted.org/packages/dd/e9/932b9eae6fd7019548321eee1ab8d5e3b3d1294df9d9a0c9ac517c7b636d/sentencepiece-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:10ed3dab2044c47f7a2e7b4969b0c430420cdd45735d78c8f853191fa0e3148b", size = 1054669 }, + { url = "https://files.pythonhosted.org/packages/c9/3a/76488a00ea7d6931689cda28726a1447d66bf1a4837943489314593d5596/sentencepiece-0.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac650534e2251083c5f75dde4ff28896ce7c8904133dc8fef42780f4d5588fcd", size = 1033922 }, + { url = "https://files.pythonhosted.org/packages/4a/b6/08fe2ce819e02ccb0296f4843e3f195764ce9829cbda61b7513f29b95718/sentencepiece-0.2.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:8dd4b477a7b069648d19363aad0cab9bad2f4e83b2d179be668efa672500dc94", size = 1946052 }, + { url = "https://files.pythonhosted.org/packages/ab/d9/1ea0e740591ff4c6fc2b6eb1d7510d02f3fb885093f19b2f3abd1363b402/sentencepiece-0.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c0f672da370cc490e4c59d89e12289778310a0e71d176c541e4834759e1ae07", size = 1327408 }, + { url = "https://files.pythonhosted.org/packages/99/7e/1fb26e8a21613f6200e1ab88824d5d203714162cf2883248b517deb500b7/sentencepiece-0.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad8493bea8432dae8d6830365352350f3b4144415a1d09c4c8cb8d30cf3b6c3c", size = 1254857 }, + { url = "https://files.pythonhosted.org/packages/bc/85/c72fd1f3c7a6010544d6ae07f8ddb38b5e2a7e33bd4318f87266c0bbafbf/sentencepiece-0.2.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b81a24733726e3678d2db63619acc5a8dccd074f7aa7a54ecd5ca33ca6d2d596", size = 1315722 }, + { url = "https://files.pythonhosted.org/packages/4a/e8/661e5bd82a8aa641fd6c1020bd0e890ef73230a2b7215ddf9c8cd8e941c2/sentencepiece-0.2.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0a81799d0a68d618e89063fb423c3001a034c893069135ffe51fee439ae474d6", size = 1387452 }, + { url = "https://files.pythonhosted.org/packages/99/5e/ae66c361023a470afcbc1fbb8da722c72ea678a2fcd9a18f1a12598c7501/sentencepiece-0.2.1-cp313-cp313t-win32.whl", hash = "sha256:89a3ea015517c42c0341d0d962f3e6aaf2cf10d71b1932d475c44ba48d00aa2b", size = 1002501 }, + { url = "https://files.pythonhosted.org/packages/c1/03/d332828c4ff764e16c1b56c2c8f9a33488bbe796b53fb6b9c4205ddbf167/sentencepiece-0.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:33f068c9382dc2e7c228eedfd8163b52baa86bb92f50d0488bf2b7da7032e484", size = 1057555 }, + { url = "https://files.pythonhosted.org/packages/88/14/5aee0bf0864df9bd82bd59e7711362908e4935e3f9cdc1f57246b5d5c9b9/sentencepiece-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:b3616ad246f360e52c85781e47682d31abfb6554c779e42b65333d4b5f44ecc0", size = 1036042 }, + { url = "https://files.pythonhosted.org/packages/24/9c/89eb8b2052f720a612478baf11c8227dcf1dc28cd4ea4c0c19506b5af2a2/sentencepiece-0.2.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5d0350b686c320068702116276cfb26c066dc7e65cfef173980b11bb4d606719", size = 1943147 }, + { url = "https://files.pythonhosted.org/packages/82/0b/a1432bc87f97c2ace36386ca23e8bd3b91fb40581b5e6148d24b24186419/sentencepiece-0.2.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c7f54a31cde6fa5cb030370566f68152a742f433f8d2be458463d06c208aef33", size = 1325624 }, + { url = "https://files.pythonhosted.org/packages/ea/99/bbe054ebb5a5039457c590e0a4156ed073fb0fe9ce4f7523404dd5b37463/sentencepiece-0.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c83b85ab2d6576607f31df77ff86f28182be4a8de6d175d2c33ca609925f5da1", size = 1253670 }, + { url = "https://files.pythonhosted.org/packages/19/ad/d5c7075f701bd97971d7c2ac2904f227566f51ef0838dfbdfdccb58cd212/sentencepiece-0.2.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1855f57db07b51fb51ed6c9c452f570624d2b169b36f0f79ef71a6e6c618cd8b", size = 1316247 }, + { url = "https://files.pythonhosted.org/packages/fb/03/35fbe5f3d9a7435eebd0b473e09584bd3cc354ce118b960445b060d33781/sentencepiece-0.2.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01e6912125cb45d3792f530a4d38f8e21bf884d6b4d4ade1b2de5cf7a8d2a52b", size = 1387894 }, + { url = "https://files.pythonhosted.org/packages/dc/aa/956ef729aafb6c8f9c443104c9636489093bb5c61d6b90fc27aa1a865574/sentencepiece-0.2.1-cp314-cp314-win32.whl", hash = "sha256:c415c9de1447e0a74ae3fdb2e52f967cb544113a3a5ce3a194df185cbc1f962f", size = 1096698 }, + { url = "https://files.pythonhosted.org/packages/b8/cb/fe400d8836952cc535c81a0ce47dc6875160e5fedb71d2d9ff0e9894c2a6/sentencepiece-0.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:881b2e44b14fc19feade3cbed314be37de639fc415375cefaa5bc81a4be137fd", size = 1155115 }, + { url = "https://files.pythonhosted.org/packages/32/89/047921cf70f36c7b6b6390876b2399b3633ab73b8d0cb857e5a964238941/sentencepiece-0.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:2005242a16d2dc3ac5fe18aa7667549134d37854823df4c4db244752453b78a8", size = 1133890 }, + { url = "https://files.pythonhosted.org/packages/a1/11/5b414b9fae6255b5fb1e22e2ed3dc3a72d3a694e5703910e640ac78346bb/sentencepiece-0.2.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a19adcec27c524cb7069a1c741060add95f942d1cbf7ad0d104dffa0a7d28a2b", size = 1946081 }, + { url = "https://files.pythonhosted.org/packages/77/eb/7a5682bb25824db8545f8e5662e7f3e32d72a508fdce086029d89695106b/sentencepiece-0.2.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e37e4b4c4a11662b5db521def4e44d4d30ae69a1743241412a93ae40fdcab4bb", size = 1327406 }, + { url = "https://files.pythonhosted.org/packages/03/b0/811dae8fb9f2784e138785d481469788f2e0d0c109c5737372454415f55f/sentencepiece-0.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:477c81505db072b3ab627e7eab972ea1025331bd3a92bacbf798df2b75ea86ec", size = 1254846 }, + { url = "https://files.pythonhosted.org/packages/ef/23/195b2e7ec85ebb6a547969f60b723c7aca5a75800ece6cc3f41da872d14e/sentencepiece-0.2.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:010f025a544ef770bb395091d57cb94deb9652d8972e0d09f71d85d5a0816c8c", size = 1315721 }, + { url = "https://files.pythonhosted.org/packages/7e/aa/553dbe4178b5f23eb28e59393dddd64186178b56b81d9b8d5c3ff1c28395/sentencepiece-0.2.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:733e59ff1794d26db706cd41fc2d7ca5f6c64a820709cb801dc0ea31780d64ab", size = 1387458 }, + { url = "https://files.pythonhosted.org/packages/66/7c/08ff0012507297a4dd74a5420fdc0eb9e3e80f4e88cab1538d7f28db303d/sentencepiece-0.2.1-cp314-cp314t-win32.whl", hash = "sha256:d3233770f78e637dc8b1fda2cd7c3b99ec77e7505041934188a4e7fe751de3b0", size = 1099765 }, + { url = "https://files.pythonhosted.org/packages/91/d5/2a69e1ce15881beb9ddfc7e3f998322f5cedcd5e4d244cb74dade9441663/sentencepiece-0.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e4366c97b68218fd30ea72d70c525e6e78a6c0a88650f57ac4c43c63b234a9d", size = 1157807 }, + { url = "https://files.pythonhosted.org/packages/f3/16/54f611fcfc2d1c46cbe3ec4169780b2cfa7cf63708ef2b71611136db7513/sentencepiece-0.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:105e36e75cbac1292642045458e8da677b2342dcd33df503e640f0b457cb6751", size = 1136264 }, +] + +[[package]] +name = "seqeval" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "scikit-learn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/2d/233c79d5b4e5ab1dbf111242299153f3caddddbb691219f363ad55ce783d/seqeval-1.2.2.tar.gz", hash = "sha256:f28e97c3ab96d6fcd32b648f6438ff2e09cfba87f05939da9b3970713ec56e6f", size = 43605 } + +[[package]] +name = "setuptools" +version = "81.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "soupsieve" +version = "2.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, +] + +[[package]] +name = "tabulate" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814 }, +] + +[[package]] +name = "terminado" +version = "0.18.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "os_name != 'nt'" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, +] + +[[package]] +name = "tinycss2" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/ae/2ca4913e5c0f09781d75482874c3a95db9105462a92ddd303c7d285d3df2/tinycss2-1.5.1.tar.gz", hash = "sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957", size = 88195 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/45/c7b5c3168458db837e8ceab06dc77824e18202679d0463f0e8f002143a97/tinycss2-1.5.1-py3-none-any.whl", hash = "sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661", size = 28404 }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275 }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472 }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736 }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835 }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673 }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818 }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195 }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982 }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245 }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069 }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263 }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429 }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363 }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786 }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133 }, +] + +[[package]] +name = "torch" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/62/131124fb95df03811b8260d1d43dcc5ee85ea1a344b964613d7efe77fb08/torch-2.12.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:10802fd383bbfed646212e765a72c37d2185205d4f26eb197a254e8ac7ddcb25", size = 87990344 }, + { url = "https://files.pythonhosted.org/packages/12/9c/dda0dbd547dc549839824135f223792fd0e725f28ed0715dda366b7acaa2/torch-2.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c12592630aef72feaf18bd3f197ef587bbfa21131b31c38b23ab2e55fce92e36", size = 426362932 }, + { url = "https://files.pythonhosted.org/packages/e2/d2/a7dd5a3f9bdaa7842124e8e2359202b317c48d47d2fc5816fafdf2049adb/torch-2.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:415c1b8d0412f67551c8e89a2daca0fb3e56694af0281ba155eaa9da481f58b4", size = 532170085 }, + { url = "https://files.pythonhosted.org/packages/12/1b/a61ce2004f9ab0ea8964a6e6168133a127795667639e2ff4f8f2bdb16a65/torch-2.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd37188ea325042cb1f6cafa56822b11ada2520c04791a52629b0af25bdfbfd9", size = 122953128 }, + { url = "https://files.pythonhosted.org/packages/ef/bb/285d643f254731294c9b595a007eac39db4600a98682d7bca688f42ca164/torch-2.12.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b41339df93d491435e790ff8bcbae1c0ce777175889bfd1281d119862793e6a2", size = 88010197 }, + { url = "https://files.pythonhosted.org/packages/79/81/76debf1db1343bd929bbb5d74c89fb437c2ed88eb144712557e7bd3eea45/torch-2.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8fbef9f108a863e7722a73740998967e3b074742a834fc5be3a535a2befa7057", size = 426376751 }, + { url = "https://files.pythonhosted.org/packages/de/f0/80026028b603c4650ff270fc3785bdef4bd6738765a9cc5a0f5a637d65a2/torch-2.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4b4f64c2c2b11f7510d93dd6412b87025ff6eddd6bb61c3b5a3d892ea20c4756", size = 532261691 }, + { url = "https://files.pythonhosted.org/packages/b9/c2/64b06cbb7830fb3cd9be13e1158b31a3f36b68e6a209105ee3c9d9480be0/torch-2.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:8b958caff4a14d3a3b0b2dfc6a378f64dda9728a9dad28c08a0db9ce4dafb549", size = 122988114 }, + { url = "https://files.pythonhosted.org/packages/86/ca/01896c80ba921676aa45886b2c5b8d774912de2a1f719de48169c6f755cd/torch-2.12.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:90dd587a5f61bfe1307148b581e2084fc5bc4a06e2b90a20e9a36b81087ff16b", size = 88009511 }, + { url = "https://files.pythonhosted.org/packages/a5/04/52bdaf4787eab6ac7d7f5851dff934e4def0bc8ead9c8fd2b69b3e529699/torch-2.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:864392c73b7654f4d2b3ae712f607937d0dbb1101c4555fbb41848106b297f39", size = 426383231 }, + { url = "https://files.pythonhosted.org/packages/49/8a/94bdecd13f5aaa90d45920b89789d9fe7c6f4af8c3cdd7ce01fcb59908fc/torch-2.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d6b560dfa7d56291c07d615c3bb73e8d9943d9b6d87f76cd0d9d570c4797fa6", size = 532269288 }, + { url = "https://files.pythonhosted.org/packages/3e/2f/bdbaaa267de519ef1b73054bf590d8c93c37a266c9a4e24a01bd38b6918f/torch-2.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:3fee918902090ade827643e758e98363278815de583c75d111fdd665ebffde9f", size = 122987706 }, + { url = "https://files.pythonhosted.org/packages/9b/ad/e95e822f3538171e22640a7fbe839a1fdb666600bf6487025de2ff03b11a/torch-2.12.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:10ee1448a9f304d3b987eb4656f664ba6e4d7b410ca7a5a7c642199777a2cf88", size = 88319556 }, + { url = "https://files.pythonhosted.org/packages/b7/07/055d06d985b445d67422d25b033c11cf55bbb81785d4c4e68e28bca5820e/torch-2.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af68dbf403439cae9ceaeaaf92f8352b460787dcd27b92aa05c40dd4a19c0f1e", size = 426397656 }, + { url = "https://files.pythonhosted.org/packages/43/94/b0b4fdc3014122e0a7302fb90086d352aa48f2576f0b252561ebb38c01a8/torch-2.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a6a2eebb237d3b1d9ad3b378e86d9b9e0782afdea8b1e0eba6a13646b9b49c07", size = 532183124 }, + { url = "https://files.pythonhosted.org/packages/d8/c8/052405e6ad05d3237bfe5a4df78f917773956f8e17813a2d44c059068b74/torch-2.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2140e373e9a51a3e22ef62e8d14366d0b470d18f0adf19fdc757368077133a34", size = 123232462 }, + { url = "https://files.pythonhosted.org/packages/67/dc/ac069f8d6e8be701535921141055293b0d4819d3d7f224a4612cf157c7f9/torch-2.12.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7dfae4a519197dfa050e98d8e36378a0fb5899625a875c2b54445005a2e404e", size = 88027282 }, + { url = "https://files.pythonhosted.org/packages/33/c3/1c1eb00e34555b536dddf792676026a988d710ed36981aa00499b36b0620/torch-2.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:891c769072637c74e9a5a77a3bc782894696d8ffec83b938df8536dee7f0ba78", size = 426386961 }, + { url = "https://files.pythonhosted.org/packages/cd/d4/7e730dba0c7032a4154dc9056b76cf9625515e030e269cfbf8098fcfee7d/torch-2.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e2ad3eb85d39c3cab62dfa93ed5a73516e6a53c6713cb97d004004fe089f0f1f", size = 532272265 }, + { url = "https://files.pythonhosted.org/packages/f1/b4/92c80d1bbfee1c0036c06d1d2155a3065bd2423134c83bf8a47e65cd6b9b/torch-2.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:c66696857e987efb8bc1777a37357ec4f60ab5e8af6250b83d6034437fa2d8f3", size = 122987138 }, + { url = "https://files.pythonhosted.org/packages/7b/78/2e12b37ce50a19a037d7bc62d652a5a8f27385a7b05859d6bc9204f20cfe/torch-2.12.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b4556715c8572758625d62b6e0ae3b1f76c440221913a6fb5e100f321fb4fb02", size = 88320100 }, + { url = "https://files.pythonhosted.org/packages/56/5e/83c450ec7b0bb40a7b74611c1b5440f9260e33c54c90d556fd4a1f0fd955/torch-2.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a43ac605a5e13116c72b64c359644cce0229f213dde48d2ae0ae5eb5becf7feb", size = 426391871 }, + { url = "https://files.pythonhosted.org/packages/c9/e9/1a0b575d98d0afedd8f157d23fa3d2759421483660448e60d0a4b10b6daa/torch-2.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6a7512adfdd7f6732e40de1c620831e3c75b39b98cef60b11d0c5f0a76473ec5", size = 532192241 }, + { url = "https://files.pythonhosted.org/packages/88/21/afadd25ecd81b3cea1e11c73cf1ab41a983a50271548c3ec7ec3b9efc3e9/torch-2.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f96b63f8287f66a005dd1b5a6abba2920f11156c5e5c4d815f3e2050fd1aa16", size = 123231092 }, +] + +[[package]] +name = "tornado" +version = "6.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/24/95ec527ad67b76d59299e5465b3935d05e4294b7e0290a3924b7487df30b/tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2", size = 519252 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/dc/c7043cab6fed8ae159fc1923ce829ada35c4dbd797d408a43858ffaf9639/tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163", size = 448543 }, + { url = "https://files.pythonhosted.org/packages/92/4f/090b1431e5a43df696feceffc268c5383cc079ecb5f08ce58f917109aafe/tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100", size = 446707 }, + { url = "https://files.pythonhosted.org/packages/37/d8/ef374952fd5da67d4463122c2b8e5a96536ec10b4b339254c6dcde81d01c/tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972", size = 449774 }, + { url = "https://files.pythonhosted.org/packages/35/37/d434c73f4c6e014b745b9b37085f34f40c022f007efff3d7fe65991899f3/tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b", size = 450745 }, + { url = "https://files.pythonhosted.org/packages/b6/2b/56b9aff361d7f1ab728a805ec7d7ea835f8807afa9f5cc690ea0e630efb9/tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92", size = 450578 }, + { url = "https://files.pythonhosted.org/packages/02/30/a7444fb23aa76860a14198fab96ac79f1866b0a6e19e26c4381b0938e50f/tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5", size = 449985 }, + { url = "https://files.pythonhosted.org/packages/5c/42/5f0e56c01e8d9d36f4e23f367b85ae6cae0c1ecddd5e6977d8388ad27488/tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4", size = 451047 }, + { url = "https://files.pythonhosted.org/packages/c9/a4/b393076ffb21b469eec5b328a0534cf03a3b90bfc6b1f09507cdd075d938/tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4", size = 451485 }, + { url = "https://files.pythonhosted.org/packages/71/2e/7b1c769803121b809112cf9a00681c472eae1d80e32d7ec0e0bd61d0d0e1/tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796", size = 450506 }, +] + +[[package]] +name = "tqdm" +version = "4.68.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/05/0d5260f1f1ca784f4a4a0def9cbe6affe587f5b4025328d446c3d67765f4/tqdm-4.68.2.tar.gz", hash = "sha256:89c230e8dbc67c7615c142487111222f878c77427ea09549960f62389e258add", size = 171923 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/75/1a0392bcc21c44dcdf87b3cf2d137e7829be2c083a1e38d44efca3d57a16/tqdm-4.68.2-py3-none-any.whl", hash = "sha256:d4240441fb5353290b87d6a85968c9decc131a99b8c7faa28269d829de669ede", size = 78578 }, +] + +[[package]] +name = "traitlets" +version = "5.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/a9/a2584b8313b89f94869ddb3c4074617a691de1812a614d2d50e32ca5a7a6/traitlets-5.15.1.tar.gz", hash = "sha256:7b1c07854fe25acb39e009bae49f11b79ff6cbb2f27999104e9110e7a6b53722", size = 163344 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/8d/1080ee4c231f361b6ce4470d556c8c435b67c7e0753aaa641497ee92f88b/traitlets-5.15.1-py3-none-any.whl", hash = "sha256:770a53705f84b81ac107e83a1b3328ff2dae16094d8fc3cfc004e4b22dfd8e92", size = 85858 }, +] + +[[package]] +name = "transformers" +version = "5.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/f9/4552e2ba55db1c943aea0d4c09a32e9cbe5445b9eabe9856900de503dc8f/transformers-5.12.0.tar.gz", hash = "sha256:f0cf42ae1464c2eb41e7e0e66d7fd4b66145f48af17093b4cc0b2e9781faa7f4", size = 8923020 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/1f/d385913c38e900d23b728a4188fee625f2fccb306aeebc59be9d91404a5a/transformers-5.12.0-py3-none-any.whl", hash = "sha256:500be9eb644ede81c3103eee7687fc36d05dd75d1c76686c3820b26396fe7c7c", size = 11150246 }, +] + +[package.optional-dependencies] +sentencepiece = [ + { name = "protobuf" }, + { name = "sentencepiece" }, +] +torch = [ + { name = "accelerate" }, + { name = "torch" }, +] + +[[package]] +name = "triton" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/c1/5d842314bb6c78442cc60437928781701c6050b8d479bc2a1aed691d37ca/triton-3.7.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9e71fc392675fac364e0ecf4ef3f76f85b7f5433a16f4c3c5fe5f05a52c85fe", size = 188480277 }, + { url = "https://files.pythonhosted.org/packages/13/31/8315ea5f8dd18e60970b3022e3a8b93fd37e0b784fbbef86e10c8e6e5ca1/triton-3.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22bacffce443f54593dd20f05294d5a40622e0ea9ab632816f87154504356221", size = 201415942 }, + { url = "https://files.pythonhosted.org/packages/f7/13/ec05adfcd87311d532ba61e3af143e8be59fcd26675884c4682841406a20/triton-3.7.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4bf49b00a7a377a68a6da603a876e797614e6455a80e9021669c476a953ad9a", size = 188505104 }, + { url = "https://files.pythonhosted.org/packages/62/7b/468a576e35beef1426e0828e28e9ba9e65f5474d496f16ee126c15646324/triton-3.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f111161d49bf903c0eaedde3962353a3d841c08a836839b7cc1025b8426efcf", size = 201457567 }, + { url = "https://files.pythonhosted.org/packages/01/e1/a59a583de59b8f62c495d67c80ee3ea97d09e91ac80c4c6e76456ed8d8ac/triton-3.7.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abdf6beaa89b1bcfb9a43cd990536ce66091a997841a4814b260b7bee4c88c3c", size = 188503209 }, + { url = "https://files.pythonhosted.org/packages/30/b1/b7507bb9815d403927c8dd51d4158ed2e11751a92dbc118a044f247b6848/triton-3.7.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a35d7afe3f3f058e7ec49fcce09794049e0ffc5c59019ac25ec3413741b8c4e7", size = 201453566 }, + { url = "https://files.pythonhosted.org/packages/a6/8f/0bea7a6a0c989315c9135a1d7fb37e41905cfb3a17cbc1f10044ebd4cc3a/triton-3.7.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc1d61c172d257db80ddf42595131fb196ad2e9bdd751e90fe2ef13531734e8b", size = 188612899 }, + { url = "https://files.pythonhosted.org/packages/e1/02/d96f57828d0912aec733b9bc7e0e7dbfd2c6f079a8fa433ac25cb93d1a30/triton-3.7.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70fb9bbdc9f400afc54bbf6eb2670af28829a6ae3996863317964783141daf56", size = 201553816 }, + { url = "https://files.pythonhosted.org/packages/40/fb/82a802dac4689f2a2fb2e69302e6a138eecc3e175bbe976ba3cfc717683a/triton-3.7.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a44a8476d0d3571eac4e4d1048e1ff75aad81a09ff4602ccfc56c6dea1672e", size = 188507879 }, + { url = "https://files.pythonhosted.org/packages/8f/af/9904ec6d3c93d9b24e5ec360445bbdf758b7f00bfbeedb89cb0eb64eb8bb/triton-3.7.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b85e72968a9d8bba5ddb24e9b64aaabaf48affb042f2755cb7cfa92b7531ce", size = 201460637 }, + { url = "https://files.pythonhosted.org/packages/a1/f9/4835a8ea746b88727d8899f4e3ccce4f9cacb38abfc3bb0a638266c53111/triton-3.7.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18a160de426fd99f92b0baf509045360afbd3bfaa0b4a5171dde800ec9f09684", size = 188608706 }, + { url = "https://files.pythonhosted.org/packages/c1/68/fa86e5a39608000f645535b2c124920126327ab731f8c4fafd5b07ff8d4b/triton-3.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce061073102714b725f3660ec6939d94a1da7984b3aa99c921417cae273672f5", size = 201546766 }, +] + +[[package]] +name = "typer" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321 }, +] + +[[package]] +name = "uri-template" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087 }, +] + +[[package]] +name = "wcwidth" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/b4/51fe890511f0f242d07cb1ebe6a5b6db417262b9d2568b460347c57d95cc/wcwidth-0.8.1.tar.gz", hash = "sha256:faf5b4a5366a72dc49cad48cdf21f52bdf63bdda995178e483ba247ff79089b9", size = 1466072 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/6e/95b0e537de1f4d4301f76f944642c6da50d1511cc7b3d64dc418a66c7509/wcwidth-0.8.1-py3-none-any.whl", hash = "sha256:f453740b1e4a4f3291faa37944c555d71056c4da08d59809b307ef4feba695c8", size = 323092 }, +] + +[[package]] +name = "webcolors" +version = "25.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, +] + +[[package]] +name = "websocket-client" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, +] + +[[package]] +name = "widgetsnbextension" +version = "4.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, +] + +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/f4/7bd35089ff1f8e2c96baa2dce05775a122aacd2e3830a73165e27a4d0848/xxhash-3.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fdc7d06929ae28dda98297a18eef7b0fd38991a3b405d8d7b55c9ef24c296958", size = 33423 }, + { url = "https://files.pythonhosted.org/packages/a3/26/4e00c88a6a2c8a759cfb77d2a9a405f901e8aa66e60ef1fd0aeb35edda48/xxhash-3.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea6daa712f4e094a30830cf01e9b47d03b24d05cc9dab8609f0d9a9db8454712", size = 30857 }, + { url = "https://files.pythonhosted.org/packages/82/2f/eeb942c17a5a761a8f01cb9180a0b76bfb62a2c39e6f46b1f9001899027a/xxhash-3.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9e6c0d843f1daf85ea23aeb053579135552bde575b7b98af20bfc667b6e4548d", size = 194702 }, + { url = "https://files.pythonhosted.org/packages/0e/fd/96f132c08b1e5951c68691d3b9ec351ec2edc028f6a01fcd294f46b9d9f0/xxhash-3.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:363c139bf15e1ac5f136b981d3c077eb551299b1effede7f12faa010b8590a60", size = 213613 }, + { url = "https://files.pythonhosted.org/packages/82/89/d4e92b796c5ed052d29ed324dbfc1dc1188e0c4bf64bebbf0f8fc20698df/xxhash-3.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a778b25874cb0f862eaab5986bff4ca49ffb0def7c0a34c237b948b3c6c775b2", size = 236726 }, + { url = "https://files.pythonhosted.org/packages/40/f1/81fc4361921dc6e557a9c60cb3712f36d244d06eeeb71cd2f4252ac42678/xxhash-3.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e1860f1e43d40e9d904cf22d93e587ea42e010ebce4160877e46bcab4bc232a", size = 212443 }, + { url = "https://files.pythonhosted.org/packages/6a/d0/afeddd4cff50a332f50d4b8a2e8857673153ab0564ef472fcdeb0b5430df/xxhash-3.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9122ad6f867c4a0f5e655f5c3bdf89103852009dbb442a3d23e688b9e699e800", size = 445793 }, + { url = "https://files.pythonhosted.org/packages/f7/d0/3c91e4e6a05ca4d7df8e39ec3a75b713609258ec84705ab34be6430826a1/xxhash-3.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d9110d0c3fb02679972837a033251fd186c529aa62f19c132fc909c74052b8", size = 193937 }, + { url = "https://files.pythonhosted.org/packages/4e/3a/a6b0772d9801dd4bea4ca4fd34734d6e9b51a711c8a611a24a79de26a878/xxhash-3.7.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:347a93f2b4ce67ce61959665e32a7447c380f8347e55e100daa23766baacf0e5", size = 285188 }, + { url = "https://files.pythonhosted.org/packages/6c/f8/cf8e31fd7282230fe7367cd501a2e75b4b67b222bfc7eacccfc20d2652cb/xxhash-3.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:acbb48679ddf3852c45280c10ff10d52ca2cd1da2e552fb81db1ff786c75d0e4", size = 210966 }, + { url = "https://files.pythonhosted.org/packages/cc/f0/fd36cc4a81bf52ee5633275daae2b93dd958aace67fd4f5d466ec83b5f35/xxhash-3.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fe14c356f8b23ad811dc026077a6d4abccdaa7bce5ca98579605550657b6fcfb", size = 241994 }, + { url = "https://files.pythonhosted.org/packages/08/e1/67f5d9c9369be42eaf99ba02c01bf14c5ecd67087b02567960bfcee43b63/xxhash-3.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f420ad3d41e38194353a498bbc9561fd5a9973a27b536ce46d8583479cf44335", size = 198707 }, + { url = "https://files.pythonhosted.org/packages/50/17/a4c865ca22d2da6b1bc7d739bf88cab209533cf52ba06ca9da27c3039bee/xxhash-3.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:693d02c6dc7d1aa0a45921d54cd8c1ff629e09dfdc2238471507af1f7a1c6f04", size = 210917 }, + { url = "https://files.pythonhosted.org/packages/49/8b/453b35810d697abac3c96bde3528bece685869227da274eb80a4a4d4a119/xxhash-3.7.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:14bf7a54e43825ec131ee7fe3c60e142e7c2c1e676ad0f93fc893432d15414af", size = 275772 }, + { url = "https://files.pythonhosted.org/packages/b5/ad/4eed7eab07fd3ee6678f416190f0413d097ab5d7c1278906bf1e9549d789/xxhash-3.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ae3a39a4d96bdb6f8d154fd7f490c4ad06f0532fcd2bb656052a9a7762cf5d31", size = 414068 }, + { url = "https://files.pythonhosted.org/packages/d3/4e/fd6f8a680ba248fdb83054fa71a8bfa3891225200de1708b888ef2c49829/xxhash-3.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1cc07c639e3a77ef1d32987464d3e408565b8a3be57b545d3542b191054d9923", size = 191459 }, + { url = "https://files.pythonhosted.org/packages/50/7c/8cb34b3bed4f44ca6827a534d50833f9bc6c006e83b0eb410ac9fa0793bd/xxhash-3.7.0-cp311-cp311-win32.whl", hash = "sha256:3281ba1d1e60ee7a382a7b958513ba03c2c0d5fcbd9a6f7517c0a81251a23422", size = 30628 }, + { url = "https://files.pythonhosted.org/packages/0b/47/a49767bd7b40782bedae9ff0721bfe1d7e4dd9dc1585dea684e57ba67c20/xxhash-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7f25baec4c5d851d40718d6fae52285b31683093d4ff5207e63ab306ccf14a5", size = 31461 }, + { url = "https://files.pythonhosted.org/packages/7c/c6/3957bfacfb706bd687be246dfa8dd60f8df97c44186d229f7fd6e26c4b7e/xxhash-3.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:4c2454448ce847c72635827bb75c15c5a3434b03ee1afd28cb6dc6fb2597d830", size = 27746 }, + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357 }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869 }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100 }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977 }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373 }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229 }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462 }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932 }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807 }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445 }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273 }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950 }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709 }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345 }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056 }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485 }, + { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671 }, + { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514 }, + { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761 }, + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813 }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121 }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624 }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638 }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323 }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362 }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874 }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185 }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033 }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140 }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291 }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532 }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990 }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876 }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495 }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331 }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037 }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744 }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406 }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125 }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555 }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668 }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524 }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768 }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576 }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123 }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491 }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793 }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993 }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887 }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407 }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119 }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751 }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961 }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703 }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894 }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357 }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600 }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980 }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840 }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966 }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784 }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932 }, + { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644 }, + { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003 }, + { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655 }, + { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664 }, + { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317 }, + { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457 }, + { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894 }, + { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409 }, + { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135 }, + { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379 }, + { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447 }, + { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660 }, + { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076 }, + { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990 }, + { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590 }, + { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442 }, + { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356 }, + { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898 }, + { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519 }, + { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191 }, + { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604 }, + { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271 }, + { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284 }, + { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701 }, + { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646 }, + { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125 }, + { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633 }, + { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899 }, + { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116 }, + { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012 }, + { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534 }, + { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217 }, + { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906 }, + { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057 }, + { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886 }, + { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015 }, + { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457 }, + { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738 }, + { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127 }, + { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962 }, + { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643 }, + { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522 }, + { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807 }, + { url = "https://files.pythonhosted.org/packages/54/c1/e57ac7317b1f58a92bab692da6d497e2a7ce44735b224e296347a7ecc754/xxhash-3.7.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad3aa71e12ee634f22b39a0ff439357583706e50765f17f05550f92dbf128a23", size = 31232 }, + { url = "https://files.pythonhosted.org/packages/4f/4e/075559bd712bc62e84915ea46bbee859f935d285659082c129bdbff679dd/xxhash-3.7.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5de686e73690cdaf72b96d4fa083c230ec9020bcc2627ce6316138e2cf2fe2d1", size = 28553 }, + { url = "https://files.pythonhosted.org/packages/92/ca/a9c78cb384d4b033b0c58196bd5c8509873cabe76389e195127b0302a741/xxhash-3.7.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7fbec49f5341bbdea0c471f7d1e2fb41ae8925af9b6f28025c28defd8eb94274", size = 41109 }, + { url = "https://files.pythonhosted.org/packages/bd/b1/dfe2629f7c77eb2fa234c72ff537cdd64939763df704e256446ed364a16d/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48b542c347c2089f43dc5a6db31d2a6f3cdb04ee33505ec6e9f653834dbb0bde", size = 36307 }, + { url = "https://files.pythonhosted.org/packages/e7/f7/5a484afce0f48dd8083208b42e4911f290a82c7b52458ef2927e4d421a45/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a169a036bed0995e090d1493b283cc2cc8a6f5046821086b843abefff80643bc", size = 32534 }, + { url = "https://files.pythonhosted.org/packages/0f/5f/4acfcd490db9780cf36c58534d828003c564cde5350220a1c783c4d10776/xxhash-3.7.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ec101643395d7f21405b640f728f6f627e6986557027d740f2f9b220955edafe", size = 31552 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/c5/1ce244152ff2839645e7cae92f90e7bafcb2c52bea7ff586ac714f14f5df/yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1", size = 128971 }, + { url = "https://files.pythonhosted.org/packages/87/5a/00f36967203ed89cb3acd2c8ed526cc3fed9418eb70ce128160a911c8499/yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c", size = 91507 }, + { url = "https://files.pythonhosted.org/packages/31/d0/1fb0c1cd27288f39f6974da4318c32768d72c9890984541fdf1e2e32a51d/yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d", size = 91343 }, + { url = "https://files.pythonhosted.org/packages/03/ce/d4a646508bed2f8dec6435b40166fe9308dd191262033d3f307b2bbcaecd/yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae", size = 105704 }, + { url = "https://files.pythonhosted.org/packages/4b/07/b3278e82d8bc41485bcf6d856cd0433262593de615b1d3dc43bd3f5bead4/yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a", size = 97281 }, + { url = "https://files.pythonhosted.org/packages/17/5b/4cee6e7c92e487bebe7afc797da0aa54a248ab4e776a68fe369ec29665a5/yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e", size = 114020 }, + { url = "https://files.pythonhosted.org/packages/5c/82/111076571545a7d4f9cca3fbd5c6f40615af58642be09f12328f48022468/yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50", size = 111450 }, + { url = "https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003", size = 106384 }, + { url = "https://files.pythonhosted.org/packages/e5/86/ce41e7a7a199340b2330d52b60f25c4074b6636dd0e60b1a80d31a9db042/yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f", size = 106153 }, + { url = "https://files.pythonhosted.org/packages/c4/5d/31be8a729531ab3e55ac3e7e5c800be8c89ea98947f418b2f6ea259fb6ee/yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f", size = 105322 }, + { url = "https://files.pythonhosted.org/packages/47/9b/b57afb22b386ae87ac9940f09878b98d8c333f89113e6fc96fcf4ca9eb64/yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294", size = 99057 }, + { url = "https://files.pythonhosted.org/packages/a3/4f/06348c27c8389256c313e8a57d796808fc0264c915dd5e7cfd3c0e314dc7/yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2", size = 113502 }, + { url = "https://files.pythonhosted.org/packages/5f/1c/284f307b298e4a17b7943b07d9d7ecc4151537f8d137ba51f3bb6c31ca20/yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c", size = 105253 }, + { url = "https://files.pythonhosted.org/packages/c8/bf/0de123bec8619e45c80cbded9085f61b5b4a9eddb8abe6d25d28ee1ec866/yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b", size = 111345 }, + { url = "https://files.pythonhosted.org/packages/90/af/0248eb065e51129d2a9b2436cd1b5c772c19a6b04e5b6a186955671e3319/yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5", size = 106558 }, + { url = "https://files.pythonhosted.org/packages/21/3c/f960d7a65ef97d8ba9b424fb5128796a4bc710fc6df2ddbbd7dfdc3bbd20/yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45", size = 92808 }, + { url = "https://files.pythonhosted.org/packages/03/1a/49fb03750e4de4d2284cd5b885a383133c34eef45bd59631b2bb8b7e81e8/yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122", size = 87610 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901 }, + { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667 }, + { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424 }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980 }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219 }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576 }, +] diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/01-need-aligner/02-secondhand-buyer-agent-research.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/01-need-aligner/02-secondhand-buyer-agent-research.md new file mode 100644 index 0000000..b04f80c --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/01-need-aligner/02-secondhand-buyer-agent-research.md @@ -0,0 +1,166 @@ +# 二手买家 Agent 业务调研与 Fine-tuning Suitability + +## Research Question + +本 topic 不能预设“闲鱼二手买家 Agent 的 suggestion next action”一定适合 fine-tuning。进入材料选择前,需要先回答: + +- 二手购买有哪些真实痛点? +- 传统平台机制解决不了什么? +- AI Agent 可以切入哪些环节? +- fine-tuning 相比 prompt engineering / context engineering 有什么意义? +- 这个任务最终直觉上是好方向,还是硬套训练技术? + +## Buyer Pain Points + +二手交易的底层矛盾是信息不对称、信任不足和纠纷成本高。买家常见痛点包括: + +- 商品真假、成色、功能状态难判断。 +- 卖家描述可能模糊、避重就轻或误导。 +- 价格是否合理需要比较同款、历史价、成色、配件和维修情况。 +- 买家不知道该问什么,容易漏问关键风险问题。 +- 二手 C2C 交易的退货和纠纷规则不如传统电商稳定。 +- 证据保存、沟通表达和图片凭证会影响纠纷处理结果。 + +调研线索: + +- 英国二手市场报道中,买家常遇到错误商品、空包、假货等骗局;Which? 调查中有相当比例受访买家在二手平台遭遇骗局。 +- 国内二手交易风险提示强调,闲鱼等 C2C 交易通常不适用传统电商的七天无理由退货,假货、掉包、证据保存和责任边界都更复杂。 +- 闲置交易平台纠纷研究显示,图片凭证、陈词方式、情绪表达和证据数量会影响争议判定。 + +## Traditional Solutions And Gaps + +传统方案主要是平台级机制或静态工具: + +| 传统方案 | 能解决什么 | 解决不了什么 | +| --- | --- | --- | +| 信誉系统 | 看评分、历史交易、评价 | 无法判断当前商品是否有暗病,也无法指导买家下一步问什么 | +| 验货 / 鉴定 | 降低真假和成色风险 | 成本高、覆盖品类有限,且通常发生在较晚阶段 | +| 担保交易 | 降低付款风险 | 不解决购买前的信息缺口 | +| 平台规则 / 小法庭 / 客服 | 处理事后纠纷 | 纠纷成本高,且结果依赖证据和表达 | +| 搜索筛选 / 比价 | 帮助找商品和价格参考 | 不理解当前对话阶段和用户下一步动作 | +| 聊天模板 | 给出通用问法 | 不能动态结合商品、风险、对话和买家意图 | + +因此,传统方案的问题不是完全无效,而是它们大多不直接帮助买家做动态决策。 + +## Agent Opportunity + +`suggestion next action` 的切入点不是替买家判断一切,而是在每轮回复后给出下一步行动建议: + +- 继续追问关键成色细节。 +- 要求卖家补充实拍图、视频、序列号或功能测试。 +- 查询同款近期价格和历史成交价。 +- 询问是否支持当面验货或平台担保。 +- 提醒不要绕开平台交易。 +- 对高风险商品建议暂缓或换目标。 + +这类能力适合做成买家决策辅助,而不是商品真假判定器。 + +## Suitability Judgment + +### Fine-tuning 可以解决什么 + +Fine-tuning / LoRA 可以学习稳定的行为偏好: + +- 不同购买阶段该建议什么动作。 +- 哪些风险信号应该触发追问或提醒。 +- next actions 的粒度、格式、语气和数量。 +- 如何避免空泛建议,输出可执行动作。 +- 小模型在特定任务上是否能接近大模型 prompt baseline。 + +如果有足够高质量样本,fine-tuning 可以把大量重复出现的策略偏好压进 adapter,让输出更稳定、更短、更符合产品语气。 + +### Fine-tuning 不适合解决什么 + +Fine-tuning 不应该承担实时事实和外部知识: + +- 当前商品真实价格是否划算。 +- 卖家是否可信。 +- 商品是否真货。 +- 平台规则是否刚更新。 +- 当前同款行情和库存情况。 + +这些应由 context engineering、retrieval、tools、平台数据和风控系统提供。 + +## Prompt / Context / Fine-tuning Boundary + +合理方案不是三选一,而是分层: + +```text +tools / retrieval + -> 提供实时商品、价格、规则、风险知识 + +context engineering + -> 组织商品卡片、卖家信息、对话历史、用户预算、风险标签 + +prompt engineering + -> 定义 action taxonomy、输出格式、少量示例和安全边界 + +fine-tuning / LoRA + -> 学习稳定 next-action policy、格式、语气和偏好分布 +``` + +生产上应先做 prompt/context baseline。Fine-tuning 只有在 baseline 出现系统性问题时才有意义,例如: + +- 输出格式不稳定。 +- 动作太泛,缺少场景敏感性。 +- 风险提醒漏掉。 +- 小模型不听复杂 prompt。 +- prompt 太长、成本或延迟不可接受。 + +## Experiment Plan + +为了避免硬套 fine-tuning,本 topic 应做公平比较: + +1. 定义固定测试集和评价标准。 +2. 做 prompt/context baseline。 +3. 做 LoRA fine-tuned model。 +4. 比较两者差异: + - 格式稳定性。 + - 动作可执行性。 + - 购买阶段匹配。 + - 风险意识。 + - 信息增益。 + - 小模型成本和延迟潜力。 + +如果 LoRA 没有明显改善,也应该如实记录。这仍然是有效学习结论。 + +## Recommended Scope + +不要把目标定成“判断闲鱼商品真假”。范围太大,也需要平台数据和工具支持。 + +更合适的目标: + +```text +买家决策辅助 Agent 的 next-action policy lab +``` + +第一阶段: + +- 30-100 条手写或半合成样本。 +- 选择 1-2 个品类,例如手机和相机。 +- 输入:用户意图、商品信息、风险线索、对话片段、Agent 当前回复。 +- 输出:2-4 个 next actions。 +- 比较 prompt baseline 和 LoRA。 + +第二阶段: + +- 扩展品类和风险标签。 +- 加入工具结果,例如同款价格、验机清单、平台规则。 +- 更接近真实岗位作品,但不拆成第二个 active topic。 + +## Current Conclusion + +作为生产方案:先 prompt/context/tooling,不要一上来 fine-tune。 + +作为学习 topic:这个方向适合 fine-tuning,因为它有清晰输入输出、可构造数据集、可做 eval、可做错误分析、可反哺训练,能逼迫学习者理解 fine-tuning 的完整工程链路。 + +因此,本 topic 应保留,但必须把 prompt/context baseline 作为第一对照组。Fine-tuning 的目标不是证明自己一定更好,而是通过实验判断它改善了什么、没有改善什么,以及边界在哪里。 + +## Sources + +- [The Guardian: secondhand marketplace scams and buyer reports](https://www.theguardian.com/money/article/2024/may/09/rife-on-secondhand-marketplaces-depop-preloved-and-shpock) +- [北京市通州区风险提示:二手交易退货、假货和纠纷风险](https://www.bjtzh.gov.cn/xytzh/c109455/202009/1313887.shtml) +- [清华 CJIS PDF:闲置交易平台纠纷与众包判定因素](https://cjis.sem.tsinghua.edu.cn/vol29-03.pdf) +- [Zigpoll: P2P marketplace pain points and trust mechanisms](https://www.zigpoll.com/content/what-are-the-biggest-pain-points-your-users-encounter-when-buying-or-selling-goods-directly-with-each-other-through-your-platform) +- [OpenAI model optimization guide](https://developers.openai.com/api/docs/guides/model-optimization) +- [Hugging Face PEFT documentation](https://huggingface.co/docs/peft/en/index) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/01-need-aligner/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/01-need-aligner/README.md new file mode 100644 index 0000000..42cc9ca --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/01-need-aligner/README.md @@ -0,0 +1,59 @@ +# 01 Need Aligner:LoRA Feedback Loop Lab + +## Learning Navigation + +- Final artifact:mini LoRA fine-tuning feedback loop lab + runbook + 实验对比报告 + 业务迁移笔记。 +- Current stage:01-need-aligner。 +- Current gap:suggestion next action 输入输出 schema、质量标准、小模型、训练环境和材料读取顺序尚未确认。 +- Evidence needed:用户 review 并确认 task card;下一阶段选择材料入口。 +- After this:进入 `02-syllabus-mapper`,围绕 lab 选择 Hugging Face LLM Course / PEFT / TRL / Transformers / Datasets 的最小材料集合。 + +## Confirmed Direction + +本 topic 的主线不是“读完 Hugging Face LLM Course”,而是以它为主学习材料,围绕闲鱼二手买家 Agent 的 suggestion next action 生成完成一个可验证的训练反馈闭环: + +```text +initial suggestion dataset + -> LoRA fine-tuning + -> test / online-like dataset + -> eval + -> error analysis + -> feedback data construction + -> retrain + -> compare +``` + +DDIA 暂不作为 active topic。它只作为 ambient reading,用来维持系统设计直觉,不占用本 topic 的 WIP。 + +## Business Task + +- Agent:闲鱼二手买家 Agent。 +- 用户目标:帮助买家搜索、判断、询问、比价、规避风险并推进二手商品购买决策。 +- 训练任务:给定用户意图、商品上下文和 Agent 当前回复,生成 2-4 个下一步建议动作。 +- 学习策略:先用小数据快速跑通完整闭环,再把任务定义和评测逐步迁移到更贴近真实岗位作品的程度;分阶段但不拆成多个 active topic。 +- 理论边界:每次实践都要回到 LoRA、数据、评测和反馈链路的机制理解,避免只会跑脚本。 + +## Fine-tuning Suitability Gate + +在进入 `02-syllabus-mapper` 前,必须先判断 suggestion next action 是否真的适合 fine-tuning: + +- 如果目标是让模型记住商品事实、实时价格、卖家动态或平台规则,fine-tuning 不是主解法;这些应该来自 context engineering / retrieval / tools。 +- 如果目标是让模型稳定学会“什么购物阶段该建议什么动作、如何识别风险、如何输出统一格式和语气、如何在大量相似场景下保持策略一致”,fine-tuning 可以作为候选解法。 +- prompt engineering / context engineering 必须先做成 baseline;fine-tuning 只有在 baseline 出现系统性问题时才有生产意义。 +- 本 topic 的学习价值在于跑通并理解这个判断链路,而不是预设 fine-tuning 一定优于 prompt。 + +详细调研见 [`02-secondhand-buyer-agent-research.md`](02-secondhand-buyer-agent-research.md)。 + +## Next Review Questions + +请先 review `.daedalus/task-card.md`,然后确认或修改下面 3 个决策: + +1. suggestion next action 的输入应该包含哪些字段:用户意图、商品卡片、卖家信息、对话历史、Agent 当前回复、风险线索? +2. 一个“好”的 suggestion next action 应该满足什么标准:可执行、贴合购买阶段、降低交易风险、信息增益高、不过度打扰、符合平台语气? +3. 第一阶段快速闭环的数据规模和模型规模应该多小,才能既快又能观察到训练前后差异? + +## Stop Rules + +- 不在用户确认 task card 前进入 demo 编码。 +- 不把 DDIA 升级为第二条 active topic。 +- 不提前展开 RLHF / DPO;先把 LoRA feedback loop 跑稳。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/01-finetuning-lab-primer.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/01-finetuning-lab-primer.md new file mode 100644 index 0000000..c256061 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/01-finetuning-lab-primer.md @@ -0,0 +1,357 @@ +# Fine-tuning Lab 基础知识 Primer + +## 为什么先补这层 + +当前用户已经确认业务任务:手机品类二手买家 Agent 的 suggestion next action。下一步本来要选择训练环境、模型和材料,但用户明确表示自己是这方面小白,缺少背景知识。 + +因此本 guide 的目标不是做最终选型,而是建立最小判断地图。 + +## 一句话总览 + +这个 lab 有两条线: + +```text +产品线:prompt/context baseline 能不能把 next action 做好? +训练线:LoRA fine-tuning 能不能让输出更稳定、更贴合偏好? +``` + +如果没有 baseline,fine-tuning 的效果无从判断。 +如果没有固定 eval,训练前后只是主观感觉。 +如果没有数据闭环,LoRA 只是跑了一次脚本。 + +## 核心概念 + +### 1. Base Model + +Base model 是原始模型,例如一个小型开源 LLM。它已经具备通用语言能力。 + +本 topic 不从零训练模型,而是在 base model 上做适配。 + +选择 base model 时主要看: + +- 能不能理解中文。 +- 体积是否小到能训练。 +- Hugging Face 上是否容易加载。 +- 是否适合 LoRA / PEFT。 +- 是否能满足第一轮教学实验,而不是追最强效果。 + +### 2. Prompt / Context Baseline + +Baseline 是“不训练模型”的对照组。 + +它用: + +- prompt:任务说明、输出格式、few-shot examples。 +- context:商品信息、对话历史、用户意图、风险线索。 + +baseline 的作用是回答:不用训练,只靠大模型能力和上下文工程,能做到什么程度? + +如果 baseline 已经足够好,生产上可能不需要 fine-tuning。 +如果 baseline 有系统性问题,才值得用 LoRA 做对照。 + +### 3. Dataset + +Dataset 是训练和评测的材料,不是随便写一些例子。 + +本 topic 至少要分: + +- train set:用来训练。 +- test set:训练时不能看,用来比较训练前后。 +- feedback set:从错误案例里构造,用于第二轮训练。 + +对于 suggestion next action,每条样本大概需要: + +```text +input: + user_goal + product_context + seller_context + conversation_history + agent_reply + risk_signals + +output: + suggested_next_actions +``` + +### 4. Eval / Rubric + +Eval 是判断输出好坏的规则。没有 eval,就不知道 fine-tuning 是否真的变好。 + +当前核心 rubric 可以先包括: + +- 推动购买任务往前。 +- 多帮用户想一步。 +- 安全性。 +- 语义完整性。 +- 相关性。 +- 可执行性。 +- 不过度打扰。 + +第一轮 eval 可以先人工打分或规则 + 人工混合,不必追求自动化完美。 + +### 5. Fine-tuning + +Fine-tuning 是继续训练模型,让模型更适合某个任务。 + +它不是给模型塞实时知识,而是让模型学习稳定行为模式: + +- 什么场景该建议什么动作。 +- 输出格式如何稳定。 +- 语气和策略偏好如何统一。 +- 小模型如何更像目标任务专家。 + +### 6. LoRA + +LoRA 是一种参数高效微调方式。 + +直觉理解: + +- 不改动 base model 的大部分参数。 +- 在模型里插入少量可训练的 adapter。 +- 训练时主要更新 adapter。 +- 保存时保存 adapter,而不是整个大模型。 + +这让训练成本更低,也更适合学习和实验。 + +### 7. PEFT + +PEFT 是 Hugging Face 的参数高效微调工具集合,LoRA 是其中一种常见方法。 + +我们学 PEFT 不是为了背 API,而是为了理解: + +- adapter 如何挂到模型上。 +- 哪些参数被训练。 +- checkpoint 保存了什么。 +- 推理时 base model 和 adapter 如何组合。 + +### 8. TRL + +TRL 更偏 post-training,例如 SFT、DPO、RLHF 等。 + +本 topic 第一阶段不展开完整 TRL pipeline。只有在 LoRA / SFT 闭环跑通后,才把 DPO 等作为后续问题。 + +## 训练环境怎么选 + +### 本地 Mac + +优点: + +- 启动快。 +- 不依赖云环境。 +- 适合写数据、跑 baseline、跑小脚本。 + +限制: + +- 训练 LLM 很容易慢或显存不够。 +- 适合 very small model / toy model / CPU pipeline proof,不适合稍微真实一点的 LoRA 训练。 + +### Colab + +优点: + +- 上手快。 +- 有免费或低成本 GPU。 +- Hugging Face 示例多。 + +限制: + +- 环境不稳定。 +- 文件持久化和复现实验要额外注意。 +- 长时间训练可能中断。 + +### 云 GPU + +优点: + +- 更接近真实训练环境。 +- 可控性更高。 + +限制: + +- 成本更高。 +- 环境配置和账单管理有额外负担。 + +### Toy Model / CPU Proof + +优点: + +- 最快证明 pipeline。 +- 适合先理解数据、训练、eval、feedback 的因果链。 + +限制: + +- 业务效果可能没有说服力。 +- 不能代表真实 LLM fine-tuning 能力。 + +## 第一轮推荐策略 + +对小白最稳的路线不是直接追真实业务效果,而是: + +```text +Step 1: 本地完成数据集 schema + prompt/context baseline + eval rubric +Step 2: 用 Colab 或小 GPU 跑最小 LoRA +Step 3: 比较 baseline vs LoRA +Step 4: 从错误样本构造 feedback set,再训一次 +``` + +这样既不会纯理论,也不会纯跑脚本。 + +## 用户当前选择 + +- 第一阶段先证明完整 pipeline,而不是追求第一版业务效果。 +- 可以接受 Colab / 云 GPU,但需要成本可控。 +- 数据集先由 Agent 生成候选样本,再由用户审核修改。 + +这意味着第一轮 source scout 应优先寻找: + +- 最快能跑通的数据集构造、baseline、LoRA、eval 示例。 +- 低成本训练环境。 +- 解释清楚 LoRA / dataset / eval 因果链的材料。 + +## 云成本直觉 + +对这个 topic 的第一阶段,云成本不应该高。目标是几十到几百条样本、小模型、小步数训练,不是大规模训练。 + +成本控制原则: + +- 先本地完成数据 schema、baseline prompt 和 eval rubric。 +- 只在真正训练 LoRA 时开 GPU。 +- 优先 Colab 或 T4/L4/A10/4090 这类低成本 GPU。 +- 不长时间挂着 notebook 或 pod。 +- 每次训练前先用极小样本 dry run。 + +合理预期: + +- pipeline proof:可能 0-10 美元级别。 +- 小规模 LoRA 多次实验:大约 10-50 美元级别。 +- 如果开始用 A100/H100 长时间训练,成本才会明显上升;这不属于第一阶段。 + +## 云迁移的工程改造成本 + +用户这里的“成本高”更准确地说是工程改造成本:从本地 / notebook / 教学 pipeline 迁移到云上训练,是否需要重写很多东西。 + +结论: + +- 如果第一阶段写成一堆 notebook cell 和本地路径,后续迁移成本会偏高。 +- 如果第一阶段从一开始就把数据、训练、评测、配置和产物保存拆清楚,迁移到 Colab / RunPod / Modal / Hugging Face Jobs 的成本不高。 + +工程上最容易迁移的是这种结构: + +```text +demo/ + datasets/ + raw/ + processed/ + configs/ + train_lora.yaml + eval.yaml + scripts/ + prepare_dataset.py + run_baseline.py + train_lora.py + run_eval.py + build_feedback_set.py + reports/ + baseline.json + lora-round-1.json + lora-round-2.json +``` + +这样本地和云上的差异主要变成: + +- 安装依赖。 +- 下载 / 挂载数据。 +- 选择 GPU。 +- 修改 config 里的模型名、batch size、输出路径。 + +不应该把核心逻辑绑死在: + +- notebook cell 顺序。 +- 绝对路径。 +- 手工复制文件。 +- 隐式环境变量。 +- 只在某个云平台可用的 API。 + +第一阶段如果坚持“script-first + config-first + artifact-first”,迁移成本通常是中低的;如果 notebook-first 且无结构,迁移成本会高。 + +这里的原则不是“以后再工程化”,而是从第一版开始就用生产工程的标准约束自己: + +- 可以使用小模型、小数据、小 GPU 时间,但不能让核心流程只存在于 notebook cell 里。 +- 可以先不追求线上效果,但必须能复现实验、定位输入输出、比较报告和追溯数据版本。 +- 可以晚一点迁移到百炼,但训练数据、评测集、rubric、配置和报告结构要从一开始为迁移留出边界。 + +换句话说,demo 的规模可以小,工程标准不能低。 + +## 阿里云百炼作为训练平台 + +阿里云百炼 / Model Studio 可以作为后续迁移目标。根据官方文档,它支持模型调优能力,包括: + +- SFT:监督微调。 +- CPT:继续预训练。 +- DPO:偏好训练。 +- 通过控制台、API 或命令行创建训练任务。 +- 管理训练集和评测集。 +- 管理调优后模型,以及导入本地训练的 LoRA 模型。 +- 云端部署已调优模型。 + +但对本 topic 来说,百炼不一定适合作为第一阶段唯一入口: + +- 第一阶段我们需要看清底层链路:数据如何进来、LoRA adapter 如何训练、eval 如何比较、feedback set 如何构造。 +- 平台化训练会隐藏一部分训练细节,适合业务落地,但不一定最适合小白理解机制。 +- 百炼更适合作为第二阶段:当本地/Colab pipeline 跑通后,把同一份数据 schema、评测集和训练目标迁移到企业平台。 + +因此推荐路径是: + +```text +Stage 1: 本地/Colab script-first pipeline,理解完整链路 +Stage 2: 百炼平台复现同一任务,学习企业训练平台的数据、任务、部署边界 +``` + +如果第一阶段就直接上百炼,也不是不行,但要额外记录平台隐藏了哪些训练细节,避免只会点控制台。 + +## 模型选择的判断标准 + +第一轮不追最强模型,追可解释闭环。 + +优先级: + +1. 容易加载和训练。 +2. Hugging Face / PEFT 示例多。 +3. 支持中文或至少能处理中文任务。 +4. 体积小,能在低成本环境跑。 +5. 输出足够稳定,便于 eval。 + +模型强不强不是第一优先。第一优先是能不能让你看清训练链路。 + +## 材料选择的判断标准 + +不要通读所有材料。每份材料必须服务一个实验环节: + +| 实验环节 | 需要的材料 | +| --- | --- | +| 加载模型和生成 baseline | Transformers / LLM Course generation 相关内容 | +| 构造数据集 | Datasets 文档 | +| LoRA 训练 | PEFT LoRA 文档和示例 | +| 训练循环 | Trainer / SFTTrainer / 示例 notebook | +| 评测和对比 | LLM Course eval / 自定义 rubric | +| 后续 DPO/RLHF | TRL 文档,暂不展开 | + +## 当前还不能决定的事 + +这些不是现在必须有答案: + +- 最终用哪个具体模型。 +- 是否上云 GPU。 +- 最终数据集规模。 +- 是否做 DPO。 + +现在只需要理解这些选项在解决什么问题。 + +## 下一步问题 + +用户不需要直接选技术栈。下一轮先回答更低门槛的问题: + +1. 第一阶段你更想先证明 pipeline,还是希望第一版就有一点真实业务效果? +2. 你能接受使用 Colab / 云 GPU 吗,还是第一阶段必须尽量本地? +3. 你更愿意先手写小数据集,还是先让 Agent 帮你生成候选样本再由你审核? diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/02-schema-and-engineering-shape.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/02-schema-and-engineering-shape.md new file mode 100644 index 0000000..a9281bd --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/02-schema-and-engineering-shape.md @@ -0,0 +1,286 @@ +# Schema 与工程形态草案 + +本文件沉淀当前 source scout 阶段对业务输入、模型输出、评测 rubric 和工程目录的初版判断。它不是最终实现,但后续 lab 代码不能偏离这里定义的 production-shaped 约束。 + +## 设计原则 + +第一阶段可以小,但不能散。 + +- Script-first:核心动作必须通过脚本执行,而不是依赖 notebook cell 顺序。 +- Config-first:模型、数据、训练参数、评测参数、输出路径都由配置驱动。 +- Artifact-first:每一步输入输出都落盘,能用 run id 串起数据、模型、评测和报告。 +- Migration-ready:本地/Colab 跑通后,迁移到阿里云百炼时尽量复用同一套数据 schema、rubric、评测集和报告结构。 + +这个设计参考了几个一线实践信号: + +- Hugging Face Transformers 官方推荐从 example training scripts 入手,并用小样本参数先验证训练链路。 +- Hugging Face Alignment Handbook 将 recipes、scripts、src、tests 分离,用 recipe configs 驱动训练与评测。 +- MLflow Projects 强调用项目入口和参数封装可复现执行。 +- ZenML 强调 pipeline、step、configuration、artifact、model 的职责分离,以及 artifact lineage。 + +## 买家上下文 Schema + +一次买家上下文应该表达“用户正在完成什么购买任务,以及下一步建议为什么应该出现”。 + +建议第一版使用 JSONL,每行一个样本: + +```json +{ + "sample_id": "phone_000001", + "category": "phone", + "task": { + "goal": "买一台二手 iPhone 14 Pro 作为主力机", + "budget_cny": [3500, 4300], + "must_haves": ["电池健康不低于 85%", "无拆修", "支持当面验机"], + "nice_to_haves": ["原盒配件", "保修未过期"], + "hard_constraints": ["不接受监管机", "不接受隐藏 ID 锁风险"] + }, + "stage": "compare", + "progress": { + "known": ["用户已找到 3 个候选商品", "用户知道大致预算"], + "unknown": ["是否有维修记录", "电池是否更换过", "是否支持线下验机"], + "blockers": ["候选商品价格接近,但风险信息不足"] + }, + "candidates": [ + { + "item_id": "xianyu_001", + "title": "iPhone 14 Pro 256G 深空黑", + "price_cny": 3980, + "seller_claims": ["国行", "无拆修", "电池 88%"], + "visible_risks": ["没有验机报告", "图片未展示边框细节"] + } + ], + "conversation": [ + { + "role": "user", + "content": "这几个价格差不多,我不知道该选哪个。" + }, + { + "role": "assistant", + "content": "先不要只按价格排序,可以优先排除维修、ID 锁和电池异常风险。" + } + ], + "knowledge_context": { + "injected": true, + "scope": "stage_specific", + "snippets": [ + { + "id": "phone_risk_id_lock", + "text": "二手 iPhone 需要确认是否退出 Apple ID、关闭查找功能,并避免监管机或隐藏锁风险。" + } + ] + }, + "labels": { + "target_actions": [ + { + "suggestion": "让卖家录屏展示 Apple ID 已退出、查找功能已关闭,并说明是否为监管机。", + "reason": "当前处于风险确认前的对比阶段,ID 锁和监管机是用户可能不知道但必须先排除的关键风险。" + } + ] + }, + "metadata": { + "source": "agent_generated_user_reviewed", + "split": "train", + "version": "v0.1" + } +} +``` + +## 阶段枚举 + +第一版不做复杂动态知识检索,但必须把阶段显式化: + +| stage | 含义 | 下一步建议重点 | +| --- | --- | --- | +| need_clarification | 需求澄清 | 预算、用途、硬性约束 | +| search | 搜索 | 关键词、筛选条件、价格区间 | +| shortlist | 初筛 | 排除明显不匹配和高风险商品 | +| compare | 对比 | 同款价格、成色、维修、电池、卖家可信度 | +| item_understanding | 了解单个商品 | 追问细节、要求补图、确认参数 | +| seller_chat | 与卖家沟通 | 问法、证据、议价、验货安排 | +| risk_check | 风险确认 | 假货、翻新、监管、锁、维修、售后风险 | +| order_decision | 下单决策 | 交易方式、验货、支付和收货检查 | +| after_sale | 售后 | 到货检查、问题留证、协商 | +| dispute | 维权 | 证据整理、平台规则、沟通路径 | + +## 知识注入策略 + +第一版采用“显式字段 + 可选注入块”,不做复杂检索系统。 + +- 数据集中保留 `knowledge_context`,让模型和评测都知道本样本依据了哪些领域知识。 +- 第一轮可以一次性注入少量手机品类风险知识。 +- 第二轮再尝试按 `stage` 选择性注入。 +- 不在第一阶段实现完整 RAG;否则会把 fine-tuning lab 变成检索系统项目。 + +## 输出 Schema + +第一版训练输出不要过重。小模型容易在复杂 JSON 上失稳,且产品里的 suggestion next action 不是给用户看的菜单标签,而是用户点击后直接发送给买家 Agent 的下一步指令。 + +最终 target schema 收敛为“短 suggestion + 短 reason”的数组: + +```json +{ + "suggestions": [ + { + "s": "帮我让卖家补一张屏幕纯白图", + "r": "缺少屏幕成色证据" + }, + { + "s": "帮我问问卖家是否支持线下验机", + "r": "降低功能和成色争议" + } + ] +} +``` + +### 输出约束 + +- `suggestions` 是数组,最多 3 条。 +- 每条包含 `s` 和 `r` 两个短字段。 +- `s` 是用户口吻的短指令,点击后会直接发送给买家 Agent。 +- `r` 是短原因,用于解释为什么这个 action 是当前好下一步。 +- 多条之间要互补,不能只是同义改写。 +- 如果当前只有一个最重要动作,可以只输出 1 条。 +- `r` 不能把 `s` 重新写成长解释,也不能引入新事实。 +- 不在模型输出中生成标签、评分或内部审核字段。 + +`s` 的判断标准: + +- 好:`帮我把这三台按电池、保修和验机证据做个对比表` +- 好:`帮我生成一句礼貌话术,问卖家是否退出 Apple ID` +- 差:`生成三台对比表` +- 差:`确认已退出 Apple ID` + +差的原因是它们更像内部动作标签,不像用户会直接发给 Agent 的自然指令。 + +复杂信息仍然放在标注和评测侧: + +```json +{ + "label_meta": { + "stage_fit": "item_understanding", + "risk_tags": ["condition_uncertainty"], + "priority": "high" + } +} +``` + +权衡: + +- `{s, r}` 比纯字符串更难一点,但仍然比长建议稳定。 +- `s` 保持“用户发给 Agent 的自然指令”形态,`r` 保持训练和审核所需的解释信号。 +- 字段名使用短名可以降低输出长度,也减少小模型格式负担。 +- eval 仍然可以用 `stage_fit / risk_tags / priority` 判断输出是否贴合阶段、覆盖关键风险、优先级是否合理。 + +推荐边界:第一轮训练就使用 `{s, r}`,但严格控制 `s` 和 `r` 的长度;如果小模型格式不稳,再退回 action-only 作为降级方案。 + +## Eval Rubric + +“推动购买任务往前”需要拆成可评测维度。 + +| 维度 | 问题 | 评分 | +| --- | --- | --- | +| relevance | 是否贴合当前购买任务、商品和阶段? | 0-2 | +| stage_progress | 是否帮助用户进入下一步,而不是原地闲聊? | 0-2 | +| hidden_need | 是否发现用户不知道但应该知道的关键点? | 0-2 | +| domain_correctness | 手机/二手交易知识是否正确? | 0-2 | +| risk_awareness | 是否覆盖必要风险,而不是只促成下单? | 0-2 | +| actionability | 用户能不能直接照着做? | 0-2 | +| safety | 是否避免违法、欺诈、隐私侵犯或危险建议? | pass/fail | +| format | 是否满足输出 schema? | pass/fail | + +第一阶段可用 rule-based + LLM judge 混合评测: + +- rule-based 检查 JSON schema、数量、空值、重复、禁用词。 +- LLM judge 评分语义维度,但评分 prompt 和样本必须版本化。 +- 用户人工审核一小批样本,用来校准 judge 是否跑偏。 + +## 工程目录草案 + +建议 lab 目录采用下面结构: + +```text +demo/ + README.md + pyproject.toml + configs/ + baseline.yaml + train_lora.yaml + eval.yaml + feedback_round_1.yaml + src/ + buyer_agent_lab/ + __init__.py + schemas.py + data.py + prompts.py + baseline.py + train.py + evaluate.py + feedback.py + reporting.py + scripts/ + prepare_dataset.py + run_baseline.py + train_lora.py + run_eval.py + build_feedback_set.py + generate_report.py + data/ + raw/ + interim/ + processed/ + eval/ + artifacts/ + runs/ + 2026-06-14T000000Z_baseline/ + 2026-06-14T010000Z_lora_r1/ + 2026-06-14T020000Z_lora_r2/ + reports/ + baseline_vs_lora_r1.md + lora_r1_vs_lora_r2.md + tests/ + test_schemas.py + test_eval_rules.py +``` + +目录职责: + +- `configs/`:所有可调参数,便于本地、Colab、百炼迁移时替换执行环境。 +- `src/`:可复用业务逻辑,不把核心逻辑塞进 scripts。 +- `scripts/`:命令行入口,只做参数解析和调用。 +- `data/`:数据生命周期分层;真实敏感数据后续不能直接入库。 +- `artifacts/runs/`:每次运行的模型输出、预测、指标、错误样本和配置快照。 +- `reports/`:人能读懂的对比结论。 +- `tests/`:先覆盖 schema 和 eval rule 这两个最容易影响结果可信度的部分。 + +## Run Artifact 约定 + +每次运行至少生成: + +```text +artifacts/runs/<run_id>/ + config.resolved.yaml + dataset_manifest.json + predictions.jsonl + metrics.json + errors.jsonl + run_summary.md +``` + +训练运行额外生成: + +```text +adapter/ +trainer_state.json +train_metrics.json +``` + +这样后续迁移到百炼时,即使训练执行层换掉,也能继续保留同一套输入输出协议。 + +## 当前取舍 + +- 先不做完整 RAG,只把知识注入设计成 schema 字段。 +- 先不做 DPO/RLHF,只做 baseline 与 LoRA/SFT 对照。 +- 先不追最强中文模型,优先保证链路可解释、可复现、可迁移。 +- 先不引入完整 MLOps 平台,但保留 artifact lineage 和配置化入口。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/03-first-lab-default-plan.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/03-first-lab-default-plan.md new file mode 100644 index 0000000..c56764a --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/03-first-lab-default-plan.md @@ -0,0 +1,139 @@ +# 第一轮 Lab 默认方案 + +本文件给出 daedalus 主动推进后的第一轮默认方案。除非用户明确反对,后续进入 `06-practice-transfer` 和 demo 设计时按这里执行。 + +## 默认结论 + +第一轮目标不是追求线上效果,而是跑通一个 production-shaped 的 LoRA feedback loop: + +```text +agent-generated reviewed dataset + -> prompt/context baseline + -> LoRA/SFT round 1 + -> fixed eval + -> error analysis + -> feedback dataset + -> LoRA/SFT round 2 + -> comparison report +``` + +默认选择: + +| 维度 | 默认方案 | 备选 | +| --- | --- | --- | +| 第一轮模型 | `Qwen/Qwen2.5-0.5B-Instruct` | 如果输出质量太弱,升级到 `Qwen/Qwen2.5-1.5B-Instruct` | +| 训练方式 | PEFT LoRA + TRL SFTTrainer | 如果 TRL 引入复杂度过高,退回 Transformers Trainer + PEFT | +| 训练环境 | Colab 或低成本单卡云 GPU | 本地只做数据、baseline、eval、报告,不强求训练 | +| 数据规模 | 初始 train 80 / eval 20 / holdout 20 | 如果训练不稳定,先缩到 train 40 / eval 10 / holdout 10 验证链路 | +| 输出 schema | `{"suggestions":[{"s":"用户口吻短指令","r":"短原因"}]}`,最多 3 条 | 如果小模型格式不稳,再降级为只输出 `s` | +| 企业平台 | 阿里云百炼作为第二阶段复现目标 | 第一阶段不以百炼为唯一入口 | + +## 为什么默认 Qwen2.5 小模型 + +选择标准不是“最强”,而是“最适合第一轮闭环”: + +- 中文任务友好,适合闲鱼二手买家 Agent 场景。 +- 0.5B / 1.5B 体量较小,适合低成本 LoRA 实验。 +- Hugging Face 上有 instruction-tuned 版本,baseline 更容易起步。 +- 与 PEFT、Transformers、TRL 生态兼容,后续迁移和排障资料多。 + +风险: + +- 0.5B 可能对复杂上下文理解不足。 +- 小数据 fine-tuning 可能主要改善格式稳定性,不一定改善真实任务质量。 +- 如果 baseline 已经很强,LoRA 改善可能不明显;这本身也是有效结论。 + +对应处理: + +- 第一轮默认要求模型输出最多 3 条 `{s, r}`;`s` 必须像用户直接发给买家 Agent 的指令,`r` 必须短且解释当前为什么要这样做。 +- 评测必须比较 baseline vs LoRA,而不是只看 LoRA 是否“看起来能用”。 +- 如果 0.5B 无法形成基本质量,再升级到 1.5B,不直接跳到大模型。 + +## 材料入口 + +只读支撑当前 lab 的最小材料。 + +| 材料 | 用途 | 阅读方式 | +| --- | --- | --- | +| Hugging Face LLM Course | 建立 Transformers / Datasets / training 基础主线 | 只读和当前 lab 直接相关章节 | +| Transformers training scripts | 了解官方脚本化训练入口和小样本调试方式 | 看运行方式和参数组织,不照抄整套 | +| PEFT docs: LoRA | 理解 adapter、LoraConfig、保存/加载边界 | 重点读 LoRA quicktour 和 package reference | +| TRL SFTTrainer docs | 使用 SFTTrainer 跑 supervised fine-tuning | 重点读 dataset 格式、trainer 参数和最小脚本 | +| Qwen2.5 model cards | 确认模型加载、chat template、license/usage | 只确认第一轮模型可用性 | +| 阿里云百炼文档 | 第二阶段迁移边界 | 只记录 SFT/DPO/数据集/部署能力,不在第一轮展开 | + +当前官方依据: + +- Hugging Face Transformers 提供 example training scripts,并建议先用 `max_train_samples` 等参数在小样本上验证链路。 +- Hugging Face PEFT 说明 LoRA 通过只训练少量额外参数降低训练成本。 +- Hugging Face TRL 的 SFTTrainer 官方示例已经使用 Qwen 小模型作为 quick start。 +- Qwen2.5 官方 Hugging Face model card 提供 0.5B 到 72B 的 base 和 instruction-tuned 模型。 + +## 第一批数据规模 + +默认先做 120 条样本: + +```text +train: 80 +eval: 20 +holdout: 20 +``` + +覆盖阶段: + +| stage | 目标样本数 | +| --- | ---: | +| need_clarification | 10 | +| search | 10 | +| shortlist | 10 | +| compare | 15 | +| item_understanding | 15 | +| seller_chat | 15 | +| risk_check | 20 | +| order_decision | 10 | +| after_sale | 8 | +| dispute | 7 | + +为什么不是更大: + +- 用户需要审核 Agent 生成样本;样本越大,人工校准成本越高。 +- 第一轮重点是 pipeline,而不是数据规模。 +- 如果 120 条无法看出趋势,再用错误分析决定补哪些 stage,而不是平均扩充。 + +同时按难度覆盖: + +| case 类型 | 目标样本数 | 用途 | +| --- | ---: | --- | +| typical | 60 | 让模型学会标准购买路径 | +| ambiguous | 30 | 训练模型在信息不足时先补证据 | +| boundary | 18 | 训练模型处理风险和收益冲突 | +| negative / unsafe | 12 | 训练模型学会暂停、避开或走维权路径 | + +这两个维度要交叉覆盖。例如 `risk_check` 里应有 typical 风险确认,也应有 boundary 和 negative/unsafe;`order_decision` 里应有正常下单前确认,也应有脱离平台付款这类 negative case。 + +## 第一轮工程入口 + +后续 demo 应提供这些命令级入口: + +```bash +python scripts/prepare_dataset.py --config configs/dataset.yaml +python scripts/run_baseline.py --config configs/baseline.yaml +python scripts/train_lora.py --config configs/train_lora_qwen_0_5b.yaml +python scripts/run_eval.py --config configs/eval.yaml +python scripts/build_feedback_set.py --config configs/feedback_round_1.yaml +python scripts/generate_report.py --run-a baseline --run-b lora_r1 +``` + +每个脚本必须生成 artifact,不能只打印结果。 + +## 是否进入 03 + +`02-syllabus-mapper` 可以在满足下面条件后进入 `03-concept-roadmap`;业务样本与 baseline 设计在后续 `06-practice-transfer` 承接: + +- 已有默认模型和升级路径。 +- 已有默认训练环境。 +- 已有第一批数据规模。 +- 已有最小材料入口。 +- 已有工程目录与 artifact 约定。 + +目前这些条件已具备。下一阶段应开始构建问题路线图和第一批数据生成/审核标准。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/README.md new file mode 100644 index 0000000..79b15ac --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/02-syllabus-mapper/README.md @@ -0,0 +1,108 @@ +# 02 Syllabus Mapper:Hugging Face LLM Course 材料选择 + +## Learning Navigation + +- Final artifact:手机品类二手买家 Agent suggestion next action 的 baseline + LoRA 对照实验。 +- Current stage:02-syllabus-mapper。 +- Current gap:需要选择最小课程、文档和示例组合,不通读、不泛学。 +- Evidence needed:材料清单、每个材料服务的实验环节、暂不阅读清单。 +- After this:进入 `03-concept-roadmap`,先建立 HF Course 1/2 概念路线;业务 schema、rubric、baseline 和数据集问题路线图已迁入 `06-practice-transfer`。 + +## Confirmed From 01 + +- Fine-tuning 是最后选择;生产上先做 prompt/context/tooling baseline。 +- 第一轮品类:手机。 +- 核心质量标准:推动购买任务往前、多帮用户想一步,同时满足安全性、语义完整性和相关性。 +- 学习策略:实践 + 理论闭环;每个实验步骤都要解释背后的训练链路。 +- 工程策略:第一阶段就采用 script-first / config-first / artifact-first;实验规模可以小,但不能用 notebook demo 标准降低可复现、可迁移、可审计要求。 + +## Syllabus Mapper Goal + +选择能支持下面实验闭环的最小材料集合: + +```text +phone-shopping scenario dataset + -> prompt/context baseline + -> LoRA fine-tuning + -> fixed eval set + rubric + -> error analysis + -> feedback data + -> retrain + compare +``` + +## Primer + +如果训练环境、模型选择、LoRA、PEFT、数据集和 eval 的概念还不清楚,先读: + +- [`01-finetuning-lab-primer.md`](01-finetuning-lab-primer.md) + +如果需要确认业务 schema、输出格式、评测 rubric 和工程目录,读: + +- [`02-schema-and-engineering-shape.md`](02-schema-and-engineering-shape.md) + +如果需要确认第一轮默认模型、训练环境、数据规模和材料入口,读: + +- [`03-first-lab-default-plan.md`](03-first-lab-default-plan.md) + +## Candidate Material Buckets + +| Bucket | 作用 | 选择标准 | +| --- | --- | --- | +| Hugging Face LLM Course | 建立 tokenizer/model/datasets/training/eval 的课程主线 | 只读支撑当前 lab 的章节 | +| Transformers docs/examples | 模型加载、推理、Trainer 或 generation baseline | 只选一个可运行入口 | +| Datasets docs | 构造、切分、保存训练集和测试集 | 必须服务数据闭环 | +| PEFT / LoRA docs | adapter 配置、训练、保存、加载 | 必须能解释 LoRA 改了什么 | +| TRL docs | 后续 DPO/RLHF 入口 | 本阶段只标记,不提前展开 | +| 阿里云百炼 / Model Studio | 第二阶段候选训练、评测、部署平台 | 先确认 SFT/DPO/数据集/部署能力,不作为第一轮唯一入口 | +| OpenAI / external model optimization docs | 校准 fine-tuning 适用边界 | 只用于 critical lens | + +## Engineering Shape Required + +材料选择必须优先支持下面的工程形态: + +- Script-first:训练、评测、反馈集构造、报告生成都有可重复执行的脚本入口。 +- Config-first:模型名、数据路径、训练参数、评测集、输出目录都由配置文件控制。 +- Artifact-first:训练集、测试集、baseline 输出、LoRA 输出、eval 结果、错误分析和再训练报告都落盘,并能通过 run id 串起来。 +- Migration-ready:第一阶段本地/Colab 能跑,第二阶段迁移到百炼时尽量复用同一份数据 schema、rubric、评测集和报告结构。 + +不选择只能在 notebook cell 顺序里成立、依赖手工复制文件、路径写死或无法复现实验产物的材料作为主入口。 + +## Do Not Read Yet + +- 完整 RLHF / DPO pipeline。 +- 大规模分布式训练。 +- 模型架构论文全集。 +- 与手机 suggestion next action 无关的 leaderboard、benchmark 和新模型新闻。 +- DDIA 正式 topic;DDIA 继续作为 ambient reading。 + +## User Questions Before Completion + +1. 第一轮训练环境是什么:本地 Mac、Colab、云 GPU,还是先用 CPU/toy model 证明 pipeline? +2. 第一轮模型选择标准是什么:中文能力、体积小、Hugging Face 示例多、LoRA 支持好,还是部署成本低? +3. 你希望材料选择优先找“能最快跑起来的示例”,还是“理论链路讲得最清楚的材料”? + +## User Answers + +- 第一阶段优先目标:先构建完整 pipeline,后续再考虑迁移到云上和真实业务效果。 +- 训练环境约束:可以接受 Colab / 云 GPU,但需要控制成本。 +- 数据集策略:先由 Agent 生成候选样本,用户审核修改。 +- 训练平台候选:用户所在工作场景主要可能使用阿里云百炼平台,需要评估它是否适合作为第二阶段迁移目标。 +- 工程形态确认:从第一阶段开始按生产工程标准设计,采用 script-first / config-first / artifact-first,而不是用 demo 标准放过自己。 +- Schema 初稿:买家上下文包含购买任务、候选商品、品类、约束条件、购买阶段、已知/未知/卡点、对话上下文和可选知识注入;target 收敛为 `{"suggestions":[{"s":"短建议","r":"短原因"}]}`,最多 3 条;`s` 是用户点击后直接发送给买家 Agent 的用户口吻指令,`r` 解释当前为什么要这样做。 +- 第一轮默认方案:`Qwen/Qwen2.5-0.5B-Instruct` 起步,`Qwen/Qwen2.5-1.5B-Instruct` 作为升级候选;Colab/低成本单卡云 GPU 训练;初始数据规模 train 80 / eval 20 / holdout 20;PEFT LoRA + TRL SFTTrainer 优先。 + +## Syllabus Decision + +本阶段已收敛为下面的默认材料组合: + +| 环节 | 默认材料 | +| --- | --- | +| 课程主线 | Hugging Face LLM Course 中 Transformers / Datasets / training 相关章节 | +| baseline 与模型加载 | Transformers docs/examples | +| 数据集构造 | Datasets docs | +| LoRA 训练 | PEFT LoRA docs | +| SFT 执行 | TRL SFTTrainer docs | +| 第一轮模型 | Qwen2.5 0.5B/1.5B Instruct model cards | +| 第二阶段迁移 | 阿里云百炼 Model Studio 文档 | + +`02-syllabus-mapper` 剩余动作不再继续泛搜材料;下一步进入 `03-concept-roadmap` 建立课程概念路线,后续在 `06-practice-transfer` 承接真实业务数据、baseline、rubric 和 demo 设计。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/03-concept-roadmap/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/03-concept-roadmap/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/03-concept-roadmap/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/03-concept-roadmap/README.md new file mode 100644 index 0000000..2333f10 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/03-concept-roadmap/README.md @@ -0,0 +1,53 @@ +# HF Course 1/2 Concept Roadmap + +本 guide 是迁移到 course-learning 后的第一份正式课程阶段指南。课程实验入口统一收敛到 `04-lesson-lab`。 + +## Current Course Scope + +- 课程:Hugging Face LLM Course +- 当前范围:Chapter 1 Transformer Models + Chapter 2 Using Transformers +- 用户已完成:Transformers pipeline smoke test、ELI5 dataset inspection、tokenizer / group_texts / labels / collator / Trainer warning 观察、DistilGPT2 Causal LM quick training run。 +- 当前目标:把“我跟着教程跑通了”重构成“我知道每一步在训练链路里承担什么职责”。 + +## Concept Questions + +| Concept | 要回答的问题 | 观察证据 | 下一步 | +| --- | --- | --- | --- | +| `pipeline` | task string 如何展开成 tokenizer、model、config、postprocess? | 已跑通 sentiment pipeline;观察到默认模型 warning | 进入 `04-lesson-lab` 时固定 model name,打印 pipeline 内部组件 | +| tokenizer | 文本为什么要先变成 `input_ids` / `attention_mask`? | 已观察 tokenizer 输出、decode 和长序列 warning | 补一页 lesson lab:单条文本、batch、padding、truncation | +| Causal LM | 为什么 GPT-2 预训练目标是预测 next token? | 已完成 ELI5 Causal LM quick training run | 用 logits shape 和 shifted labels 解释 loss | +| `group_texts` | 为什么要拼接再切成固定 block?输入输出类型是什么? | 已观察 block size = 128,`labels == input_ids` | 将观察整理进 `notes/04-lesson-lab` | +| data collator | collator 在 batch 进入 model 前做了什么? | 已理解 `mlm=False`、pad/eos warning | 打印一个 batch 的 keys、shape、device | +| `Trainer.train()` | batch 如何经过 forward、loss、backward、optimizer step? | 已迁移到 `04-lesson-lab/03-trainer-train-under-the-hood.md` | 当前正式入口是 `guides/04-lesson-lab/README.md` | + +## Mechanism Deep Dive Candidates + +只在 lesson lab 观察仍解释不清时进入 `05-mechanism-deep-dive`。 + +- `ForCausalLMLoss` 如何 shift logits / labels。 +- `pipeline("text-generation")` 如何调用 `model.generate`。 +- `Trainer` 如何组装 dataloader、loss、accelerator、optimizer 和 scheduler。 + +## Transfer Thread + +这些概念最终要迁移到闲鱼买家 Agent suggestion next action 的 LoRA/SFT feedback loop: + +- tokenizer / batch:决定数据样本如何变成训练输入。 +- labels / loss:决定模型到底被训练去预测什么。 +- collator / padding:决定结构化 `{s,r}` target 如何批处理。 +- Trainer / artifacts:决定训练过程如何脚本化、配置化、产物化。 +- eval:决定 prompt/context baseline 与 LoRA/SFT 的比较是否可信。 + +## Next Lesson Lab + +下一步生成: + +- [`../04-lesson-lab/README.md`](../04-lesson-lab/README.md) + +目标是继续执行 `Trainer.train()` 拆解 lesson lab: + +```text +batch -> model(**batch) -> logits/loss -> loss.backward -> optimizer.step +``` + +用户需要亲自运行或复查 notebook 中的打印结果;Agent 不能把自己的解释写成用户已观察证据。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/01-hf-course-ch1-ch2-foundation.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/01-hf-course-ch1-ch2-foundation.md new file mode 100644 index 0000000..df63662 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/01-hf-course-ch1-ch2-foundation.md @@ -0,0 +1,122 @@ +# HF Course 1/2 基础学习导航 + +本阶段目标:先完成 Hugging Face LLM Course 第 1、2 章,建立 Transformers 推理链路的基础,再进入买家 Agent baseline 和 LoRA。 + +不要把本阶段变成“看完课程”。每一节都要落到一个问题: + +> 我现在能解释 pipeline 的哪一层?能用一个最小实验验证它吗? + +## 为什么先学 1 和 2 + +当前已经跑通 `pipeline("text-classification")`,但还没有真正理解: + +- task 如何选择默认模型。 +- tokenizer 如何把文本变成模型输入。 +- model 输出是什么。 +- postprocess 如何把 logits 变成 label / score。 +- 为什么不指定 model 不适合生产或严肃实验。 + +第 1 章解决“Transformer 能做什么、整体架构是什么”;第 2 章解决“pipeline 背后发生了什么、如何直接使用 model 和 tokenizer”。 + +## Chapter 1:Transformer Models + +只抓四个核心问题: + +| 课程内容 | 本 topic 要得到什么 | +| --- | --- | +| Transformers, what can they do? | 知道 pipeline task 是任务接口,不是模型本身 | +| How do Transformers work? | 建立 tokenizer -> model -> head/postprocess 的整体图 | +| Transformer Architectures | 区分 encoder / decoder / encoder-decoder 的大方向 | +| Bias and limitations | 记住模型输出不是事实,后续 eval 必须独立设计 | + +不展开: + +- 不深挖注意力公式。 +- 不追模型排行榜。 +- 不提前研究完整训练细节。 + +## Chapter 2:Using Transformers + +这是本阶段主线。 + +| 课程内容 | 本 topic 要完成的实验 | +| --- | --- | +| Behind the pipeline | 把 `pipeline` 拆成 tokenizer、model、postprocess | +| Models | 显式加载 `AutoModelForSequenceClassification` | +| Tokenizers | 打印 tokens、input_ids、attention_mask | +| Handling multiple sequences | 理解 padding、truncation、batch | +| Putting it all together | 写一个不用 pipeline 的等价推理脚本 | + +## 最小实验清单 + +在 `demo/hugging-face-course-learning` 里完成: + +1. 固定 model name,不使用默认模型。 +2. 打印 pipeline 组件: + - task + - model class + - tokenizer class + - config id2label / label2id +3. 写 `behind_pipeline.py`: + - tokenizer(text) + - model(**inputs) + - softmax(logits) + - 映射 label +4. 做一个 batch 输入实验: + - 两条英文句子。 + - 打印 padding / attention_mask。 +5. 记录一个现象: + - runtime alias `sentiment-analysis` 可运行,但 static overload 更偏 canonical `text-classification`。 + +## 完成标准 + +本阶段完成时,用户应能用自己的话解释: + +```text +raw text + -> tokenizer + -> input_ids / attention_mask + -> model + -> logits + -> softmax + -> label / score +``` + +并能回答: + +- pipeline 帮我们隐藏了哪些步骤? +- model 和 tokenizer 为什么必须匹配? +- attention_mask 是为了解决什么问题? +- 为什么实验里要固定 model name? +- 哪些 warning 可以忽略,哪些 warning 会影响实验可靠性? + +## 之后再进入 + +完成 1/2 章基础后,再回到: + +- 买家 Agent `{s, r}` baseline。 +- 第一批 v0.4 样本审核。 +- eval rubric 固化。 +- LoRA / PEFT 训练闭环。 + +## 当前补充:从 Text Generation 到 Causal LM + +用户当前学习到 Chapter 1/5 Text generation 小节: + +```text +GPT-2's pretraining objective is based entirely on causal language modeling, +predicting the next word in a sequence. +``` + +这里先不要急着把它理解成“我要马上完整微调一个 GPT-2”。更准确的顺序是: + +```text +GPT-2 为什么能生成文本 + -> 因为它按 causal LM 目标预训练 + -> causal LM 的核心是只看左边上下文预测下一个 token + -> fine-tuning recipe 只是把自己的文本也改造成这种 next-token prediction 训练样本 +``` + +`causal_language_model.py` 是顺着这个概念提前接触到的实现 recipe。阅读时先看: + +- [`02-causal-lm-code-reading.md`](02-causal-lm-code-reading.md) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/02-causal-lm-code-reading.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/02-causal-lm-code-reading.md new file mode 100644 index 0000000..05e2e10 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/02-causal-lm-code-reading.md @@ -0,0 +1,293 @@ +# Causal Language Modeling 代码阅读指南 + +本文件解释 `causal_language_model.py` 这类 Hugging Face recipe 背后的底层逻辑。 + +概念入口不是“我要先完整微调一个模型”,而是 Chapter 1/5 Text generation 小节里的这句话: + +```text +GPT-2's pretraining objective is based entirely on causal language modeling, +predicting the next word in a sequence. +``` + +这句话讲的是 GPT-2 为什么擅长生成文本;`causal_language_model.py` 展示的是如何把自己的文本数据也整理成同一种 next-token prediction 训练目标。 + +用户当前感受:代码能跟着抄,但不知道每一步在做什么、为什么这么写、如果没有参考资料如何自己写出来。 + +## 先判断:这段代码到底在做什么? + +它不是单纯的 text generation inference。 + +它是在做一个 causal language modeling fine-tuning pipeline。它是 “GPT-2 预训练目标” 的下游应用,而不是 Chapter 1/5 那句话本身: + +```text +原始问答文本 + -> 抽取训练文本 + -> tokenizer 转成 token ids + -> 拼成长 token stream + -> 切成固定长度 block + -> labels = input_ids + -> causal LM 内部右移 labels + -> 计算 next-token prediction loss + -> Trainer 微调模型 +``` + +一句话:把很多段文本整理成“给前面的 token,预测下一个 token”的训练样本。 + +## 底层任务:为什么叫 Causal LM? + +Chapter 1/5 里讲到: + +- BERT 这类 encoder 常用 masked language modeling:看左右文,预测被遮住的 token。 +- GPT 这类 decoder 常用 causal language modeling:只能看左边上下文,预测下一个 token。 + +Causal LM 的训练目标可以写成: + +```text +输入: token_1, token_2, token_3 +目标: token_2, token_3, token_4 +``` + +模型不是一次预测整篇文章的“语义”,而是在每个位置预测下一个 token 的概率分布。 + +## 你的代码每一步在解决什么问题 + +### 1. 登录 Hugging Face + +```python +notebook_login() +``` + +用途: + +- 如果要 push model 到 Hub,需要身份。 +- 如果下载 gated/private 资源,也需要 token。 + +当前学习阶段不一定需要。尤其本地脚本里用 `notebook_login()` 会让流程不够脚本化,后面更适合改成环境变量或 `huggingface-cli login`。 + +### 2. 加载数据集 + +```python +eli5 = load_dataset("dany0407/eli5_category", split="train[:5000]") +eli5 = eli5.train_test_split(test_size=0.2) +``` + +用途: + +- 先拿一部分 ELI5 数据,避免全量太慢。 +- 切出 train/test,后面训练和评估要分开。 + +底层逻辑: + +训练不是只看模型能不能记住训练文本,还要看它在没训练过的文本上 loss 如何。 + +### 3. 加载 tokenizer + +```python +tokenizer = AutoTokenizer.from_pretrained("distilbert/distilgpt2") +``` + +用途: + +- 把字符串变成模型能处理的 token ids。 +- tokenizer 必须和模型匹配,否则 token id 的含义会错。 + +底层逻辑: + +模型不认识文字,只认识整数 id。tokenizer 是“文本世界”和“模型张量世界”的边界。 + +### 4. flatten 数据 + +```python +eli5 = eli5.flatten() +``` + +用途: + +- ELI5 的字段里有嵌套结构,例如 `answers.text`。 +- flatten 后更容易在 `map` 里批量取字段。 + +底层逻辑: + +训练管线需要稳定、扁平、批处理友好的字段。 + +### 5. preprocess:抽文本 + tokenization + +```python +def preprocess_function(examples): + return tokenizer([" ".join(x) for x in examples["answers.text"]]) +``` + +用途: + +- 取每条样本的回答文本。 +- 多个回答合并成一段训练文本。 +- 用 tokenizer 转成 `input_ids` 和 `attention_mask`。 + +底层逻辑: + +Causal LM 不关心“这是第几个回答”的原始结构,它需要的是连续文本 token 序列。 + +### 6. remove_columns + +```python +tokenized_eli5 = eli5.map( + preprocess_function, + batched=True, + num_proc=4, + remove_columns=eli5["train"].column_names, +) +``` + +用途: + +- 去掉原始文本字段,只保留模型训练需要的 token 字段。 + +底层逻辑: + +Trainer 后面只需要张量化字段。原始字段如果混在 batch 里,collator 可能不知道怎么处理。 + +### 7. group_texts:拼接再切块 + +```python +block_size = 128 + +def group_texts(examples): + concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()} + total_length = len(concatenated_examples[list(examples.keys())[0]]) + total_length = (total_length // block_size) * block_size + result = { + k: [t[i : i + block_size] for i in range(0, total_length, block_size)] + for k, t in concatenated_examples.items() + } + result["labels"] = result["input_ids"].copy() + return result +``` + +这是最容易迷糊的一步。 + +它做了三件事: + +1. 把 batch 里的 token list 全部拼成一条长 token stream。 +2. 按 `block_size` 切成固定长度片段。 +3. 复制 `input_ids` 作为 `labels`。 + +为什么要这样? + +- GPT 类模型训练时需要固定长度或批内可对齐的 token blocks。 +- 文本生成模型不需要每条原始问答都保持边界,它只需要大量连续 token 来练 next-token prediction。 +- `labels = input_ids` 看起来奇怪,但 causal LM 的 model 内部会做 shift:用当前位置之前的 token 预测当前位置 token。 + +## 8. data collator + +```python +tokenizer.pad_token = tokenizer.eos_token +data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False) +``` + +用途: + +- 把多条样本组成 batch。 +- 动态 padding。 +- `mlm=False` 表示不是 BERT 式 masked LM,而是 causal LM。 + +为什么 `pad_token = eos_token`? + +GPT2 系列默认没有 pad token。为了 batch padding,需要指定一个 pad token。常见做法是复用 eos token。 + +## 9. AutoModelForCausalLM + +```python +model = AutoModelForCausalLM.from_pretrained("distilbert/distilgpt2") +``` + +用途: + +- 加载适合 causal language modeling 的模型头。 + +底层逻辑: + +`AutoModelForCausalLM` 不只是 base transformer,它带了 language modeling head,可以把 hidden states 映射到 vocabulary logits。 + +输出形状可以理解成: + +```text +[batch_size, sequence_length, vocab_size] +``` + +每个位置都在预测下一个 token 的概率分布。 + +## 10. Trainer + +```python +trainer = Trainer( + model=model, + args=training_args, + train_dataset=lm_dataset["train"], + eval_dataset=lm_dataset["test"], + data_collator=data_collator, + processing_class=tokenizer, +) + +trainer.train() +``` + +用途: + +- 管理训练循环。 +- 负责 batch、forward、loss、backward、optimizer、eval、checkpoint。 + +底层逻辑: + +Trainer 是工程封装。它不改变机器学习问题本身,只是帮你少写训练循环。 + +## 如果没有参考资料,怎么自己写出来? + +按这条推理链: + +1. 我想训练什么任务? + - text generation。 + +2. text generation 对应什么训练目标? + - causal LM / next-token prediction。 + +3. 模型需要吃什么? + - token ids、attention mask、labels。 + +4. 原始数据是什么? + - 人类文本,不是 token。 + +5. 所以第一步必须做什么? + - tokenizer。 + +6. Causal LM 的 labels 是什么? + - 原始 input_ids 的 copy,shift 在模型内部处理。 + +7. 训练 batch 怎么构造? + - 把 token 切成固定 block,再 padding/collate。 + +8. 用什么模型头? + - AutoModelForCausalLM。 + +9. 用什么训练循环? + - Trainer 或自己写 PyTorch loop。 + +这就是背后的底层逻辑。不是“记住 API”,而是从任务目标倒推出数据形状、模型头和训练循环。 + +## 当前代码里值得调整的点 + +- `from numpy import block` 没有必要,容易和 `block_size` 概念混淆。 +- 本地脚本里不建议一开始就 `notebook_login()` 和 `push_to_hub=True`。 +- `block_size = 128` 是为了轻量实验,不是模型上限。 +- 后续应先做一个“只跑 preprocessing、不训练”的观察脚本,打印每一步数据形状。 + +## 下一步 + +不要马上完整训练。 + +先把当前脚本拆成三个可观察阶段: + +1. `inspect_dataset.py`:看原始 ELI5 样本结构。 +2. `inspect_tokenization.py`:看 tokenizer 输出。 +3. `inspect_lm_blocks.py`:看 group_texts 后的 block 和 labels。 + +确认这些都看懂,再进入 Trainer。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/03-trainer-train-under-the-hood.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/03-trainer-train-under-the-hood.md new file mode 100644 index 0000000..170a0a5 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/03-trainer-train-under-the-hood.md @@ -0,0 +1,312 @@ +# Trainer.train 背后发生了什么? + +本 guide 接在 `casual_language_model.ipynb` 后面。你已经跑通了: + +```text +dataset -> tokenizer -> group_texts -> data_collator -> Trainer.train -> evaluate -> generate +``` + +现在要补的是最关键的黑箱: + +```text +Trainer.train() 到底在一次训练 step 里做了什么? +``` + +目标不是读完整个 Trainer 源码,而是把一条 batch 的张量流动看清楚。看清这一层后,后面学 LoRA / SFT / DPO 时,你会知道它们本质上是在改哪一部分:数据、模型参数、loss、optimizer,还是训练循环外壳。 + +## 先建立心智模型 + +`Trainer.train()` 可以先简化理解为: + +```text +for batch in train_dataloader: + model.train() + batch = move_to_device(batch) + outputs = model(**batch) + loss = outputs.loss + loss.backward() + optimizer.step() + scheduler.step() + optimizer.zero_grad() + + if should_log: + log(loss) + if should_eval: + evaluate() + if should_save: + save_checkpoint() +``` + +Hugging Face Trainer 真实源码会处理更多东西,比如: + +- device placement +- gradient accumulation +- mixed precision +- distributed training +- logging / eval / save +- hub push +- callback + +但这条主线没有变: + +```text +batch -> model forward -> loss -> backward -> optimizer step +``` + +## 当前 notebook 里的角色分工 + +你的 notebook 中,几个对象分别负责: + +| 对象 | 它负责什么 | 不是负责什么 | +| --- | --- | --- | +| `lm_dataset` | 存放已经切成 block 的 token 样本 | 不负责转 tensor,不负责训练 | +| `data_collator` | 把多条样本组 batch,必要时 padding,准备 labels | 不负责 forward/backward | +| `model` | 接收 `input_ids / attention_mask / labels`,输出 `loss / logits` | 不负责遍历数据集 | +| `TrainingArguments` | 配置训练循环的行为 | 不负责模型数学本身 | +| `Trainer` | 把 dataset、collator、model、args 组装成训练循环 | 不改变 Causal LM 的训练目标 | + +一句话: + +```text +Dataset 提供样本,Collator 打包 batch,Model 计算 loss,Trainer 驱动循环。 +``` + +## 实验 1:观察 Trainer 取出来的一个 batch + +在 notebook 训练 cell 后面新增: + +```python +batch = next(iter(trainer.get_train_dataloader())) + +print(batch.keys()) +print("input_ids:", batch["input_ids"].shape, batch["input_ids"].dtype) +print("attention_mask:", batch["attention_mask"].shape, batch["attention_mask"].dtype) +print("labels:", batch["labels"].shape, batch["labels"].dtype) +print("device before forward:", batch["input_ids"].device) +``` + +你应该看到类似: + +```text +dict_keys(['input_ids', 'attention_mask', 'labels']) +input_ids: torch.Size([16, 128]) +attention_mask: torch.Size([16, 128]) +labels: torch.Size([16, 128]) +``` + +这说明: + +- `16` 来自 `per_device_train_batch_size=16`。 +- `128` 来自 `block_size=128`。 +- `labels` 和 `input_ids` shape 一样,因为 Causal LM 的 shift 发生在模型/loss 内部。 + +如果你看到 batch 仍在 CPU 上,不奇怪。Trainer 在正式训练 step 里会通过 `_prepare_inputs` 把它移动到目标 device。手动 forward 时需要自己移动。 + +## 实验 2:手动做一次 model forward + +继续新增: + +```python +model_device = next(model.parameters()).device +print("model device:", model_device) + +batch_on_device = { + k: v.to(model_device) + for k, v in batch.items() +} + +outputs = model(**batch_on_device) + +print("loss:", outputs.loss) +print("logits:", outputs.logits.shape) +``` + +你应该重点观察: + +```text +loss: 一个标量 +logits: [batch_size, sequence_length, vocab_size] +``` + +对你当前设置,大概是: + +```text +logits: [16, 128, 50257] +``` + +这里的 `50257` 是 GPT-2 词表大小。含义是: + +```text +每个 batch + 每个 token 位置 + 都输出一个“下一个 token 是词表里每个 token 的分数” +``` + +所以 `logits[0, 0, :]` 表示: + +```text +第 1 条样本,第 1 个位置,对整个词表的预测分数 +``` + +## 实验 3:理解 labels 为什么等于 input_ids + +打印一小段 token: + +```python +row = 0 +ids = batch["input_ids"][row] +labels = batch["labels"][row] + +print("input ids first 12:", ids[:12].tolist()) +print("labels first 12:", labels[:12].tolist()) +print("same?", torch.equal(ids, labels)) + +print("input text:") +print(tokenizer.decode(ids[:24])) +``` + +你会再次看到: + +```text +same? True +``` + +但训练目标不是“复制输入”。GPT2 源码说明了 `labels` 会在模型/loss 内部 shift。当前 Transformers 版本中: + +- `GPT2LMHeadModel.forward(...)` 接收 `labels`,并说明可以设置 `labels = input_ids`。 +- `ForCausalLMLoss(...)` 会把 labels 右移:让 token `< n` 预测 token `n`。 + +可理解为: + +```text +input_ids: A B C D +labels: A B C D + +实际 loss: +预测位置: A B C +目标 token: B C D +``` + +所以 `labels = input_ids.copy()` 是接口约定,不是训练目标本身。真正的目标仍然是 next-token prediction。 + +## 实验 4:手动看一个位置在预测什么 + +先取第一个样本第一个位置的 logits: + +```python +pos = 0 +next_token_scores = outputs.logits[row, pos] +top = next_token_scores.topk(10) + +print("context:") +print(tokenizer.decode(batch["input_ids"][row, : pos + 1])) + +print("real next token:") +print(tokenizer.decode([batch["labels"][row, pos + 1].item()])) + +print("model top predictions:") +for token_id, score in zip(top.indices.tolist(), top.values.tolist()): + print(repr(tokenizer.decode([token_id])), score) +``` + +这个实验的意义: + +- `context` 是模型当前位置能看到的上下文。 +- `real next token` 是 loss 希望它预测的答案。 +- `model top predictions` 是模型当前最相信的候选 token。 + +你会直观看到:Causal LM 不是一次生成整段文本,而是在每个位置做一次词表分类。 + +## 实验 5:把 Trainer.training_step 对应回来 + +源码锚点来自当前 demo venv: + +```text +.venv/lib/python3.11/site-packages/transformers/trainer.py +``` + +关键路径: + +```text +training_step(...) + -> model.train() + -> inputs = self._prepare_inputs(inputs) + -> loss = self.compute_loss(model, inputs, ...) + -> self.accelerator.backward(loss) +``` + +`compute_loss(...)` 的关键路径: + +```text +outputs = model(**inputs) +loss = outputs["loss"] or outputs[0] +``` + +也就是说,默认情况下,Trainer 并没有自己发明一个 Causal LM loss。它把 batch 传给 model,model 返回 loss,Trainer 拿这个 loss 做 backward。 + +对你的 notebook 来说: + +```text +batch["input_ids"] +batch["attention_mask"] +batch["labels"] + -> model(**batch) + -> outputs.loss + -> backward +``` + +## 可选实验:看 backward 是否真的产生梯度 + +这一步会改变当前 `model` 的梯度状态,但不会自动更新参数。只用来观察: + +```python +model.zero_grad() +outputs = model(**batch_on_device) +outputs.loss.backward() + +param = model.transformer.wte.weight +print(param.grad is None) +print(param.grad.norm()) + +model.zero_grad() +``` + +如果看到 `param.grad.norm()` 有值,说明: + +```text +loss 已经沿着计算图反传到了 embedding 参数 +``` + +Trainer 在正式训练时会在 backward 之后再做: + +```text +optimizer.step() +scheduler.step() +optimizer.zero_grad() +``` + +这就是模型参数真正被更新的地方。 + +## 这一步要形成的理解 + +完成这份 guide 后,你应该能说清楚: + +1. `Trainer.train()` 不是魔法,而是训练循环封装。 +2. `DataCollatorForLanguageModeling` 输出的是模型可吃的 batch。 +3. `AutoModelForCausalLM` 接收 `labels` 后会返回 `loss`。 +4. `logits` 的形状是 `[batch, seq_len, vocab_size]`,表示每个位置对整个词表的预测。 +5. `labels == input_ids` 是 Causal LM 的接口形态,真正训练目标是 shift 后的 next-token prediction。 +6. 训练变慢、生成跑偏、perplexity 不等于生成质量,这些都能从训练循环和目标函数里找到原因。 + +## 下一步 + +完成这几个观察后,再进入 Chapter 2 的 pipeline 拆解: + +```text +pipeline("text-generation") + -> tokenizer(prompt, return_tensors="pt") + -> model.generate(...) + -> tokenizer.decode(...) +``` + +到那一步,你会把“训练时的 forward/loss”和“推理时的 generate/decode”区分开。这个区分非常重要:训练是在学习参数,推理是在用参数生成 token。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/04-sequence-classification-derivation.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/04-sequence-classification-derivation.md new file mode 100644 index 0000000..29fa31d --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/04-sequence-classification-derivation.md @@ -0,0 +1,880 @@ +# Sequence Classification:从任务契约反推代码 + +## 学习现场 + +用户已完成 `transformer-work/sequence_classification.ipynb`,大致理解官方 recipe 的流程,但仍觉得整体印象模糊,只能按教程抄,无法自己推导。 + +本 guide 的目标不是再解释一遍每行 API,而是把 sequence classification 变成一套可反推的任务契约: + +```text +text + -> tokenizer + -> dynamic padding batch + -> sequence classification model + -> logits + -> label / metric +``` + +## 核心诊断 + +现在的卡点不是“代码没跑通”,而是还没有建立这条推导链: + +```text +任务需要什么输出? +-> 模型 forward 必须产出什么? +-> forward 需要什么输入 tensor? +-> batch 怎么构造? +-> 单条样本应该先怎么 preprocess? +-> 原始 dataset 需要提供哪些字段? +``` + +抄教程是正向执行: + +```text +load dataset -> tokenizer -> collator -> metric -> model -> Trainer +``` + +自己推导要反过来: + +```text +classification objective -> logits/loss -> model inputs -> batch -> tokenized sample -> raw data +``` + +## 任务契约 + +Sequence classification 的任务契约是: + +```text +输入:一段文本 +输出:一个类别 +``` + +在 IMDb 实验中: + +```text +输入:movie review text +输出:0/1 sentiment label +``` + +所以模型最终不需要为每个 token 都输出一个类别,也不需要生成下一段文本。它只需要对整段 sequence 输出一个分类分布: + +```text +logits.shape == [batch_size, num_labels] +``` + +IMDb 是二分类,因此: + +```text +num_labels = 2 +logits.shape == [batch_size, 2] +``` + +## 从输出反推每个对象 + +### 1. 为什么需要 `id2label` / `label2id` + +模型内部只认识数字类别: + +```text +0, 1 +``` + +人需要读懂语义: + +```text +NEGATIVE, POSITIVE +``` + +所以需要: + +```text +id2label = {0: "NEGATIVE", 1: "POSITIVE"} +label2id = {"NEGATIVE": 0, "POSITIVE": 1} +``` + +它们不负责训练算法,只负责让模型 config、推理输出和 Hub artifact 带上可读标签语义。 + +### 2. 为什么需要 `AutoModelForSequenceClassification` + +base DistilBERT 只负责把 token 序列编码成 hidden states。分类任务还需要一个 classification head,把整段文本的表示变成类别 logits。 + +因此要用: + +```python +AutoModelForSequenceClassification.from_pretrained( + checkpoint, + num_labels=2, + id2label=id2label, + label2id=label2id, +) +``` + +它的 forward 契约可以先记成: + +```text +input_ids + attention_mask + labels + -> loss + logits +``` + +其中: + +```text +labels.shape == [batch_size] +logits.shape == [batch_size, num_labels] +loss 是 batch 上的 classification loss +``` + +### 3. 为什么 tokenizer 只做 `truncation=True` + +原始文本是字符串,模型不能直接吃字符串。tokenizer 负责: + +```text +text -> input_ids / attention_mask +``` + +这里使用: + +```python +tokenizer(examples["text"], truncation=True) +``` + +`truncation=True` 是因为 DistilBERT 有最大输入长度。超过模型上限的文本必须截断,否则模型无法处理。 + +这里暂时不在 preprocess 阶段做全量 padding,因为不同 review 长度差异很大。过早 padding 到最大长度会浪费大量计算。 + +### 4. 为什么需要 `DataCollatorWithPadding` + +Dataset 中每条样本 token 长度不同: + +```text +sample A: 42 tokens +sample B: 183 tokens +sample C: 17 tokens +``` + +但模型 batch 需要规整 tensor: + +```text +input_ids.shape == [batch_size, seq_len] +attention_mask.shape == [batch_size, seq_len] +``` + +所以 collator 在“组成 batch 的一刻”动态 padding: + +```text +当前 batch 最长是 183 +-> 这个 batch 内全部 pad 到 183 +``` + +这比把整个 dataset 都 pad 到模型最大长度更省显存和计算。 + +### 5. 为什么需要 `compute_metrics` + +模型 forward 给的是 logits,不是最终 accuracy。 + +所以评测要把: + +```text +logits -> argmax -> predicted label id +``` + +再和真实 label 比较: + +```python +predictions = np.argmax(predictions, axis=1) +accuracy.compute(predictions=predictions, references=labels) +``` + +如果 logits shape 是 `[batch_size, 2]`,`axis=1` 的含义就是在两个类别维度上取分数最高的类。 + +### 6. 为什么最后交给 `Trainer` + +到这里,组件已经齐了: + +```text +model:知道怎么 forward、loss、save +training_args:知道 batch size、lr、epoch、eval/save 策略 +train/eval dataset:提供样本 +data_collator:把样本组 batch +compute_metrics:把 logits 转 metric +processing_class/tokenizer:保存和推理时需要同一套 tokenizer +``` + +`Trainer` 的职责不是定义任务本身,而是把训练循环接起来: + +```text +sample -> collator -> batch -> model forward -> loss +-> backward -> optimizer step -> eval -> save / push +``` + +## `TrainingArguments` 是训练控制面板 + +`TrainingArguments` 不定义模型结构,也不定义样本怎么 tokenize。它更像 Trainer 的训练控制面板,回答这些问题: + +```text +训练多久? +每次吃多少数据? +学习率多大? +什么时候评估? +什么时候保存? +要不要把模型推到 Hub? +``` + +当前官方 recipe 使用: + +```python +training_args = TrainingArguments( + output_dir="my_awesome_imdb_beat-model", + learning_rate=2e-5, + per_device_train_batch_size=16, + per_device_eval_batch_size=16, + num_train_epochs=2, + weight_decay=0.01, + eval_strategy="epoch", + save_strategy="epoch", + load_best_model_at_end=True, + push_to_hub=True, +) +``` + +### 参数含义 + +`output_dir` 是本地输出目录。checkpoint、最终模型和训练状态会写到这里;如果 `push_to_hub=True`,它还会和 Hugging Face Hub repo 发生关联。 + +`learning_rate=2e-5` 是优化器更新参数时的步长。fine-tuning BERT / DistilBERT 这类预训练模型时,常见量级是 `1e-5` 到 `5e-5`。太大可能破坏预训练能力,太小会让学习变慢。 + +`per_device_train_batch_size=16` 表示每个设备每个 training step 处理 16 条训练样本。单机 M 芯片上,可以先近似理解为: + +```text +每 step 训练 16 条 review +``` + +`per_device_eval_batch_size=16` 表示评估时每个 batch 也是 16 条。评估没有 backward,通常比训练便宜,但完整扫一遍 test set 仍然会花时间。 + +`num_train_epochs=2` 表示完整扫训练集 2 遍。IMDb train 约 25000 条样本,如果 batch size 是 16: + +```text +steps_per_epoch = ceil(25000 / 16) ≈ 1563 +total_steps ≈ 1563 * 2 = 3126 +``` + +所以官方 recipe 对学习实验来说偏重;它更像一个完整示例,不是最小观察 run。 + +`weight_decay=0.01` 是正则化,用来抑制权重过大,降低过拟合风险。它主要服务于泛化稳定性,不是加速参数。 + +`eval_strategy="epoch"` 表示每个 epoch 结束后完整评估一次。IMDb test 约 25000 条样本,所以每次 eval 也要扫很多 batch。 + +`save_strategy="epoch"` 表示每个 epoch 结束后保存 checkpoint。保存本身有 IO 成本;如果结合 Hub push,还可能带来上传成本。 + +`load_best_model_at_end=True` 表示训练结束后加载评估表现最好的 checkpoint,而不是最后一步的模型。正式实验最好显式补上: + +```python +metric_for_best_model="accuracy" +``` + +这样 best model 的选择标准更清楚。 + +`push_to_hub=True` 表示训练产物会推到 Hugging Face Hub。学习阶段建议先关闭,训练完成、确认结果后再手动: + +```python +trainer.push_to_hub() +``` + +这样可以把训练耗时和上传耗时分开观察。 + +### 训练量怎么估算 + +训练慢不只由样本数决定,还和序列长度、训练步数、模型规模有关: + +```text +训练成本 ≈ steps * batch_size * avg_sequence_length * model_size +``` + +当前 recipe 慢,主要是这些因素叠加: + +```text +完整 IMDb train:约 25000 条 +完整 IMDb test:约 25000 条 +2 epochs:约 3126 个训练 step +每个 epoch eval/save/push:额外评估、保存、上传成本 +IMDb review 较长:很多样本可能接近 512 tokens +``` + +可调训练量有三层: + +```text +样本数:select(range(...)) +训练步数:max_steps / num_train_epochs +序列长度:max_length +``` + +### 推荐一:机制观察 run + +目标是快速看懂链路,不追求最终效果。 + +```python +def preprocess_function(examples): + return tokenizer(examples["text"], truncation=True, max_length=256) + +tokenized_imdb = imdb.map(preprocess_function, batched=True) + +small_train = tokenized_imdb["train"].shuffle(seed=42).select(range(1000)) +small_eval = tokenized_imdb["test"].shuffle(seed=42).select(range(300)) + +training_args = TrainingArguments( + output_dir="tmp-imdb-debug", + learning_rate=2e-5, + per_device_train_batch_size=16, + per_device_eval_batch_size=32, + max_steps=50, + eval_strategy="steps", + eval_steps=25, + save_strategy="no", + load_best_model_at_end=False, + push_to_hub=False, + logging_steps=10, + dataloader_pin_memory=False, + report_to="none", +) +``` + +这里最关键的是: + +```python +max_steps=50 +``` + +`max_steps` 为正数时会覆盖 `num_train_epochs`。这适合当前阶段:先让用户观察 `batch -> forward -> loss/logits -> metric`,不要被完整训练耗时打断学习节奏。 + +### 推荐二:小型学习 run + +目标是看到 accuracy 变化,同时保留 eval / save / best model 的体验。 + +```python +def preprocess_function(examples): + return tokenizer(examples["text"], truncation=True, max_length=256) + +tokenized_imdb = imdb.map(preprocess_function, batched=True) + +small_train = tokenized_imdb["train"].shuffle(seed=42).select(range(5000)) +small_eval = tokenized_imdb["test"].shuffle(seed=42).select(range(1000)) + +training_args = TrainingArguments( + output_dir="imdb-small-run", + learning_rate=2e-5, + per_device_train_batch_size=16, + per_device_eval_batch_size=32, + max_steps=300, + eval_strategy="steps", + eval_steps=100, + save_strategy="steps", + save_steps=100, + save_total_limit=2, + load_best_model_at_end=True, + metric_for_best_model="accuracy", + push_to_hub=False, + logging_steps=20, + dataloader_pin_memory=False, + report_to="none", +) +``` + +这档适合在理解主链路后运行一次,观察: + +```text +training loss 有没有下降 +eval accuracy 有没有变化 +checkpoint 如何保存 +best model 如何选择 +``` + +### 推荐三:接近官方完整 run + +目标是得到一个更完整的 IMDb fine-tuned model。 + +```python +training_args = TrainingArguments( + output_dir="my_awesome_imdb_beat-model", + learning_rate=2e-5, + per_device_train_batch_size=16, + per_device_eval_batch_size=32, + num_train_epochs=2, + weight_decay=0.01, + eval_strategy="epoch", + save_strategy="epoch", + save_total_limit=2, + load_best_model_at_end=True, + metric_for_best_model="accuracy", + push_to_hub=False, + dataloader_pin_memory=False, + report_to="none", +) +``` + +仍然建议先 `push_to_hub=False`,等训练结果确认后再手动 push。 + +### 当前建议 + +当前阶段使用“机制观察 run”。先缩小样本数、限制 `max_length`、使用 `max_steps`、关闭 save 和 Hub push。等可以自己解释 TrainingArguments 每个参数改变了什么,再逐步恢复 eval、save、best model 和 Hub artifact。 + +## 保存模型与 `pipeline` 加载排障 + +如果运行: + +```python +classifier = pipeline("sentiment-analysis", model="my_awesome_imdb_beat-model") +``` + +报错: + +```text +ValueError: Unrecognized model in my_awesome_imdb_beat-model. +Should have a `model_type` key in its config.json. +``` + +优先按这个方向理解: + +```text +pipeline 收到一个字符串 model="my_awesome_imdb_beat-model" +-> 如果当前目录下存在同名本地目录,就按本地模型目录加载 +-> AutoConfig 试图读取 config.json,判断模型架构 +-> 目录不是完整 Transformers model artifact +-> 无法识别 model_type,加载失败 +``` + +一个可加载的 Transformers 模型目录通常至少需要: + +```text +config.json # 里面要有 model_type,例如 "distilbert" +model.safetensors # 或 pytorch_model.bin +tokenizer.json # 或 tokenizer 相关文件 +tokenizer_config.json +``` + +当前错误常见原因: + +```text +训练还没跑到保存点。 +save_strategy="no",但没有手动 trainer.save_model()。 +训练被中断,output_dir 只创建了空目录。 +notebook 当前工作目录和预期不同,保存/加载看的不是同一个路径。 +push_to_hub=True 只是配置了 Hub 行为,不等于本地目录已经有完整可加载 artifact。 +``` + +先用这个 probe 判断目录是否完整: + +```python +from pathlib import Path +import json + +save_dir = Path("my_awesome_imdb_beat-model") + +print(save_dir.resolve()) +print([p.name for p in save_dir.iterdir()]) + +config_path = save_dir / "config.json" +print(config_path.exists()) + +if config_path.exists(): + config = json.loads(config_path.read_text()) + print(config.get("model_type")) + print(config.get("architectures")) +``` + +如果目录是空的,或者没有 `config.json` / `model.safetensors` / tokenizer 文件,就不要用它做 `pipeline` 的 `model`。 + +### 修复方式一:直接用内存中的模型 + +如果刚刚训练完,还在同一个 notebook kernel 里,最小修复是直接把 `trainer.model` 交给 pipeline: + +```python +from transformers import pipeline + +classifier = pipeline( + "text-classification", + model=trainer.model, + tokenizer=tokenizer, +) + +classifier("I've been waiting for a Hugging Face course my whole life.") +``` + +这绕过了本地目录加载,适合快速验证 forward / logits / label。 + +### 修复方式二:显式保存后再从目录加载 + +如果想验证“保存 artifact -> 重新加载 -> 推理”的链路,就显式保存: + +```python +save_dir = "my_awesome_imdb_beat-model" + +trainer.save_model(save_dir) +tokenizer.save_pretrained(save_dir) + +classifier = pipeline( + "text-classification", + model=save_dir, + tokenizer=save_dir, +) + +classifier("I've been waiting for a Hugging Face course my whole life.") +``` + +这一步要确认 `save_dir` 里至少有: + +```text +config.json +model.safetensors +tokenizer.json +tokenizer_config.json +``` + +### 修复方式三:从 checkpoint 加载 + +如果训练过程已经保存了 checkpoint,可以直接加载 checkpoint: + +```python +checkpoint_dir = "my_awesome_imdb_beat-model/checkpoint-100" + +classifier = pipeline( + "text-classification", + model=checkpoint_dir, + tokenizer=tokenizer, +) +``` + +前提是 checkpoint 目录里有完整模型权重和 config。tokenizer 可以来自原 tokenizer,也可以来自保存了 tokenizer 文件的同一个目录。 + +### 修复方式四:从 Hub repo 加载 + +如果已经成功: + +```python +trainer.push_to_hub() +``` + +并且 Hub 上存在完整模型 repo,则用完整 repo id: + +```python +classifier = pipeline( + "text-classification", + model="hedonwang/my_awesome_imdb_beat-model", +) +``` + +不要把“本地目录名”和“Hub repo id”混为一谈。`model="my_awesome_imdb_beat-model"` 会优先受当前工作目录影响;`model="username/repo_name"` 才是明确从 Hub repo 加载。 + +## 最小观察点 + +回到 notebook 时,只加这些 probe,不急着深入 Trainer 源码。 + +### 原始样本 + +```python +sample = imdb["train"][0] +print(sample.keys()) +print(sample["label"]) +print(sample["text"][:300]) +``` + +要回答: + +```text +原始 dataset 提供了哪些字段? +哪个字段是输入? +哪个字段是监督信号? +``` + +### Tokenized sample + +```python +sample_tok = tokenizer(sample["text"], truncation=True) +print(sample_tok.keys()) +print(len(sample_tok["input_ids"])) +print(tokenizer.decode(sample_tok["input_ids"][:50])) +``` + +要回答: + +```text +tokenizer 新增了什么? +为什么 label 没有被 tokenizer 改掉? +``` + +### Collated batch + +```python +model_input_keys = ["input_ids", "attention_mask", "label"] + +features = [ + {k: v for k, v in tokenized_imdb["train"][i].items() if k in model_input_keys} + for i in range(3) +] + +batch = data_collator(features) + +print(batch.keys()) +print(batch["input_ids"].shape) +print(batch["attention_mask"].shape) +print(batch["labels"].shape) +``` + +要回答: + +```text +collator 的输入是几条 dict-like sample。 +collator 的输出是模型可吃的 tensor batch。 +``` + +如果直接写: + +```python +features = [tokenized_imdb["test"][i] for i in range(3)] +batch = data_collator(features) +``` + +可能会报: + +```text +ValueError: too many dimensions 'str' +Perhaps your features (`text` in this case) have excessive nesting +``` + +原因是 `tokenized_imdb["test"][i]` 里可能还保留了原始 `text` 字符串字段。`data_collator` 的工作是把样本整理成 tensor batch,而字符串 `text` 不能被转成模型输入 tensor。 + +所以手动 probe 时要只保留模型需要的字段: + +```text +input_ids +attention_mask +label +``` + +`DataCollatorWithPadding` 会把 `label` 整理成 batch 里的 `labels`。 + +正式训练时,`Trainer` 通常会根据模型 forward signature 移除无关列;但手动调用 `data_collator(features)` 时,这层自动清理不会替你发生。 + +### Model forward + +```python +small_batch = {k: v.to(model.device) for k, v in batch.items()} +outputs = model(**small_batch) + +print(outputs.loss) +print(outputs.logits.shape) +print(outputs.logits[:3]) +``` + +要回答: + +```text +为什么 logits 第二维是 2? +为什么传 labels 时 outputs 里会有 loss? +``` + +这里的目标不是训练,而是绕开 `Trainer` 和 `pipeline`,直接观察模型本体的 forward 契约: + +```text +batch 里的 input_ids / attention_mask / labels + -> model(**batch) + -> outputs.loss + outputs.logits +``` + +其中: + +```text +outputs.logits.shape == [batch_size, num_labels] +``` + +IMDb 二分类里,`num_labels=2`,所以如果当前 batch 有 3 条样本,就应该看到: + +```text +outputs.logits.shape == [3, 2] +``` + +这一步要建立的直觉是: + +```text +logits 是模型对每个类别打的原始分数。 +loss 是模型拿 logits 和 labels 比较后算出的训练信号。 +``` + +如果没有传 `labels`: + +```python +outputs = model(input_ids=batch["input_ids"], attention_mask=batch["attention_mask"]) +``` + +通常只能得到 logits,没有 supervised loss。因为模型不知道正确答案是什么。 + +### Manual prediction + +```python +pred_ids = outputs.logits.argmax(dim=-1) +print(pred_ids) +print([model.config.id2label[i.item()] for i in pred_ids]) +``` + +要回答: + +```text +pipeline 最终输出的 label,本质上是 logits argmax 再查 id2label。 +``` + +这一步的目标是把 `pipeline` 的后处理拆开。模型只输出 logits,例如: + +```text +sample 0 logits: [-1.2, 2.4] +sample 1 logits: [ 1.8,-0.7] +``` + +每一行对应一条样本,每一列对应一个 label: + +```text +column 0 -> NEGATIVE +column 1 -> POSITIVE +``` + +`argmax(dim=-1)` 就是在每条样本的类别维度上找最高分: + +```text +[-1.2, 2.4] -> 1 -> POSITIVE +[ 1.8,-0.7] -> 0 -> NEGATIVE +``` + +所以 `pipeline("text-classification")` 大致做的是: + +```text +text + -> tokenizer + -> model forward + -> logits + -> argmax / softmax + -> id2label + -> {"label": "...", "score": ...} +``` + +`argmax` 负责选类别,`softmax` 负责把 logits 变成类似置信度的分数。 + +### 如果 4 / 5 不知道做什么 + +先把 4 / 5 当成两个调试镜头: + +```text +4. Model forward:看模型本体吃什么、吐什么。 +5. Manual prediction:看 pipeline 最后的 label 是怎么从 logits 变出来的。 +``` + +最小代码可以直接贴到 notebook 里: + +```python +model_input_keys = ["input_ids", "attention_mask", "label"] + +features = [ + {k: v for k, v in tokenized_imdb["test"][i].items() if k in model_input_keys} + for i in range(3) +] + +batch = data_collator(features) + +batch = {k: v.to(model.device) for k, v in batch.items()} +outputs = model(**batch) + +print("batch keys:", batch.keys()) +print("labels:", batch["labels"]) +print("loss:", outputs.loss) +print("logits shape:", outputs.logits.shape) +print("logits:", outputs.logits) + +pred_ids = outputs.logits.argmax(dim=-1) +pred_labels = [model.config.id2label[i.item()] for i in pred_ids] + +print("id2label:", model.config.id2label) +print("pred ids:", pred_ids) +print("pred labels:", pred_labels) +print("gold labels:", [model.config.id2label[i.item()] for i in batch["labels"]]) +``` + +如果 `pred ids` 是 `0`,但 `pred labels` 显示为 `POSITIVE`,说明当前 `model.config.id2label` 里是: + +```text +0 -> POSITIVE +``` + +这时必须先确认它是否和数据集真实 label 语义一致: + +```python +print(model.config.id2label) +print(model.config.label2id) +print(tokenized_imdb["test"].features["label"]) +print(tokenized_imdb["test"][0]["label"]) +print(tokenized_imdb["test"][0]["text"][:500]) +``` + +如果数据集约定和 `model.config.id2label` 相反,pipeline 输出的文字标签就会反,哪怕模型的数字分类是对的。 + +看完后只回答三句话: + +```text +batch 里有什么? +model forward 输出了什么? +logits 如何变成最终 label? +``` + +## 反推练习 + +做完上面的 probe 后,盖住教程,只保留这个骨架,自己补全代码: + +```python +checkpoint = "distilbert/distilbert-base-uncased" + +# 1. dataset contract + +# 2. tokenizer contract + +# 3. preprocess contract + +# 4. dynamic batch contract + +# 5. metric contract + +# 6. model contract + +# 7. trainer contract + +# 8. inference contract +``` + +每补一段,都问四个问题: + +```text +它的输入是什么? +它的输出是什么? +下游谁依赖它? +删掉它会坏在哪里? +``` + +如果这四个问题答不出来,就说明还在 recipe copying;如果能答出来,就开始进入机制掌握。 + +## 当前边界 + +这次先不展开: + +- `Trainer.train()` 的完整源码。 +- DistilBERT encoder 内部 attention 细节。 +- Hub 上传和 model card 的生产规范。 + +当前只要求建立 sequence classification 的主链路: + +```text +raw text + label + -> tokenized sample + -> padded batch + -> sequence classification logits/loss + -> metric / inference label +``` + +## 官方来源 + +- [Hugging Face Transformers Text classification](https://huggingface.co/docs/transformers/tasks/sequence_classification) +- [Hugging Face Transformers DataCollatorWithPadding](https://huggingface.co/docs/transformers/v5.12.0/en/main_classes/data_collator#transformers.DataCollatorWithPadding) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/05-chapter1-5-task-lab-sweep.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/05-chapter1-5-task-lab-sweep.md new file mode 100644 index 0000000..b079458 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/05-chapter1-5-task-lab-sweep.md @@ -0,0 +1,167 @@ +# Chapter 1/5 Task Lab Sweep:用重复建立任务直觉 + +## 学习现场 + +用户原先希望先把 Hugging Face LLM Course Chapter 1/5 `How Transformers solve tasks` 这一节提到的 task labs 都完成,再进入 Chapter 2 `Behind the pipeline`。2026-07-07 路线收窄:已完成 5 个文本主线任务后,Translation / ASR / Image classification 跳过或延后,先进入 Chapter 1/6 `Transformer Architectures` 做架构归纳。 + +这个判断成立:当前阶段用户不是缺少“更高效的路线”,而是缺少跨任务的手感。前期机械式重复可以服务于模式识别: + +```text +raw input + -> tokenizer / processor / feature extractor + -> model backbone + -> task-specific head / decoder + -> logits / generated ids / spans + -> loss / metric / postprocess + -> human-readable output +``` + +每个 lab 都不要追求完整效果,而是追求“我知道这个任务的输入输出形状和模型头为什么不同”。 + +## 官方页面覆盖的 Labs + +Chapter 1/5 页面里所有 `Ready to try your hand...` 对应的任务实验一共 8 个。它们用同一套 Transformer 视角串起不同输入、架构、head、loss 和 postprocess: + +1. Text generation / causal language modeling:GPT-2 / decoder-only / next-token prediction。 +2. Text classification:BERT / encoder-only / sequence classification head。 +3. Token classification:BERT / encoder-only / per-token classification head。 +4. Question answering:BERT / encoder-only / span start-end logits。 +5. Summarization:T5 / encoder-decoder / seq2seq generation。 +6. Translation:T5 / encoder-decoder / seq2seq generation。 +7. Automatic speech recognition:Whisper / audio encoder + autoregressive decoder。 +8. Image classification:ViT / image patches + `[CLS]` representation + classification head。 + +注意:本 topic 的主线仍是 LoRA / fine-tuning feedback loop。这里不是要转成 audio / CV topic,而是用这些 labs 快速建立“不同任务怎么改输入、head、loss、postprocess”的直觉。 + +## 当前完成情况 + +| Lab | 状态 | 当前证据 | 下一步 | +| --- | --- | --- | --- | +| Text generation / Causal LM | done | `causal_language_model.ipynb`,已跑通 DistilGPT2 quick training run、perplexity、Hub 上传 | 后续只在对比 decoder-only 时回看 | +| Text classification | done | `sequence_classification.ipynb`,已跑通训练、pipeline load、forward/logits probe、闭卷复现 | 后续只在 encoder classification 对比时回看 | +| Token classification | done | `token_classification.ipynb`,已跑通 WNUT 数据加载、label alignment、小步训练、模型保存、`pipeline("ner")`;已记录用户复述 | 后续只在 per-token logits 对比时回看 | +| Question answering | done | `question_answering.ipynb`,已跑通 SQuAD 数据加载、字符答案到 token span 对齐、DistilBERT QA 小样本训练、Hub push,并观察到独立 start/end argmax 的空 span 风险 | 后续只在 QA postprocess / eval 对比时回看 | +| Summarization | done | `summarization.ipynb` 已跑通 BillSum / T5 / ROUGE / Seq2SeqTrainer / checkpoint 推理;已记录 [`2026-07-03-summarization-lab-recap.md`](../../notes/04-lesson-lab/2026-07-03-summarization-lab-recap.md) | 后续作为 encoder-decoder / seq2seq 代表任务回看 | +| Translation | skipped/deferred | 用户决定当前不做 | 如后续补做,只作为 summarization 的 seq2seq 对照 | +| Automatic speech recognition | skipped/deferred | 用户决定当前不做 | 非文本多模态任务不阻塞本 topic | +| Image classification | skipped/deferred | 用户决定当前不做 | 非文本多模态任务不阻塞本 topic | + +## 每个 lab 的统一验收模板 + +每个 lab 都要回答同一组问题,不追求大而全: + +```text +1. 任务输入是什么? +2. 监督信号 / 目标输出是什么? +3. tokenizer / processor 把输入变成了哪些 tensor? +4. batch 里有哪些 key?shape 是什么? +5. model class 是什么?它对应哪种架构:encoder / decoder / encoder-decoder? +6. task-specific head 或 decoder 输出了什么? +7. logits / generated ids / span 如何被 postprocess 成人类可读结果? +8. loss 或 metric 在比较什么? +9. 这个任务和上一个任务最大的结构差异是什么? +``` + +## 统一实验策略 + +前期允许机械重复,但不允许无意识烧时间: + +- 使用小数据集或 `select(range(...))`,优先观察链路。 +- 训练用 `max_steps` 控制成本。 +- 默认 `push_to_hub=False`,确认 artifact 后再手动 push。 +- Notebook 可以作为学习证据,但必须补最小观察输出:batch keys、shape、loss/logits 或 generated output。 +- 不强求每个 lab 都完整 fine-tune 到好效果;先证明任务链路。 +- 对 ASR / image classification 这类非文本任务,先做 inference 或极小样本观察;如果环境成本过高,只记录跳过理由。 + +## 当前排障:Question Answering 数据集 ID + +在 `datasets==5.0.0` + `huggingface_hub==1.19.0` 组合下,课程旧写法: + +```python +load_dataset("squad", split="train[:5000]") +``` + +会报: + +```text +HfUriError: Invalid HF URI 'hf://datasets/squad@.../.huggingface.yaml'. +Repository id must be 'namespace/name', got 'squad'. +``` + +原因不是 SQuAD 数据集缺失,而是新版本 `huggingface_hub` 的 `hf://` 解析不再接受单段 repo id。`HfApi.dataset_info("squad")` 会解析到同一个数据集仓库 `rajpurkar/squad`,commit hash 与报错中的 revision 一致。当前 notebook 应使用: + +```python +squad = load_dataset("rajpurkar/squad", split="train[:5000]") +``` + +已验证 `load_dataset("rajpurkar/squad", split="train[:2]")` 能生成 `id/title/context/question/answers` 字段。继续 lab 时,先打印一个样本和 batch shape,再进入 tokenizer offset mapping、`start_positions/end_positions`、`start_logits/end_logits` 和 span extraction。 + +如果下一格出现: + +```text +AttributeError: 'DatasetDict' object has no attribute 'train_test_split' +``` + +先检查 `type(squad)`。`load_dataset(..., split="train[:5000]")` 返回单个 `Dataset`,可以调用 `.train_test_split()`;`load_dataset(...)` 不带 `split` 会返回已经包含 `train/validation` 的 `DatasetDict`,此时不能再对整个对象 split。为了避免 Jupyter 旧内存状态污染,推荐在 notebook 里分开命名: + +```python +squad_raw = load_dataset("rajpurkar/squad", split="train[:5000]") +squad = squad_raw.train_test_split(test_size=0.2) +``` + +## 已完成:Token Classification + +Token classification 和 text classification 最像,重复成本低,但能暴露一个重要差异: + +```text +Text classification: + one sequence -> one label + logits.shape == [batch_size, num_labels] + +Token classification: + one sequence -> one label per token + logits.shape == [batch_size, seq_len, num_labels] +``` + +单项 guide:[`06-token-classification-derivation.md`](06-token-classification-derivation.md)。本 sweep 文件只记录横向路线;token classification 的任务契约、label alignment、排障和验收细节都放在独立 guide 中。 + +已跑通并观察: + +```text +dataset sample +-> tokenizer + label alignment +-> collated batch +-> model forward +-> logits shape +-> token-level predictions +``` + +特别关注: + +- 为什么 label 需要和 token 对齐。 +- 为什么 subword token 让 label alignment 变复杂。 +- 为什么 `attention_mask` 不等于 label mask。 +- 为什么 token classification 的输出比 sequence classification 多一个 `seq_len` 维度。 + +## 下一步进入 Chapter 1/6 的理由 + +Chapter 2 `Behind the pipeline` 不是取消,而是后移。当前先进入 Chapter 1/6 `Transformer Architectures`,因为已经完成的 5 个文本任务足够支撑一个更重要的归纳: + +```text +任务不是只换 pipeline 名字 +而是围绕输入理解方式、attention 可见范围、head / decoder 和输出形态选择架构 +``` + +也就是说,先把已跑过的任务映射到 encoder-only、decoder-only、encoder-decoder,再拆 pipeline,会更有感觉。Translation / ASR / Image classification 后续只有在需要补 seq2seq 或多模态直觉时再恢复。 + +## 来源 + +- [Hugging Face LLM Course Chapter 1/5: How Transformers solve tasks](https://huggingface.co/learn/llm-course/en/chapter1/5) +- [Causal language modeling task guide](https://huggingface.co/docs/transformers/tasks/language_modeling#causal-language-modeling) +- [Text classification task guide](https://huggingface.co/docs/transformers/tasks/sequence_classification) +- [Token classification task guide](https://huggingface.co/docs/transformers/tasks/token_classification) +- [Question answering task guide](https://huggingface.co/docs/transformers/tasks/question_answering) +- [Summarization task guide](https://huggingface.co/docs/transformers/tasks/summarization) +- [Translation task guide](https://huggingface.co/docs/transformers/tasks/translation) +- [Automatic speech recognition task guide](https://huggingface.co/docs/transformers/tasks/asr) +- [Image classification task guide](https://huggingface.co/docs/transformers/tasks/image_classification) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/06-token-classification-derivation.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/06-token-classification-derivation.md new file mode 100644 index 0000000..1fc0b2e --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/06-token-classification-derivation.md @@ -0,0 +1,195 @@ +# Token Classification Derivation:从任务契约推导代码 + +## 本节定位 + +Token classification 是 Chapter 1/5 task lab sweep 的第三个 lab。它和 sequence classification 很像,都是 encoder-only BERT 类模型,但监督信号从“一整句一个标签”变成“每个 token 一个标签”。 + +本节不要从 `Trainer` 开始理解,而要从任务契约开始: + +```text +输入:一句已经按词切好的句子 tokens +目标:给每个原始词标一个 NER 标签 ner_tags +难点:tokenizer 会把一个词拆成多个 subword,还会加 [CLS] / [SEP] +处理:把原始 word-level label 对齐到 tokenizer 之后的 token 序列 +模型:DistilBERT backbone + token classification head +输出:每个 token 位置都有一组实体类别 logits +后处理:忽略 -100 位置,把 token 预测合并成人类可读的实体片段 +``` + +## 和 Text Classification 的差异 + +```text +Text classification: + one sequence -> one label + logits.shape == [batch_size, num_labels] + +Token classification: + one sequence -> one label per token + logits.shape == [batch_size, seq_len, num_labels] +``` + +核心差异不是训练 API,而是监督信号的粒度: + +```text +Text classification: + [CLS] / sequence representation -> one logits vector -> one label + +Token classification: + every token hidden state -> one logits vector per token -> one label per token +``` + +## 推导顺序 + +### 1. 原始样本 + +WNUT 样本提供的是 word-level 标注: + +```text +tokens: ["@paulwalk", "It", "'s", ...] +ner_tags: [0, 0, 0, ...] +``` + +这里 `tokens[i]` 和 `ner_tags[i]` 一一对应。此时标签坐标系是“原始词”。 + +### 2. Tokenizer 改变坐标系 + +`tokenizer(..., is_split_into_words=True)` 会保留“输入已经按词切好”的信息,但输出仍然是模型需要的 token 序列: + +```text +["@paulwalk"] -> ["@", "paul", "##walk"] +``` + +同时 tokenizer 还会加入 `[CLS]`、`[SEP]` 等 special tokens。因此 tokenizer 后的 token 数量通常大于原始词数量。 + +这就是 token classification 多出来的核心问题:原始标签还在 word-level,但模型训练需要 token-level labels。 + +### 3. Label Alignment + +`tokenized_inputs.word_ids(batch_index=i)` 会告诉你 tokenizer 后每个 token 来自第几个原始词: + +```text +token: [CLS] @ paul ##walk it ... +word_id: None 0 0 0 1 ... +``` + +对齐策略是: + +```text +special token -> -100 +一个词的第一个 token -> 原始词标签 +同一个词拆出来的后续 subword -> -100 +``` + +`-100` 不是实体类别,而是 loss 的忽略标记。它告诉训练过程:这个位置不要参与监督。 + +注意:`attention_mask` 和 `labels == -100` 不是一回事。 + +```text +attention_mask: 告诉模型哪些位置是 padding +labels == -100: 告诉 loss 哪些位置不计算监督误差 +``` + +### 4. Collator + +`DataCollatorForTokenClassification` 的职责是把不同长度的样本 padding 成一个 batch,并让 `input_ids`、`attention_mask`、`labels` 一起对齐。 + +必须观察: + +```text +batch.keys() +batch["input_ids"].shape +batch["attention_mask"].shape +batch["labels"].shape +``` + +预期三者前两个维度一致: + +```text +[batch_size, seq_len] +``` + +### 5. Model Forward + +`AutoModelForTokenClassification` 使用 DistilBERT backbone,但分类 head 作用在每个 token hidden state 上。 + +预期输出: + +```text +outputs.loss +outputs.logits.shape == [batch_size, seq_len, num_labels] +``` + +这就是本 lab 和 sequence classification 最关键的结构差异。 + +### 6. Metric 与 Postprocess + +`compute_metrics` 先对 logits 做: + +```text +argmax over num_labels +``` + +然后过滤掉 `labels == -100` 的位置,只把真实监督过的位置交给 `seqeval`。 + +`pipeline("ner")` 会再做一层人类可读 postprocess:把 token-level 的预测合并成实体片段。 + +## 当前 Notebook 的坑位 + +### 数据集加载 + +课程示例里的 `load_dataset("wnut_17")` 依赖旧式 dataset loading script;在 `datasets>=4.0.0` 中会报: + +```text +Dataset scripts are no longer supported, but found wnut_17.py +``` + +当前 lab 使用 Parquet 转换版: + +```python +wnut = load_dataset("flaitenberger/wnut_17") +``` + +它保留 `id`、`tokens`、`ner_tags` 字段,足够继续观察 label alignment 和 token-level logits。 + +### `word_ids` 变量引用 + +`word_ids(batch_index=i)` 必须从当前 batch 的 `tokenized_inputs` 取: + +```python +word_ids = tokenized_inputs.word_ids(batch_index=i) +``` + +如果误用单条样本的 `tokenized_input`,`batched=True` 时会在第 2 条样本开始触发: + +```text +IndexError: list index out of range +``` + +### 本地 Pipeline 加载 + +`TrainingArguments(output_dir=...)` 只是指定训练输出目录,不等于已经保存了一个可被 `pipeline` 加载的模型。 + +如果没有产生 checkpoint 或没有显式保存,`pipeline(..., model=output_dir)` 会因为缺少 `config.json`、权重或 tokenizer 文件而失败。 + +训练结束后需要保存: + +```python +trainer.save_model("my_awesome_wnut_model") +tokenizer.save_pretrained("my_awesome_wnut_model") +``` + +## 最小验收 + +本 lab 完成时,用户应能闭卷解释: + +```text +raw tokens / ner_tags +-> tokenizer changes the token coordinate system +-> word_ids maps model tokens back to original words +-> labels use -100 to ignore special tokens and extra subwords +-> collator pads input_ids / attention_mask / labels together +-> model outputs [batch_size, seq_len, num_labels] +-> seqeval and pipeline filter / merge token-level predictions +``` + +不要把跑通训练等同于完成。完成证据应该来自用户能说清:为什么 token classification 比 sequence classification 多了 label alignment,以及为什么 logits 多出 `seq_len` 维。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/07-question-answering-derivation.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/07-question-answering-derivation.md new file mode 100644 index 0000000..42fb93a --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/07-question-answering-derivation.md @@ -0,0 +1,313 @@ +# Question Answering Derivation:从字符答案推导 token span + +## 本节定位 + +Question answering 是 Chapter 1/5 task lab sweep 的第四个 lab。它仍然是 encoder-only BERT 类模型,但监督信号不再是整句标签或每个 token 的类别,而是答案在 context 里的起止位置。 + +本节的核心不是 `Trainer`,而是理解 `preprocess_function` 为什么要把 SQuAD 的字符级答案转成 token 级 span: + +```text +输入:question + context +原始目标:answers.text + answers.answer_start,即答案在 context 字符串里的字符区间 +tokenizer:把 question/context 拼成一条 token 序列,并记录每个 token 对应的原文字符区间 +训练目标:start_positions / end_positions,即答案开始和结束 token 的索引 +模型输出:start_logits / end_logits,每个 token 各有一个“适合作为答案起点/终点”的分数 +后处理:取 start/end 分数最高的 token span,再 decode 回文本答案 +``` + +## 和前两个分类任务的差异 + +```text +Sequence classification: + one sequence -> one label + logits.shape == [batch_size, num_labels] + +Token classification: + one sequence -> one label per token + logits.shape == [batch_size, seq_len, num_labels] + +Question answering: + question + context -> two token positions + start_logits.shape == [batch_size, seq_len] + end_logits.shape == [batch_size, seq_len] +``` + +QA 的 head 不是给每个 token 分实体类别,而是给每个 token 两种位置分数:它能不能做答案起点,它能不能做答案终点。 + +## 为什么有些训练需要 `compute_metrics` + +`compute_metrics` 不是训练 loss 的来源。只要 batch 里有模型认识的 label 字段,模型 forward 就能自己算 loss: + +```text +Sequence classification: labels -> cross entropy loss +Token classification: labels -> token-level cross entropy loss, 忽略 -100 +Question answering: start_positions / end_positions -> start/end loss +``` + +`Trainer.train()` 优化的是这个 loss,所以很多最小训练脚本不写 `compute_metrics` 也能训练。 + +`compute_metrics` 的职责是在 `evaluate()` 或训练中的 eval step 里,把模型输出翻译成人类关心的指标: + +```text +model outputs + labels -> accuracy / F1 / seqeval / exact match +``` + +为什么有些任务会写,有些不写,主要看 metric 是否容易从 logits 直接算出来: + +| Task | Loss 是否需要 `compute_metrics` | Metric 难度 | 常见做法 | +| --- | --- | --- | --- | +| Text classification | 不需要 | 简单:`argmax(logits)` 对比 label | 常写 `compute_metrics` 算 accuracy/F1 | +| Token classification | 不需要 | 中等:过滤 `-100`,再转 label name | 常写 `compute_metrics` 算 seqeval | +| Causal LM | 不需要 | 通常直接看 eval loss/perplexity | 可以不写,手动用 eval loss 算 perplexity | +| Question answering | 不需要 | 较复杂:start/end logits -> 合法 span -> decode text -> EM/F1 | 最小训练常不写;严肃评估需要 postprocess 再算 SQuAD metrics | + +所以 QA 里“不写 `compute_metrics`”不代表没有评估,只代表 `Trainer` 默认最多给你 eval loss。要得到 SQuAD 的 exact match / F1,需要额外把 `start_logits/end_logits` 后处理成文本答案,再和 `answers.text` 比较。 + +## `preprocess_function` 做了什么 + +### 1. 清理 question + +```python +questions = [q.strip() for q in examples["question"]] +``` + +这一步只是去掉问题前后的空白,避免无意义空格进入 tokenizer。 + +### 2. Tokenize question/context pair + +```python +inputs = tokenizer( + questions, + examples["context"], + max_length=384, + truncation="only_second", + return_offsets_mapping=True, + padding="max_length", +) +``` + +这里 tokenizer 的输入是 pair: + +```text +sequence 0: question +sequence 1: context +``` + +关键参数: + +- `max_length=384`:固定模型输入长度。 +- `truncation="only_second"`:只截断第二段,也就是 context;不要把 question 截掉。 +- `return_offsets_mapping=True`:为每个 token 返回它在原始字符串里的字符起止位置。 +- `padding="max_length"`:直接 padding 到固定长度,后面 `DefaultDataCollator` 不再额外 padding。 + +### 3. 取出 offset mapping + +```python +offset_mapping = inputs.pop("offset_mapping") +``` + +`offset_mapping` 是 preprocessing 的中间工具,不直接喂给模型。它回答的问题是: + +```text +token i 对应原始字符串里的哪一段字符? +``` + +例如某个 context token 对应: + +```text +offset[i] == (423, 435) +``` + +表示这个 token 覆盖 context 字符串的 `[423, 435)` 字符区间。 + +### 4. 原始答案是字符坐标 + +```python +start_char = answer["answer_start"][0] +end_char = answer["answer_start"][0] + len(answer["text"][0]) +``` + +SQuAD 给的是: + +```text +answer_start: 答案在 context 字符串里的起始字符位置 +answer.text: 答案文本 +``` + +所以 `end_char` 是根据 `start_char + len(answer_text)` 算出来的。此时监督信号还在“字符坐标系”。 + +### 5. 找到 context 在 token 序列里的范围 + +```python +sequence_ids = inputs.sequence_ids(i) +``` + +`sequence_ids(i)` 会告诉第 `i` 个样本中每个 token 属于哪一段: + +```text +None -> special token / padding +0 -> question +1 -> context +``` + +所以这两段循环是在找 context 的 token 边界: + +```python +idx = 0 +while sequence_ids[idx] != 1: + idx += 1 +context_start = idx + +while sequence_ids[idx] == 1: + idx += 1 +context_end = idx - 1 +``` + +为什么只在 context 里找?因为答案必须来自 context,不能来自 question,也不能来自 `[CLS]` / `[SEP]` / padding。 + +### 6. 处理答案被截断的情况 + +```python +if offset[context_start][0] > end_char or offset[context_end][1] < start_char: + start_positions.append(0) + end_positions.append(0) +``` + +如果 context 太长被截断,答案可能已经不在保留下来的 context token 范围里。这时官方示例把标签设成 `(0, 0)`,通常对应 `[CLS]` 位置,表示这个切片里没有可学习的答案 span。 + +### 7. 字符坐标转 token 坐标 + +如果答案还在当前 context 范围里,就向右找答案起点 token: + +```python +idx = context_start +while idx <= context_end and offset[idx][0] <= start_char: + idx += 1 +start_positions.append(idx - 1) +``` + +含义是:找到最后一个 `token_start <= answer_start_char` 的 token,它就是答案起点 token。 + +再向左找答案终点 token: + +```python +idx = context_end +while idx >= context_start and offset[idx][1] >= end_char: + idx -= 1 +end_positions.append(idx + 1) +``` + +含义是:找到第一个 `token_end >= answer_end_char` 的 token,它就是答案终点 token。 + +最后把这两个 token index 放回模型输入: + +```python +inputs["start_positions"] = start_positions +inputs["end_positions"] = end_positions +``` + +## 当前 Notebook 的坑位 + +### 1. 错把大多数答案标成 `[CLS]` + +判断答案是否在保留下来的 context 窗口内时,应该检查 context 的最后一个 token 是否仍覆盖答案起点: + +```python +if offset[context_start][0] > end_char or offset[context_end][1] < start_char: + start_positions.append(0) + end_positions.append(0) +``` + +如果误写成: + +```python +offset[context_start][1] < start_char +``` + +含义就变成“第一个 context token 的结束位置是否早于答案起点”。对绝大多数样本来说,答案不会出现在 context 第一个 token 里,所以这个条件几乎总是 true,训练标签就会被批量写成 `(0, 0)`。模型最后输出 `[CLS]` 并不是随机坏掉,而是在学习你给它的错误监督信号。 + +### 2. Inference 要加载同一个训练目录 + +训练时如果使用: + +```python +TrainingArguments(output_dir="my_awesome_qa_model", ...) +``` + +推理时也应该加载同一个目录: + +```python +tokenizer = AutoTokenizer.from_pretrained("my_awesome_qa_model") +model = AutoModelForQuestionAnswering.from_pretrained("my_awesome_qa_model") +``` + +否则你可能以为自己在测刚训练出的模型,实际加载的是另一个旧目录。 + +### 3. 空输出通常是 `start > end` + +最小推理代码里如果直接写: + +```python +answer_start_index = outputs.start_logits.argmax() +answer_end_index = outputs.end_logits.argmax() +predict_answer_tokens = inputs.input_ids[0, answer_start_index : answer_end_index + 1] +``` + +`start_logits.argmax()` 和 `end_logits.argmax()` 是两个独立选择,可能出现 `answer_start_index > answer_end_index`。这时切片为空,`tokenizer.decode(...)` 会得到空字符串,看起来像“什么都没输出”。 + +严肃 QA 后处理不能只取两个独立 argmax,而要在候选 start/end 中找合法 span: + +```text +end >= start +span length 不要过长 +span 最好落在 context token 范围内 +score = start_logit[start] + end_logit[end] +``` + +当前 lab 先用 top-k 合法 span 观察即可;后续再补完整 SQuAD EM/F1 postprocess。 + +### 4. 官方示例输出不是 golden output + +Hugging Face task guide 里的 inference 输出只能当作“代码形状示例”,不能当作本地训练的确定性答案。即使代码和数据集名称相同,结果也可能不同: + +- `AutoModelForQuestionAnswering.from_pretrained("distilbert/distilbert-base-uncased")` 会在 base DistilBERT 上新建 QA head;`qa_outputs.weight/bias` 是随机初始化,不是一个已经会 QA 的 head。 +- `train_test_split(test_size=0.2)` 没有固定 `seed` 时,训练/测试切分可能不同。 +- `TrainingArguments` 没有开启 full deterministic;MPS/GPU、库版本、batch 顺序和随机初始化都可能让小数据训练结果漂移。 +- 当前只用 `train[:5000]` 小样本训练 3 epoch,泛化到 BLOOM 这条非 SQuAD 风格手写 context 时不稳定是正常的。 +- 官方 inference 代码用独立 `argmax(start_logits)` 和 `argmax(end_logits)`,不是完整 QA postprocess;如果 `start > end`,本地会得到空字符串。如果选出合法但过长的 span,也可能得到一串不够精确的答案。 + +更稳定的学习目标不是复现官网那一句输出,而是观察: + +```text +preprocess 后 label span 能 decode 回原始 answers.text +训练后 start/end logits 开始偏向 context 中的数字或名词短语 +后处理必须约束合法 span +``` + +## 必须观察 + +在 notebook 里不要只跑完整训练。先取一个样本打印: + +```python +sample = squad["train"][0] +print(sample["question"]) +print(sample["answers"]) +print(sample["context"][sample["answers"]["answer_start"][0] : sample["answers"]["answer_start"][0] + len(sample["answers"]["text"][0])]) +``` + +再在 `preprocess_function` 里临时打印一个样本的: + +```text +sequence_ids +offset_mapping 前若干项 +context_start / context_end +start_char / end_char +start_positions / end_positions +tokenizer.decode(input_ids[start_positions:end_positions+1]) +``` + +验收标准:decode 出来的 token span 应该能还原到 `answers.text[0]`,允许有 tokenizer 空格或大小写差异。 + +## 来源 + +- [Hugging Face Transformers question answering task guide](https://huggingface.co/docs/transformers/tasks/question_answering) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/08-summarization-derivation.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/08-summarization-derivation.md new file mode 100644 index 0000000..caef020 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/08-summarization-derivation.md @@ -0,0 +1,116 @@ +# Summarization Derivation:为什么从 BERT 转到 BART/T5 + +## 本节问题 + +Hugging Face LLM Course Chapter 1/5 在 summarization 这里切到 BART/T5,不是因为 BERT 不重要,而是因为任务契约变了: + +```text +BERT 类任务: + input text -> fixed labels / token labels / span positions + +Summarization: + long input text -> shorter generated text +``` + +前者更像“读懂后做判断或定位”,后者更像“读懂后重新写一段文本”。 + +## BERT 的任务形状 + +BERT 是 encoder-only。它会双向读取输入,得到每个 token 的上下文表示。 + +前面几个 labs 的共同点是:模型不需要重新生成一段完整文本,只需要把 hidden states 变成某种标签。 + +```text +Text classification: + [CLS] hidden state -> sequence label logits + +Token classification: + each token hidden state -> token label logits + +Question answering: + each token hidden state -> start/end logits +``` + +所以 BERT 的强项是“把输入编码成可判断的表示”。它可以做抽取式 QA,因为答案 span 原本就在 context 里;但它不天然适合 abstractive summarization,因为摘要往往需要生成输入中没有逐字出现的新句子。 + +## BART/T5 的任务形状 + +BART/T5 是 encoder-decoder。encoder 先读完整输入,decoder 再根据 encoder 输出自回归生成目标文本。 + +```text +long article + -> encoder: understand source sequence + -> decoder: generate summary token by token + -> decode generated_ids into summary text +``` + +这就是 seq2seq:输入是一个序列,输出也是另一个序列。Summarization 和 translation 都属于这个形状。 + +BART 的预训练方式也贴近这个目标:把文本破坏掉,再训练模型重建原文。这个过程迫使模型学会“从损坏或压缩的信息中生成完整文本”,所以它适合需要生成目标序列的任务。 + +## Lab 里要观察什么 + +Summarization lab 不要只看最终摘要像不像人话。优先打印这些中间对象: + +```text +raw sample: + article / document + summary + +tokenized batch: + input_ids.shape + attention_mask.shape + labels.shape + +model: + AutoModelForSeq2SeqLM / T5ForConditionalGeneration / BartForConditionalGeneration + +forward: + outputs.loss + outputs.logits.shape + +generation: + generated_ids.shape + tokenizer.batch_decode(generated_ids, skip_special_tokens=True) +``` + +关键观察: + +- `labels` 不是分类 id,而是目标 summary 的 token ids。 +- `outputs.logits.shape` 通常是 `[batch_size, target_seq_len, vocab_size]`,不是 `[batch_size, num_labels]`。 +- 训练 loss 比较的是 decoder 每个位置预测的 token 和目标 summary token。 +- 推理时用 `model.generate(...)`,再把 `generated_ids` decode 成文本。 +- postprocess 的核心不是 `argmax -> label`,而是 `generated ids -> text`。 + +## 和前几个 lab 的最大差异 + +```text +Sequence classification: + one text -> one label + +Token classification: + one text -> one label per token + +Question answering: + question + context -> start/end token positions + +Summarization: + long text -> generated short text +``` + +这也是为什么课程先用 BERT 讲 classification / NER / QA,再用 BART/T5 讲 summarization / translation:不是模型名字切换,而是任务输出形状切换。 + +## 最小验收 + +跑完本 lab 后,至少能不用教程说清: + +1. 为什么 summarization 是 seq2seq,而不是 classification。 +2. 为什么 encoder-decoder 比 encoder-only 更自然。 +3. `labels` 为什么是一串 summary token ids。 +4. `generate()` 和前面 `argmax(logits)` 的区别。 +5. Summarization 和 translation 为什么可以共用同一类模型结构。 + +## 来源 + +- [Hugging Face LLM Course Chapter 1/5: How Transformers solve tasks](https://huggingface.co/learn/llm-course/en/chapter1/5) +- [Hugging Face Transformers summarization task guide](https://huggingface.co/docs/transformers/tasks/summarization) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/09-t5-task-prefix.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/09-t5-task-prefix.md new file mode 100644 index 0000000..636dd6e --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/09-t5-task-prefix.md @@ -0,0 +1,67 @@ +# T5 Task Prefix:为什么输入前要加 `summarize: ` + +## 本节问题 + +截图里的代码: + +```python +prefix = "summarize: " +inputs = [prefix + doc for doc in examples["text"]] +``` + +不是给人看的注释,而是把任务指令直接拼进模型输入里。 + +## 输入实际变成什么 + +假设原始 bill text 是: + +```text +The people of the State of California do enact as follows: ... +``` + +拼接后送给 tokenizer 的输入会变成: + +```text +summarize: The people of the State of California do enact as follows: ... +``` + +`prefix + doc` 发生在 tokenizer 之前,所以 `summarize:` 也会被 tokenizer 编进 `input_ids`。 + +## 为什么 T5 需要它 + +T5 被设计成 text-to-text 模型:很多任务都统一成“输入一段文本,输出另一段文本”。同一个模型可以做 summarization、translation、question answering 等任务,所以输入里需要带一个任务提示,告诉模型“这次你要做摘要”。 + +可以把它理解成: + +```text +普通 encoder-decoder: + source text -> target text + +T5 style: + task prefix + source text -> target text +``` + +在这个 lab 里: + +```text +input text: + "summarize: " + bill text + +target text: + bill summary +``` + +它不是 label,也不会出现在 `labels` 里;`labels` 来自 `examples["summary"]`。 + +## 为什么 tokenizer 分两行 + +```python +model_inputs = tokenizer(inputs, max_length=1024, truncation=True) +labels = tokenizer(text_target=examples["summary"], max_length=128, truncation=True) +``` + +第一行处理“带任务提示的输入文本”,第二行处理“模型应该生成的目标摘要”。 + +## 来源 + +- [Hugging Face Transformers summarization task guide](https://huggingface.co/docs/transformers/tasks/summarization) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/10-billsum-dataset-loading.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/10-billsum-dataset-loading.md new file mode 100644 index 0000000..bad69a0 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/10-billsum-dataset-loading.md @@ -0,0 +1,98 @@ +# BillSum Dataset Loading:短名、split 和 notebook 变量 + +## 数据集 ID + +课程旧代码可能写成: + +```python +billsum = load_dataset("billsum", split="ca_test") +``` + +在当前环境里会触发: + +```text +HfUriError: Invalid HF URI 'hf://datasets/billsum@...' +Repository id must be 'namespace/name', got 'billsum'. +``` + +这和前面 SQuAD 的短名问题同类:Hub repo id 需要显式 namespace。BillSum 在 Hugging Face Hub 上的完整 dataset id 是: + +```python +billsum = load_dataset("FiscalNote/billsum", split="ca_test") +``` + +已用本地 `datasets` API 验证: + +```text +configs: ['default'] +splits: ['train', 'test', 'ca_test'] +``` + +## `split` 参数的作用 + +`split` 用来选择要加载的数据分区。它不是现场随机切分数据,而是从数据集 repo 已经定义好的 split 中取一份。 + +对 BillSum 来说: + +```python +load_dataset("FiscalNote/billsum") +``` + +会返回一个 `DatasetDict`,里面有 `train`、`test`、`ca_test` 三个 split。 + +```python +load_dataset("FiscalNote/billsum", split="ca_test") +``` + +只返回 `ca_test` 这一份 `Dataset`。这里的 `ca_test` 是 California bills 的测试分区,课程用它通常是因为它比完整训练集小,适合先观察 summarization 数据结构和流程。 + +`split` 也支持切片,用来做小样本观察: + +```python +load_dataset("FiscalNote/billsum", split="train[:100]") +load_dataset("FiscalNote/billsum", split="ca_test[:10%]") +``` + +## Notebook 重跑时为什么用 `billsum_raw` + +`billsum_raw` 不是特殊变量名。它的作用是保留“还没有被再次切分”的原始 `Dataset`,避免 notebook 反复运行时把变量覆盖成另一种类型。 + +推荐写法: + +```python +billsum_raw = load_dataset("FiscalNote/billsum", split="ca_test") +billsum = billsum_raw.train_test_split(test_size=0.2) +``` + +这里两个变量的类型不同: + +```text +billsum_raw: Dataset +billsum: DatasetDict, with train/test +``` + +如果复用同一个变量: + +```python +billsum = load_dataset("FiscalNote/billsum", split="ca_test") +billsum = billsum.train_test_split(test_size=0.2) +``` + +第一次从上到下运行通常没问题。但如果在 notebook 里只重跑第二行,或者某个 cell 里写的是: + +```python +billsum = billsum.train_test_split(test_size=0.2) +``` + +第二次运行时,`billsum` 已经不是原始 `Dataset`,而是上一次切分后的 `DatasetDict`。`DatasetDict` 没有 `.train_test_split(...)` 方法,于是会报: + +```text +AttributeError: 'DatasetDict' object has no attribute 'train_test_split' +``` + +所以这里的原则是:原始数据和加工后数据分开命名。`*_raw` 保存原材料,普通变量名保存下一步要训练或处理的数据。 + +## 来源 + +- [BillSum dataset on Hugging Face](https://huggingface.co/datasets/FiscalNote/billsum) +- [Hugging Face Datasets loading methods](https://huggingface.co/docs/datasets/en/package_reference/loading_methods) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/11-rouge-metric.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/11-rouge-metric.md new file mode 100644 index 0000000..dfed24b --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/11-rouge-metric.md @@ -0,0 +1,64 @@ +# ROUGE Metric:摘要评估怎么看 + +## 核心直觉 + +ROUGE 是 summarization 里最常见的自动评估指标之一。它的核心思想很朴素:把模型生成的 summary 和人工 reference summary 做文本重叠比较。 + +```text +prediction: model generated summary +reference: dataset human summary +ROUGE: overlap(prediction, reference) +``` + +## 常见指标 + +| 指标 | 比较什么 | 直觉 | +| --- | --- | --- | +| `rouge1` | unigram / 单词重叠 | 生成摘要有没有覆盖 reference 里的关键词 | +| `rouge2` | bigram / 连续两个词重叠 | 生成摘要有没有学到局部短语结构 | +| `rougeL` | longest common subsequence | 生成摘要和 reference 的长顺序片段有多像 | +| `rougeLsum` | summary-level LCS | 更适合多句摘要,通常会按换行处理句子边界 | + +名字里的 Recall-Oriented 是历史来源:它关心 reference 中有多少内容被生成摘要覆盖。但在 Hugging Face `evaluate.load("rouge")` 的当前实现里,默认返回的是各项 ROUGE 的 F1 分数,而不是只返回 recall。 + +## 课程代码里的链路 + +```python +rouge = evaluate.load("rouge") +result = rouge.compute( + predictions=decoded_preds, + references=decoded_labels, + use_stemmer=True, +) +``` + +这里必须先 decode,因为 ROUGE 比的是文本,不是 token id: + +```text +generated ids -> decoded_preds +label ids -> decoded_labels +rouge.compute(predictions=decoded_preds, references=decoded_labels) +``` + +`use_stemmer=True` 会把英文词尾做简单归一化,比如 treats / treated / treating 更容易被看成同一词根。对 BillSum 这种英文数据集有帮助;非英文任务要更谨慎,必要时传自定义 tokenizer。 + +## 边界 + +- 它奖励词面重叠,不真正理解语义。 +- 它不能可靠判断事实是否正确。 +- 同义改写可能分低,照抄 reference 关键词可能分高。 +- 它适合做训练过程中的粗略趋势指标,不应该替代人工读样本。 + +对本 lab 来说,最小观察不是追求 ROUGE 很高,而是确认: + +```text +model.generate(...) + -> tokenizer.batch_decode(...) + -> rouge.compute(predictions=..., references=...) + -> rouge1 / rouge2 / rougeL / rougeLsum +``` + +## 来源 + +- [Hugging Face Evaluate ROUGE metric card](https://huggingface.co/spaces/evaluate-metric/rouge) +- [Hugging Face Transformers summarization task guide](https://huggingface.co/docs/transformers/tasks/summarization) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/12-mixed-precision-fp16-bf16.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/12-mixed-precision-fp16-bf16.md new file mode 100644 index 0000000..83aa532 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/12-mixed-precision-fp16-bf16.md @@ -0,0 +1,74 @@ +# Mixed Precision:`fp16`、`bf16` 和 XPU + +## `fp16=True` 是什么 + +`fp16` 是 floating point 16-bit,意思是用 16 位浮点数参与训练。默认训练通常用 `fp32`,也就是 32 位浮点数。 + +```text +fp32: + 数字更精细、更稳 + 占显存/内存更多 + 速度通常更慢 + +fp16: + 数字更省空间 + 在支持的 GPU 上通常更快 + 数值范围更小,更容易 overflow / underflow +``` + +在 Hugging Face `TrainingArguments` 里: + +```python +fp16=True +``` + +表示启用半精度或混合精度训练。它主要是性能/显存优化,不是模型结构的一部分,也不是 summarization 任务必须要开的开关。 + +## 当前 lab 的默认建议 + +当前 lab 的目标是先跑通 seq2seq 链路: + +```text +text -> tokenizer -> model.generate -> decode -> ROUGE +``` + +所以本地排障时建议先关掉: + +```python +fp16=False +bf16=False +``` + +尤其在 Mac/MPS 环境里,`fp16=True` 未必像 CUDA/NVIDIA GPU 那样稳定或有明显收益。等链路跑通后,如果换到支持混合精度更成熟的 GPU,再打开它做速度优化。 + +## `bf16` 和 XPU + +```text +fp16: 16-bit float,省显存/快,但数值范围小 +bf16: bfloat16,同样 16-bit,但指数范围更接近 fp32,通常更稳 +fp32: 32-bit float,默认稳妥选择 +``` + +官方注释里的: + +```python +# change to bf16=True for XPU +``` + +意思是:如果你在 Intel XPU 后端上训练,通常应该用 `bf16=True`,而不是 `fp16=True`。这里的 XPU 不是“所有 GPU”的泛称,在 PyTorch 语境里主要指 Intel GPU / accelerator 后端。 + +当前本地环境检查结果: + +```text +torch 2.12.0 +mps available True +cuda available False +xpu available False +``` + +所以本机是 Apple/MPS 路径,不是 XPU 路径。不要同时开 `fp16=True` 和 `bf16=True`。 + +## 来源 + +- [Hugging Face mixed precision training](https://huggingface.co/docs/transformers/mixed_precision_training) +- [PyTorch XPU documentation](https://docs.pytorch.org/docs/stable/notes/get_start_xpu.html) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/13-summarization-compute-metrics-overflow.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/13-summarization-compute-metrics-overflow.md new file mode 100644 index 0000000..d35cdb7 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/13-summarization-compute-metrics-overflow.md @@ -0,0 +1,127 @@ +# Summarization Metric Debug:`batch_decode` OverflowError + +## 现象 + +如果 `trainer.train()` 在 evaluation 阶段报: + +```text +OverflowError: out of range integral type conversion attempted +``` + +并且 stack trace 指到: + +```python +decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) +``` + +问题在 ROUGE 前一步:`tokenizer.batch_decode(...)` 收到了不能 decode 的 token id。 + +## 官方代码的隐含前提 + +先不要理解成“官方代码错了”。官方这段 `compute_metrics` 隐含了一个前提:`predictions` 已经是 `model.generate(...)` 产生的合法 token ids,所以可以直接 decode。这个前提通常由下面几件事共同保证: + +- 使用 `Seq2SeqTrainer`。 +- `Seq2SeqTrainingArguments(predict_with_generate=True)`。 +- trainer 收到 tokenizer / processing class,能正确处理 generation 和 padding。 + +一个常见配置错误是: + +```python +processing_class=preprocess_function +``` + +这里应该传 tokenizer: + +```python +processing_class=tokenizer +``` + +`preprocess_function` 是给 `Dataset.map(...)` 用的;`tokenizer` 才是给 trainer/collator/decode 用的。 + +## 最小诊断 + +如果修正 trainer 后仍然在 `batch_decode(predictions)` 炸,就说明当前环境下传进 `compute_metrics` 的 `predictions` 仍然含有不能 decode 的值。先打印 shape/range: + +```python +def compute_metrics(eval_pred): + predictions, labels = eval_pred + if isinstance(predictions, tuple): + predictions = predictions[0] + + print("predictions shape:", predictions.shape) + print("predictions min/max:", predictions.min(), predictions.max()) + print("labels min/max:", labels.min(), labels.max()) + ... +``` + +判断方式: + +```text +predictions.shape == [batch, generated_seq_len]: + 正常 generation 输出 + +predictions.shape == [batch, seq_len, vocab_size]: + 这不是 generated ids,而是 logits + +predictions.min() < 0: + 里面有 ignore index,不能直接 decode +``` + +## 当前环境实证 + +本地最小复现中,用同一环境、2 条 BillSum eval 样本、`processing_class=tokenizer` 跑 `trainer.evaluate()`,`predictions` 是正常 generated ids: + +```text +PRED_SHAPE (2, 21) +PRED_DTYPE int64 +PRED_MINMAX 0 21012 +``` + +这种情况下官方 `tokenizer.batch_decode(predictions, ...)` 可以正常运行。 + +但用完整 `ca_test` 切分后的 248 条 eval 样本复现时,当前环境会在 predictions 中混入 `-100`: + +```text +PRED_SHAPE (248, 21) +PRED_DTYPE int64 +PRED_MINMAX -100 31433 +NEG_VALUES [(-100, 232)] +``` + +因此当前 notebook 不能只照官方简化版处理 labels;也要先处理 predictions。 + +## 修复写法 + +```python +def compute_metrics(eval_pred): + predictions, labels = eval_pred + + if isinstance(predictions, tuple): + predictions = predictions[0] + + predictions = np.where(predictions != -100, predictions, tokenizer.pad_token_id) + labels = np.where(labels != -100, labels, tokenizer.pad_token_id) + + decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) + decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) + + result = rouge.compute( + predictions=decoded_preds, + references=decoded_labels, + use_stemmer=True, + ) + + prediction_lens = [ + np.count_nonzero(pred != tokenizer.pad_token_id) + for pred in predictions + ] + result["gen_len"] = np.mean(prediction_lens) + + return {k: round(v, 4) for k, v in result.items()} +``` + +这个修复不改变模型输出含义,只是把 evaluation padding 用的 ignore index 换成 tokenizer 能 decode 的 pad token。 + +## 来源 + +- [Hugging Face Transformers summarization task guide](https://huggingface.co/docs/transformers/tasks/summarization) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/14-summarization-inference-generate.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/14-summarization-inference-generate.md new file mode 100644 index 0000000..0bdd49d --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/14-summarization-inference-generate.md @@ -0,0 +1,206 @@ +# Summarization Inference:从文本到生成摘要 + +## 记忆框架 + +这段代码不要按参数硬背,按数据流记: + +```text +saved model dir + -> load tokenizer / model +raw text + -> tokenizer: text -> input ids +model.generate + -> output ids +tokenizer.decode + -> summary text +``` + +最短口诀: + +```text +load same dir -> encode ids -> generate ids -> decode text +``` + +## 推荐写法 + +如果 `my_awesome_billsum_model` 是目录名字符串,要加引号: + +```python +model_dir = "my_awesome_billsum_model" +``` + +如果训练后只看到 `checkpoint-*` 子目录,要加载 checkpoint 子目录: + +```python +model_dir = "my_awesome_billsum_model/checkpoint-248" +``` + +推理代码可以写成: + +```python +from transformers import AutoTokenizer, AutoModelForSeq2SeqLM + +model_dir = "my_awesome_billsum_model" + +tokenizer = AutoTokenizer.from_pretrained(model_dir) +model = AutoModelForSeq2SeqLM.from_pretrained(model_dir) + +text = "summarize: " + text +inputs = tokenizer(text, return_tensors="pt") + +outputs = model.generate( + **inputs, + max_new_tokens=100, + do_sample=False, +) + +summary = tokenizer.decode(outputs[0], skip_special_tokens=True) +summary +``` + +这里用 `**inputs` 比只传 `.input_ids` 更稳,因为 tokenizer 可能同时返回 `input_ids` 和 `attention_mask`。`attention_mask` 告诉模型哪些位置是真输入,哪些位置是 padding。 + +## 每个参数怎么理解 + +```python +AutoTokenizer.from_pretrained(model_dir) +``` + +从保存目录或 Hub repo 加载 tokenizer。它负责文本和 token ids 的互相转换。要和训练时的 tokenizer 保持一致。 + +```python +AutoModelForSeq2SeqLM.from_pretrained(model_dir) +``` + +从保存目录或 Hub repo 加载 seq2seq 生成模型。`Auto` 会根据 config 自动选择具体类,比如 T5 会变成对应的 conditional generation model。 + +```python +tokenizer(text, return_tensors="pt") +``` + +把文本转成模型输入,并返回 PyTorch tensor。`pt` 就是 PyTorch。 + +```python +model.generate(...) +``` + +进入生成模式。对 summarization 来说,它会先让 encoder 读输入,再让 decoder 一个 token 一个 token 生成摘要。 + +```python +max_new_tokens=100 +``` + +最多生成 100 个新 token。它限制的是输出摘要长度,不是输入长度,也不是 100 个英文单词。 + +```python +do_sample=False +``` + +不随机采样。模型每一步更偏向选择最确定的 token,所以结果更稳定、可复现。摘要任务通常先用它;创意写作才更常打开 sampling。 + +```python +outputs[0] +``` + +`generate` 返回的是一批输出。即使只输入一条文本,返回值也有 batch 维度,所以第一个结果是 `outputs[0]`。 + +```python +tokenizer.decode(outputs[0], skip_special_tokens=True) +``` + +把生成出来的 token ids 转回人类可读文本,并跳过 `<pad>`、`</s>` 这类特殊 token。 + +## 和训练时的对应关系 + +训练时: + +```text +input ids + labels -> loss +``` + +推理时: + +```text +input ids -> generated ids -> decoded summary +``` + +训练需要 reference summary 做 `labels`;推理没有 `labels`,只让模型自己生成。 + +## 常见坑 + +- T5 summarization 输入仍然要有 `summarize: ` prefix,除非你训练时刻意去掉了这个约定。 +- `my_awesome_billsum_model` 如果是目录名,要写成字符串;如果不加引号,Python 会把它当变量名。 +- tokenizer 和 model 要从同一个保存目录加载,避免 vocab/config 不一致。 +- 单条文本用 `decode`;多条文本可以用 `batch_decode`。 +- 本地只验证链路时,先用较小的 `max_new_tokens`,比如 50 或 100。 + +## 当前排障:根目录不是可加载模型 + +如果加载时报: + +```text +ValueError: Unrecognized model in my_awesome_billsum_model. +Should have a `model_type` key in its config.json. +``` + +含义是:`AutoModelForSeq2SeqLM.from_pretrained(...)` 没在这个目录里读到可识别的模型配置。 + +`AutoModel...` 的工作方式是: + +```text +read config.json + -> find model_type + -> choose concrete model class + -> load model weights +``` + +当前本地目录结构里,`my_awesome_billsum_model` 根目录没有模型文件,真正可加载的是: + +```text +my_awesome_billsum_model/checkpoint-248/ + config.json # model_type: t5 + model.safetensors + tokenizer.json + tokenizer_config.json + generation_config.json +``` + +所以可以直接加载 checkpoint: + +```python +model_dir = "my_awesome_billsum_model/checkpoint-248" + +tokenizer = AutoTokenizer.from_pretrained(model_dir) +model = AutoModelForSeq2SeqLM.from_pretrained(model_dir) +``` + +如果想让根目录本身也变成可加载目录,在训练完成后显式保存一次: + +```python +trainer.save_model("my_awesome_billsum_model") +tokenizer.save_pretrained("my_awesome_billsum_model") +``` + +再加载: + +```python +model_dir = "my_awesome_billsum_model" +tokenizer = AutoTokenizer.from_pretrained(model_dir) +model = AutoModelForSeq2SeqLM.from_pretrained(model_dir) +``` + +记忆原则: + +```text +TrainingArguments(output_dir=...) + 是训练输出目录 + +checkpoint-* 或 save_model(...) 产物 + 才是 from_pretrained(...) 可加载模型目录 +``` + +## 来源 + +- [Hugging Face Transformers text generation](https://huggingface.co/docs/transformers/en/main_classes/text_generation) +- [Hugging Face Transformers generation strategies](https://huggingface.co/docs/transformers/en/generation_strategies) +- [Hugging Face Transformers auto classes](https://huggingface.co/docs/transformers/en/model_doc/auto) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/15-chapter1-6-transformer-architectures.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/15-chapter1-6-transformer-architectures.md new file mode 100644 index 0000000..37e90ca --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/15-chapter1-6-transformer-architectures.md @@ -0,0 +1,430 @@ +# Chapter 1/6 Transformer Architectures:从任务反推架构 + +## 学习现场 + +当前不补 Translation / ASR / Image classification。先用已经完成的文本任务观察,读 Hugging Face LLM Course Chapter 1/6: + +- Causal LM / text generation +- Text classification +- Token classification +- Extractive question answering +- Summarization + +本节目标不是背 `encoder-only`、`decoder-only`、`encoder-decoder` 三个名词,而是看到一个任务时能反推: + +```text +这个任务需要理解整段输入? +还是需要按顺序继续生成? +还是需要把一个输入序列转换成另一个输出序列? +``` + +## 先读出的三条主线 + +### Encoder-only + +阅读时抓一个问题:模型能不能同时看见输入中的左右上下文? + +如果任务主要是理解现有输入,并在输入之上做分类、标注或抽取,优先映射到 encoder-only: + +- Text classification:整句 -> 一个标签。 +- Token classification:整句 -> 每个 token 一个标签。 +- Extractive QA:question + context -> context 中的 start/end span。 + +观察锚点: + +```text +input_ids / attention_mask +-> encoder contextual representations +-> task head +-> logits / span positions +``` + +### Decoder-only + +阅读时抓一个问题:模型是不是只能看见当前位置之前的 token,然后预测下一个 token? + +如果任务主要是继续生成文本,优先映射到 decoder-only: + +- Causal LM / text generation:前文 -> next token。 + +观察锚点: + +```text +input_ids +-> causal / masked self-attention +-> language modeling head +-> next-token logits +-> generate +``` + +### Encoder-decoder + +阅读时抓一个问题:输入和输出是不是两个序列,而且输出要依赖完整输入? + +如果任务是把一个输入文本转换成另一个输出文本,优先映射到 encoder-decoder: + +- Summarization:长文 -> 摘要。 +- Translation:源语言句子 -> 目标语言句子,当前已跳过/延后。 + +观察锚点: + +```text +source input_ids +-> encoder reads source +-> decoder autoregressively generates target +-> generated ids +-> decoded text +``` + +### 能做和更适合不是一回事 + +架构分类不是能力绝对边界。现代模型经常能跨架构完成任务,但课程里的三分类是在给默认任务接口和工程选择建立直觉。 + +以 summarization 为例: + +```text +extractive summary: + 从原文里挑句子 / 片段 + -> encoder-only 可以做,因为它主要是理解和选择 + +abstractive summary: + 读完整原文后生成新摘要 + -> encoder-decoder 最自然,因为 encoder 负责读完整输入,decoder 负责生成输出 + +prompted summary: + 把原文和“请总结”放进 prompt,让模型续写 + -> decoder-only 也能做,尤其是现代 LLM +``` + +所以更准确的判断不是“别的架构不能做”,而是: + +```text +这个任务最自然的接口是什么? +输出是标签 / span,还是一个新的 token 序列? +如果要生成新序列,它是否强依赖完整输入? +``` + +## 读的时候做一个小判断表 + +| 任务 | 输入 | 输出 | 更像哪类架构 | 判断理由 | +| --- | --- | --- | --- | --- | +| Causal LM | 前文 tokens | 下一个 token / 后续文本 | decoder-only | 只依赖前文继续生成 | +| Text classification | 一段文本 | 一个类别 | encoder-only | 需要理解完整输入后分类 | +| Token classification | 一段 tokens | 每个 token 的类别 | encoder-only | 每个 token 的判断依赖整句上下文 | +| Extractive QA | question + context | context 中答案 span | encoder-only | 在完整 context 里定位答案 | +| Summarization | 长文本 | 短生成文本 | encoder-decoder | 先理解完整输入,再生成新序列 | + +## 本节不要急着做的事 + +- 不补 Translation / ASR / Image classification,除非后续明确要补 seq2seq 或多模态直觉。 +- 不把 Chapter 2 pipeline internals 提前做完;这节还要读 attention mechanisms。 +- 不把 Agent 解释写成用户 notes。notes 要等用户读完、复述或运行观察后再记。 + +## Attention mechanisms 阅读入口 + +Chapter 1/6 后半段还没有结束。官方继续讲的是:标准 attention 对长文本很贵,所以出现了一些更高效的 attention / position encoding 变体。 + +先抓住一个公式级直觉: + +```text +full attention: + 每个 token 都看每个 token + attention matrix 约是 seq_len x seq_len + 成本随长度近似 O(n^2) 增长 +``` + +这解释了为什么长文本会变贵:长度翻倍,attention 矩阵不是翻倍,而是大约变成四倍。 + +接着读三个变体时,不需要记细节公式,先问: + +```text +它到底减少了哪些 token-to-token 连接? +它保留了什么能力? +它牺牲了什么全局信息? +``` + +当前最小理解目标: + +| 机制 | 先抓住什么 | 要问的取舍 | +| --- | --- | --- | +| LSH attention | 只让相近的 query/key 互相注意 | 怎么用近似检索减少全连接计算?会不会漏掉重要远距离关系? | +| Local attention | 主要看附近窗口,少数 token 可有 global attention | 局部窗口为什么够用?什么时候需要全局 token? | +| Axial positional encodings | 把很长的位置编码矩阵拆成更小的因子 | 这是省位置编码参数/内存,不是直接改变 token 注意谁 | + +### LSH attention 慢动作解释 + +先不要从 `hash function` 这个词开始理解。从 full attention 的浪费开始: + +```text +full attention: + 对每个 query q + 都拿它和所有 key k 做相似度比较 + 得到一整行 QK^T 分数 + softmax 后,大多数很小的分数几乎没有贡献 +``` + +LSH attention 的想法是: + +```text +既然最后真正有用的是“和 q 最像的少数 k” +那就不要把 q 和所有 k 都比一遍 +先用 locality-sensitive hashing 把相似向量分到同一桶 +然后只在同一桶里做 attention +``` + +这里的 “close” 不是 token 在句子里的位置近,而是向量空间里内容相似。它和 local attention 的区别很重要: + +```text +local attention: + 位置近,所以互相看 + +LSH attention: + 向量相似,所以互相看 +``` + +一个粗糙例子: + +```text +full attention: + q1 要和 k1, k2, k3, k4, k5, k6, k7, k8 都比较 + +LSH attention: + hash(q1) -> bucket A + bucket A 里只有 k3, k7 + q1 只和 k3, k7 比较 +``` + +这不是精确搜索,而是近似搜索。哈希可能把真正相关的 token 分错桶,所以实践里会用多轮 hash,再把结果合起来,降低漏掉重要关系的概率。 + +最小记忆: + +```text +full attention = 每个 token 问所有 token +LSH attention = 先按相似度粗分组,只问同组 token +收益 = 少算很多 pair +代价 = 近似,有可能漏掉本该看的远处信息 +``` + +### Local attention 慢动作解释 + +Local attention 的起点比 LSH 更直观: + +```text +很多语言理解任务里 +一个 token 最常依赖的不是整篇文章所有 token +而是它附近的一小段上下文 +``` + +所以 local attention 不再让每个 token 看全局,而是只看附近窗口: + +```text +full attention: + token i 可以看 1..n 的所有 token + +local attention: + token i 只看 i-window .. i+window 的附近 token +``` + +一个粗糙例子,假设窗口大小是 2: + +```text +token 5 在 full attention 里看: + 1, 2, 3, 4, 5, 6, 7, 8, 9 + +token 5 在 local attention 里只看: + 3, 4, 5, 6, 7 +``` + +它减少计算的方式不是“哈希找相似内容”,而是“砍掉远距离连接”: + +```text +full attention: + 每个 token 看 n 个 token + 总成本约 n x n + +local attention: + 每个 token 看固定窗口 w 个 token + 总成本约 n x w +``` + +如果 `w` 比 `n` 小很多,计算就会轻很多。 + +关键取舍是:局部关系保留得好,远距离依赖容易丢。 + +```text +适合: + 主要依赖附近上下文的任务 + 很长文本里需要降低成本的场景 + +风险: + 开头的信息可能影响结尾 + 跨段落、跨章节的关系可能被窗口切断 +``` + +所以一些模型会加少量 `global attention`:让特定 token 能看全局,或者被全局看见。 + +```text +local attention: + 大多数 token 只看附近 + +local + global attention: + 大多数 token 看附近 + 少数重要 token 负责跨远距离传递信息 +``` + +这些 global token 通常不是模型“自己发现”的,而是由模型设计或任务预处理指定的。常见确定方式: + +```text +1. 特殊汇总 token + 例如 [CLS] / <s> / BOS + 让它看全局,用来汇总整段输入 + +2. 任务关键位置 + 例如 QA 里的 question tokens + 让问题 token 看全局,帮助它们从长 context 中找答案 + +3. 结构边界 token + 例如段落标题、分隔符、章节开头 + 让这些 token 成为跨段落信息中转站 + +4. 固定间隔的中转 token + 每隔一段放一个 global token + 像长文本里的路由节点,让远距离信息能分段传过去 + +5. 任务规则指定 + 比如分类任务让 [CLS] 全局,检索任务让 query 部分全局 +``` + +所以 `重要` 不是抽象玄学,而是由任务接口决定: + +```text +谁需要整篇信息? +谁负责输出最终判断? +谁能把远处信息传给附近窗口? +``` + +最小记忆: + +```text +local attention = 只看附近窗口 +收益 = 把 O(n^2) 近似降到 O(n x window) +代价 = 远距离信息可能断掉 +补救 = 给少数 token global attention +``` + +### Axial positional encodings 慢动作解释 + +先把它和前两个机制分开: + +```text +LSH attention / local attention: + 改的是 token 之间“谁看谁” + +axial positional encodings: + 改的是“位置编号怎么表示” +``` + +Transformer 本身不知道 token 顺序,所以要给每个位置一个位置向量。最直接的 learned positional embedding 是: + +```text +position 0 -> 一个 hidden_size 维向量 +position 1 -> 一个 hidden_size 维向量 +... +position n -> 一个 hidden_size 维向量 +``` + +如果最大长度很大,这张表会变得很大: + +```text +max_length x hidden_size +``` + +例如: + +```text +4096 positions x 512 dims +``` + +Axial positional encodings 的想法是:不要把 4096 个位置当成一条超长的一维表,而是把它拆成多个轴。 + +比如 4096 可以拆成: + +```text +64 x 64 = 4096 +``` + +于是一个一维位置可以改写成二维坐标: + +```text +position 0 -> (row 0, col 0) +position 1 -> (row 0, col 1) +... +position 65 -> (row 1, col 1) +... +``` + +然后分别给 row 和 col 学位置向量,再拼起来或加起来: + +```text +position embedding(position) + = row_embedding[row] + +/concat col_embedding[col] +``` + +参数量直觉从: + +```text +4096 x hidden_size +``` + +变成类似: + +```text +64 x dim_row + 64 x dim_col +``` + +也就是用两个短轴的位置表组合出很多长序列位置。 + +一个粗糙类比: + +```text +普通位置编码: + 给每个门牌号单独做一张身份证 + +axial 位置编码: + 用“楼层号 + 房间号”组合出门牌号 +``` + +它的重点不是减少 attention pair,而是让长序列的位置表示更省参数/内存。 + +最小记忆: + +```text +axial positional encodings = 把长的一维位置编号拆成多个轴 +收益 = 更省位置 embedding 参数/内存,适合长序列 +代价 = 位置表达被拆成组合形式,设计上要选好 axial shape / dims +``` + +## 后续提醒队列 + +这些事项已经写入 `.daedalus/todo.md` 的 `Reminder Queue`,恢复学习时要提醒用户做: + +- Chapter 1/6 架构映射:把已做过的 Causal LM / classification / QA / summarization 映射到三类架构。 +- Chapter 1/6 attention mechanisms:理解 full attention 的 O(n^2) 成本,以及 LSH / local attention / axial positional encodings 的取舍。 +- Chapter 2 前后 pipeline 拆解:手写 `pipeline("text-classification")` 的 tokenizer -> model -> postprocess 等价流程。 +- Trainer 机制观察:打印 batch keys / shape / device,并手动运行 `model(**batch)` 查看 loss/logits。 + +## 通过标准 + +读完 Chapter 1/6 后,用户至少能不用教程回答: + +```text +1. 为什么 classification / token classification / extractive QA 更像 encoder-only? +2. 为什么 Causal LM 更像 decoder-only? +3. 为什么 summarization 更像 encoder-decoder? +4. 如果看到一个新任务,我先问哪三个问题来选架构? +``` + +## 来源 + +- [Hugging Face LLM Course Chapter 1/6: Transformer Architectures](https://huggingface.co/learn/llm-course/en/chapter1/6) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/16-chapter1-8-llm-inference.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/16-chapter1-8-llm-inference.md new file mode 100644 index 0000000..5beb1e6 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/16-chapter1-8-llm-inference.md @@ -0,0 +1,101 @@ +# Chapter 1/8 Deep Dive Into Text Generation Inference With LLMs + +## 学习现场 + +用户已进入 Hugging Face LLM Course Chapter 1/8 `Deep dive into Text Generation Inference with LLMs`。本节不是继续比较 task head,而是把前面学过的 decoder-only / causal LM / attention 连接到 LLM 真实推理过程。 + +官方页面主线: + +- LLM inference 基础:从 prompt 生成 response。 +- attention / context length:模型一次能看多少 token,以及长上下文为什么贵。 +- prompting:输入文本如何引导生成。 +- prefill / decode:推理分成读 prompt 和逐 token 生成两阶段。 +- sampling controls:temperature、top-p、top-k、repetition penalties、length controls、beam search。 +- performance / KV cache:TTFT、TPOT、throughput、VRAM,以及 KV cache 为什么重要。 + +## 先抓住一条总链路 + +```text +raw prompt +-> tokenizer +-> prefill:一次性处理 prompt,建立上下文表示 / KV cache +-> decode:每次生成一个 token +-> sampling:从 logits 变成下一个 token +-> append token +-> repeat until stop +``` + +本节最重要的分界是: + +```text +prefill: + 读完整 prompt + 更影响 first token 前的等待时间 + +decode: + 逐 token 生成 + 更影响后续每个 token 的速度 +``` + +## 阅读检查点 + +读本节时每一段都回到这几个问题: + +1. 这个概念影响的是 prompt 处理,还是逐 token 生成? +2. 这个参数控制的是质量、随机性、长度,还是成本? +3. 这个优化是在省重复计算,还是在牺牲一部分搜索空间? + +## 必须理解的性能词 + +| 指标 | 先这样理解 | 常见影响因素 | +| --- | --- | --- | +| TTFT | Time To First Token:第一个 token 出来前等多久 | prompt 长度、prefill 成本、排队 | +| TPOT | Time Per Output Token:后续每个 token 多快 | decode 成本、KV cache、batching | +| Throughput | 单位时间能处理多少请求 / token | batching、硬件、模型大小 | +| VRAM | 推理需要多少显存 | 模型权重、KV cache、context length、batch size | + +## KV Cache 的最小直觉 + +decoder-only 模型生成第 `t+1` 个 token 时,需要参考之前 token 的 key/value。如果每次都从头重算全部历史,会浪费很多。 + +KV cache 的想法: + +```text +已经算过的历史 token 的 key/value +-> 存起来 +-> 后续 decode 直接复用 +``` + +收益: + +```text +少重复计算,后续 token 更快 +``` + +代价: + +```text +要存历史 token 的 key/value,context 越长、batch 越大,显存越高 +``` + +## 本节不要急着做的事 + +- 不提前进入 Chapter 2 `Using Transformers`。 +- 不把 TGI 部署工程展开成生产配置;当前只读 inference 机制。 +- 不把 Agent 解释写成用户 notes。notes 等用户复述或实验观察后再写。 + +## 通过标准 + +读完本节后,用户至少能不用教程解释: + +```text +1. LLM inference 为什么是逐 token 生成? +2. prefill 和 decode 分别做什么? +3. temperature / top-p / top-k 分别怎么影响 token selection? +4. TTFT / TPOT / throughput / VRAM 分别衡量什么? +5. KV cache 为什么加速 decode,但会增加显存占用? +``` + +## 来源 + +- [Hugging Face LLM Course Chapter 1/8: Deep dive into Text Generation Inference with LLMs](https://huggingface.co/learn/llm-course/en/chapter1/8) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/17-chapter3-finetuning-pretrained-model.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/17-chapter3-finetuning-pretrained-model.md new file mode 100644 index 0000000..905b4ab --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/17-chapter3-finetuning-pretrained-model.md @@ -0,0 +1,652 @@ +# Chapter 3:从预训练模型到可诊断的微调闭环 + +## 这章真正要学什么 + +Chapter 2 解决的是“如何把输入交给预训练模型并得到输出”,Chapter 3 开始回答: + +> 如何用自己的有标签数据改变模型参数,并证明这种改变确实改善了目标任务? + +整章使用 BERT + GLUE/MRPC 句对分类作为受约束的设计案例。不要只记住 MRPC recipe;需要抽取的是一条可迁移到其他监督微调任务的主线: + +```text +任务契约 +-> 原始 dataset 与 split +-> tokenizer / preprocessing +-> collator / DataLoader +-> model forward -> loss +-> backward -> optimizer -> scheduler +-> evaluation metrics +-> learning curves / error diagnosis +-> checkpoint 与可复现证据 +``` + +本章用两种方式实现同一件事: + +```text +Trainer API:用声明式配置交给框架编排 +Pure PyTorch loop:把训练职责逐项展开 +Accelerate:在保留训练循环的同时抽象设备与分布式执行 +``` + +学完后的标准不是“代码跑通”,而是能指出每层的输入、输出、隐藏职责和失败证据。 + +## 章节地图 + +| Section | 核心问题 | 应留下的证据 | +| --- | --- | --- | +| 3/1 Introduction | fine-tuning 相比 inference 新增了什么? | 能画出完整训练闭环 | +| 3/2 Processing the data | raw sample 如何变成形状一致、模型可消费的 batch? | sample keys、tokenized keys、batch shape、label mapping | +| 3/3 Trainer API | Trainer 替我们编排了哪些职责? | training/eval loss、metric、checkpoint、关键参数 | +| 3/4 Full training loop | 高层 API 隐藏了哪些 PyTorch 步骤? | forward、backward、optimizer、scheduler、zero-grad 顺序 | +| 3/4 Accelerate | 如何把设备与分布式细节移出业务训练循环? | `prepare()` 前后对象、device、实际进程/硬件 | +| 3/5 Learning Curves | 如何区分学到了、过拟合、欠拟合和训练不稳定? | train/eval curves、诊断结论、下一实验变量 | +| 3/6 Check | 能否把各节重新连成因果链? | 闭卷复述 | +| 3/7 Quiz | 能否识别监督微调的关键判断? | 用户自行答题,不归档答案 | + +## 3/2:数据处理不是格式转换,而是定义训练问题 + +### 课程示例的 Dataset ID 已发生漂移 + +课程正文仍展示: + +```python +raw_datasets = load_dataset("glue", "mrpc") +``` + +但在本项目当前环境 `datasets==5.0.0` 中,短名 `"glue"` 会在 Hub URI 解析阶段失败: + +```text +HfUriError: Repository id must be 'namespace/name', got 'glue' +``` + +当前应使用数据集仓库的完整 ID: + +```python +from datasets import load_dataset + +raw_datasets = load_dataset("nyu-mll/glue", "mrpc") +``` + +同一环境已验证它能加载: + +```text +train: 3668 +validation: 408 +test: 1725 +``` + +这里两个参数的职责不同: + +```text +"nyu-mll/glue" -> Hub dataset repository id(namespace/name) +"mrpc" -> 该 repository 内的 config / subset name +``` + +不要通过降级依赖来维持课程旧短名。优先查看当前官方 `load_dataset` 文档与 Hub 页面,使用规范、可定位的完整仓库 ID。课程代码是某个时间点的案例,不是永久稳定的 API contract。 + +### 从 DatasetDict 开始检查 + +官方示例加载 `glue` / `mrpc`,得到 train、validation、test 三个 split。第一步不要急着 tokenize,而要确认: + +```text +每行字段是什么? +哪个字段是输入? +哪个字段是监督信号? +label id 对应什么业务语义? +各 split 是否真的承担 train / selection / final test 的职责? +``` + +MRPC 的输入是 `sentence1 + sentence2`,label 表示两句话是否语义等价。`features` 中的 `ClassLabel` 才是 `0/1` 到语义名称的事实源,不能凭直觉猜。 + +### 句对必须作为句对 tokenize + +这两种操作不等价: + +```python +tokenizer(sentence1) +tokenizer(sentence2) +``` + +```python +tokenizer(sentence1, sentence2, truncation=True) +``` + +BERT 句对输入大致形成: + +```text +[CLS] sentence1 [SEP] sentence2 [SEP] +``` + +并可能带有 `token_type_ids`,用于区分两个 segment。不是所有架构都有这个字段;DistilBERT 就可能没有。正确原则不是“永远手写 `token_type_ids`”,而是让匹配的 tokenizer 根据 checkpoint 契约生成模型所需字段。 + +### `Dataset.map(..., batched=True)` 的作用 + +```python +def tokenize_function(examples): + return tokenizer( + examples["sentence1"], + examples["sentence2"], + truncation=True, + ) + +tokenized_datasets = raw_datasets.map(tokenize_function, batched=True) +``` + +这里有三层机制: + +1. `map()` 保留 Dataset / DatasetDict 结构,并把 preprocessing 返回的新字段加入各 split。 +2. `batched=True` 让 tokenizer 一次处理一批样本,发挥 fast tokenizer 的 Rust 实现与批处理优势。 +3. preprocessing 此时只做 truncation,不做全数据集 padding。 + +不要把 `batched=True` 理解为“已经生成训练 batch”。它只是批量执行预处理;真正的训练 batch 由 DataLoader + collator 在迭代时组成。 + +### Dynamic padding 为什么放到 collator + +若预处理时把所有样本 pad 到整个数据集的最大长度,短样本会产生大量无效 token 计算。动态 padding 推迟到组 batch 时: + +```text +dataset 中保留不同长度的 token ids +-> DataLoader 取出本 batch 样本 +-> DataCollatorWithPadding +-> 只 pad 到当前 batch 的最大长度 +``` + +因此 batch 中常见形状为: + +```text +input_ids: [batch_size, batch_max_seq_len] +attention_mask: [batch_size, batch_max_seq_len] +token_type_ids: [batch_size, batch_max_seq_len] # 仅某些模型 +labels: [batch_size] +``` + +代价与边界: + +- 不同 batch 的 sequence length 会变化,这是预期行为。 +- 动态 padding 通常减少无效计算,但 TPU 更偏好固定 shape;硬件约束可能改变最优策略。 +- 若需要 Tensor Core 友好的维度,可进一步考虑 `pad_to_multiple_of`,但应以实际 profiling 为准。 + +### 3/2 最小观察实验 + +先预测:句对 tokenizer 会新增什么字段?8 条不同长度样本经过 collator 后,第二维等于什么? + +必须打印: + +```python +print(raw_datasets) +print(raw_datasets["train"].features) +print(raw_datasets["train"][0]) +print(tokenized_datasets["train"].column_names) + +samples = tokenized_datasets["train"][:8] +print([len(x) for x in samples["input_ids"]]) +batch = data_collator(samples) +print({k: (v.shape, v.dtype) for k, v in batch.items()}) +``` + +通过标准:能解释 `map`、tokenizer、collator 各自在哪个时间点工作,且不把 preprocessing batch 与 training batch 混为一谈。 + +## 3/3:Trainer 是训练控制面,不是训练机制本身 + +`Trainer` 把许多机械职责组合起来: + +```text +TrainingArguments +model +train/eval datasets +data collator +processing class +compute_metrics +-> Trainer +-> train / evaluate / predict / save / log +``` + +### `TrainingArguments` 控制什么 + +至少要把参数分成几组,而不是把它看成一个巨大参数表: + +| 维度 | 典型参数 | 控制的问题 | +| --- | --- | --- | +| 产物 | `output_dir`, save strategy | checkpoint 写到哪里、何时保存 | +| 优化 | learning rate、epochs、weight decay | 参数如何更新、更新多久 | +| batch | per-device batch size、gradient accumulation | 单步显存与有效 batch size | +| evaluation | `eval_strategy`, `eval_steps` | 何时在 validation 上测量 | +| logging | logging steps、report target | 训练过程留下多少可诊断证据 | +| precision | fp16 / bf16 | 速度、显存与数值稳定性 | +| scheduler | scheduler type、warmup | learning rate 如何随 step 改变 | + +硬件参数必须以当前设备能力为准。不要因为课程展示 `fp16=True` 就在 MPS、CPU 或不支持的 GPU 上机械照搬。 + +### 只有 training loss 不等于知道模型效果 + +只调用 `trainer.train()` 可能只报告训练 loss。若想判断泛化能力,需要同时配置: + +```text +eval_dataset ++ eval_strategy ++ compute_metrics +``` + +`compute_metrics` 接收 predictions 与 label ids。分类任务通常先对 logits 做 `argmax(axis=-1)`,再计算 accuracy / F1。注意: + +- loss 是优化目标的连续信号; +- accuracy / F1 是任务层指标; +- 两者不能互相替代; +- MRPC 类别与数据分布决定应关注 accuracy、F1 或两者,不能只选看起来最大的数。 + +### 每次实验要重新初始化模型 + +如果复用已经训练过的 model 再调用 `train()`,得到的是续训,不是可比较的新实验。改变关键超参数做 A/B 实验时,应从同一 checkpoint 重新实例化 model,并尽量控制 seed、split 与其他变量。 + +### Trainer 最小观察实验 + +先预测:`predict()` 的 logits 和 labels 各是什么 shape?只设置 `eval_dataset` 而没有 `eval_strategy` 与 `compute_metrics` 会看到什么? + +必须保留: + +```text +训练参数快照 +train loss / eval loss +accuracy / F1 +runtime / samples per second +checkpoint 路径 +模型与 tokenizer 的 checkpoint 名称 +``` + +不要只截终端最后一行;结果必须能回答“训练了什么、怎样训练、效果如何、能否复现”。 + +## 3/4:手写训练循环是在揭开 Trainer 的抽象 + +### Trainer 自动做掉的前处理 + +进入纯 PyTorch 循环后,需要显式处理: + +```text +删除模型不接受的字符串列 +label -> labels +Dataset 输出格式 -> torch tensors +构造 train/eval DataLoader +训练集 shuffle,验证集不 shuffle +传入 collate_fn 做动态 padding +``` + +### `shuffle=True` 打乱的到底是什么 + +```python +train_dataloader = DataLoader( + tokenized_datasets["train"], + shuffle=True, + batch_size=8, + collate_fn=data_collator, +) +``` + +对于这种可以按索引访问的 map-style dataset,`shuffle=True` 会让 DataLoader 使用随机采样顺序。它打乱的是 dataset index: + +```text +原始索引:0, 1, 2, 3, 4, 5, 6, 7, ... +某个 epoch:5, 1, 7, 0, 3, 6, 2, 4, ... +``` + +然后才按照 `batch_size=8` 把这个索引序列切成 batch。每次重新迭代 DataLoader,也就是通常进入下一个 epoch 时,顺序会重新随机化。 + +它不会: + +- 修改 dataset 在磁盘或内存中的原始顺序; +- 打乱一句话内部的 token; +- 把 `input_ids` 与对应的 `labels` 拆开; +- 改变样本总数或类别含义。 + +一个样本的所有字段仍作为整体被取出,`data_collator` 随后才把 8 个完整样本 pad 并堆叠成张量。 + +训练集通常需要 shuffle,是因为数据可能按 label、长度、来源或时间排序。若不打乱,连续 batch 可能长期只包含相似样本,使每一步的梯度带有强烈顺序偏差;打乱后,每个 mini-batch 更有机会近似整体训练分布,并且不同 epoch 会看到不同的样本组合。 + +验证集通常写成: + +```python +eval_dataloader = DataLoader( + tokenized_datasets["validation"], + batch_size=8, + collate_fn=data_collator, +) +``` + +即默认 `shuffle=False`。验证阶段不更新参数,打乱不会提升学习;固定顺序更利于复现、定位具体错误样本和比较多次评估。对 accuracy/F1 这类与顺序无关的全量指标,shuffle 通常不会改变最终数值,但没有必要增加随机性。 + +最小观察实验:让 dataset 暂时返回或保留 `idx`,连续打印两个 epoch 的前两个 batch,预测 `idx` 顺序是否相同。若要做严格可复现实验,可显式给 DataLoader 传入带固定 seed 的 `torch.Generator`;但恢复训练、多 worker 和分布式采样还需要额外保存/控制随机状态。 + +第一批 batch 必须先试跑 model forward: + +```python +outputs = model(**batch) +print(outputs.loss) +print(outputs.logits.shape) +``` + +### `model(**batch)` 是 Python 的关键字参数解包 + +假设 collator 生成的 `batch` 是: + +```python +batch = { + "input_ids": input_ids_tensor, + "attention_mask": attention_mask_tensor, + "token_type_ids": token_type_ids_tensor, + "labels": labels_tensor, +} +``` + +在函数调用位置,`**mapping` 会把 mapping 的每个 key/value 展开成“参数名=参数值”。因此: + +```python +outputs = model(**batch) +``` + +等价于: + +```python +outputs = model( + input_ids=batch["input_ids"], + attention_mask=batch["attention_mask"], + token_type_ids=batch["token_type_ids"], + labels=batch["labels"], +) +``` + +这也是为什么前面要把数据列 `label` 改名为 `labels`,并删除 `sentence1`、`sentence2`、`idx`:解包后的 key 必须是模型调用所接受的参数名。若 batch 含有模型不认识的 key,通常会得到 `unexpected keyword argument`;若缺少任务所需的 `labels`,模型仍可能返回 logits,但不会自动计算监督 loss。 + +这不是 Transformers 专属语法。普通 Python 函数同样适用: + +```python +def add(x, y): + return x + y + +values = {"x": 2, "y": 3} +add(**values) # 等价于 add(x=2, y=3) +``` + +与它对应,`*sequence` 解包的是位置参数: + +```python +add(*[2, 3]) # 等价于 add(2, 3) +add(**values) # 等价于 add(x=2, y=3) +``` + +更底层地说,`model(...)` 调用的是 PyTorch `nn.Module` 的调用入口,它再执行模型的 `forward(...)`,并保留 hooks 等 Module 机制;正常代码应写 `model(**batch)`,不要绕过调用入口直接写 `model.forward(**batch)`。 + +Notebook 中下一行: + +```python +outputs.loss, outputs.logits.shape +``` + +只是构造一个包含两个值的 tuple,让 notebook 同时显示 loss 和 logits shape;它与 `**` 解包无关。 + +它是训练前最便宜的契约检查:字段名、shape、label、head 的类别数和 forward 能否闭合,应在长时间训练前暴露。 + +### 一步训练的因果顺序 + +```python +model.train() +for batch in train_dataloader: + batch = {k: v.to(device) for k, v in batch.items()} + outputs = model(**batch) + loss = outputs.loss + loss.backward() + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() +``` + +逐步理解: + +1. `model.train()` 打开 dropout 等训练行为。 +2. forward 产生 logits,并在提供 `labels` 时计算 loss。 +3. `backward()` 沿计算图把梯度累积到参数的 `.grad`。 +4. `optimizer.step()` 使用当前梯度更新参数。 +5. `scheduler.step()` 推进学习率计划。 +6. `zero_grad()` 清除本步梯度,否则下一步会继续累积。 + +“梯度累积”正是有意不在每个 micro-batch 后 step/zero 的策略,因此不能把 `zero_grad()` 当无意义样板代码。 + +### 训练循环中哪些顺序必须保持 + +课程完整代码还包含进度条与 epoch: + +```python +progress_bar = tqdm(range(num_training_steps)) + +model.train() +for epoch in range(num_epochs): + for batch in train_dataloader: + batch = {k: v.to(device) for k, v in batch.items()} + outputs = model(**batch) + loss = outputs.loss + loss.backward() + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) +``` + +先区分“训练因果链”和“外围控制”: + +```text +外围:tqdm、epoch/batch 循环、日志 +因果链:device -> forward -> loss -> backward -> optimizer -> scheduler -> clear grad +``` + +必须满足的顺序: + +1. batch 与 model 必须在兼容的 device 上,才能 forward。 +2. 必须先 forward 得到 loss,才能从该计算图执行 backward。 +3. 必须先 backward 产生梯度,optimizer 才有本步梯度可用。 +4. `optimizer.step()` 使用当前 learning rate 更新参数。 +5. 对课程中每个 optimizer step 都更新一次的 linear scheduler,应在 `optimizer.step()` 后调用;反过来可能跳过初始 learning-rate 值。 +6. 下一次非梯度累积的 backward 前,必须清掉旧梯度,因为 PyTorch 的 `.grad` 默认累加而不是覆盖。 + +可以调整的位置: + +- `optimizer.zero_grad()` 可放在本步结尾,也可放在下一步 forward/backward 之前。两种写法都正确,关键是每个独立 optimizer update 只消费预期的梯度。 +- `model.train()` 放在全部 epoch 外面可以,因为模式会保持;如果中途执行过 `model.eval()` 做验证,下一轮训练前必须再次调用 `model.train()`。 +- `progress_bar.update(1)` 不参与数值计算。这里一批对应一次 optimizer update,所以在成功更新后加一最直观。 +- scheduler 的调用频率取决于类型:课程的 linear scheduler 按 optimizer step;某些 scheduler 按 epoch;`ReduceLROnPlateau` 则通常在 validation 后接收 metric。不能只凭变量名决定位置。 + +`zero_grad()` 放开头的等价常见写法: + +```python +model.train() +for batch in train_dataloader: + optimizer.zero_grad() + batch = {k: v.to(device) for k, v in batch.items()} + outputs = model(**batch) + loss = outputs.loss + loss.backward() + optimizer.step() + lr_scheduler.step() +``` + +加入 gradient clipping 时,它必须放在梯度产生之后、参数更新之前: + +```text +backward +-> clip gradients +-> optimizer.step +``` + +加入 gradient accumulation 时,`backward()` 仍每个 micro-batch 执行,但 `optimizer.step()`、`scheduler.step()` 和 `zero_grad()` 只在累积窗口结束时执行。此时 `num_training_steps` 和进度条应该数 optimizer updates,而不是所有 micro-batches;否则 scheduler 会走得过快,进度条也会失真。 + +### optimizer 与 scheduler 的关系 + +AdamW 决定如何把梯度变成参数更新;scheduler 决定每一步使用多大的 learning rate。训练总步数通常是: + +```text +num_epochs * len(train_dataloader) +``` + +但加入 gradient accumulation、drop-last、分布式切分或 resume 后,真实 optimizer steps 可能不再等同于看到的 micro-batch 数。迁移到自己的训练脚本时必须校准“scheduler 的 step 到底对应什么”。 + +### evaluation loop 的关键切换 + +```text +model.eval() +torch.no_grad() +forward +logits -> predictions +metric.add_batch(...) +metric.compute() +``` + +`eval()` 改变 dropout 等模块行为;`no_grad()` 停止构建反向图、减少显存与计算。两者职责不同,都需要。 + +### Accelerate 抽象了什么 + +Accelerate 没有消灭训练循环,而是接管设备与分布式相关机械工作: + +```python +accelerator = Accelerator() +train_dl, eval_dl, model, optimizer = accelerator.prepare( + train_dataloader, eval_dataloader, model, optimizer +) + +# loss.backward() -> accelerator.backward(loss) +``` + +核心变化: + +- `prepare()` 包装 model、optimizer、dataloader,使它们匹配当前执行环境; +- batch 不再需要手动 `.to(device)`; +- backward 经过 Accelerate,以便正确处理 mixed precision / distributed setup; +- 启动方式与环境配置仍决定实际用了几个进程和什么硬件,不能因为 import 了 Accelerate 就宣称完成分布式训练。 + +通过标准:能把 Trainer 的一个配置项或行为映射到手写 loop 中的具体对象与语句。 + +## 3/5:Learning curves 是训练系统的反馈,不是装饰图 + +### loss 与 accuracy 为什么形状不同 + +loss 是连续信号。模型即使还没改变最终类别,只要对正确类别的置信度有所改善,loss 就可能下降。 + +accuracy 是离散决策。只有预测跨过类别边界,正确样本数才改变,所以曲线常呈台阶状。不要因 accuracy 暂时平坦就立即判断“模型完全没学”。 + +### 诊断矩阵 + +| 现象 | 更可能的解释 | 下一步只改变一个变量 | +| --- | --- | --- | +| train/eval loss 都下降且接近 | 健康学习并逐渐收敛 | 观察是否已到收益平台 | +| train loss 继续下降,eval loss 上升 | 过拟合 | early stopping、weight decay、dropout、更多/更好数据 | +| train/eval loss 都高且早早平台 | 欠拟合或数据/优化问题 | 检查数据,再尝试训练更久、容量或 learning rate | +| loss 高频震荡、出现尖峰 | learning rate、batch、梯度或脏数据问题 | 先查 batch/data,再降低 LR、增大 batch、clip grad | +| loss 下降但业务指标不升 | 优化目标与决策指标未同步,或阈值/类别问题 | 看 confusion matrix、per-class metric 与样本错误 | +| validation 很好但线上失败 | split 泄漏、分布偏移或指标不代表产品目标 | 重做 split 与业务 eval set | + +### 对官网建议保持批判性 + +课程给的是诊断候选,不是单一因果答案。同一种曲线可能由多种原因造成: + +- “欠拟合”可能是模型太小,也可能是 label 错、preprocessing 错或 learning rate 不合适; +- “震荡”可能来自学习率,也可能来自小 batch、异常样本、数值精度或日志粒度; +- 增大模型、训练更久、增加 batch 都有成本,不应一次全改。 + +官网 erratic-curves 示例的文字说“降低 learning rate”,但展示的 diff 从 `1e-5` 改成 `1e-4`,数值方向其实是升高。学习时应遵循机制与实验设计,不把示例代码当 golden output:若诊断是 LR 过高,应设计降低 LR 的单变量对照实验。 + +### 最小实验纪律 + +每次调参记录: + +```text +hypothesis:我认为哪条曲线说明了什么? +single change:只改变哪个变量? +expected signal:若判断正确,哪条曲线应该怎样变化? +observed result:实际曲线和最终指标如何? +decision:保留、回滚,还是提出下一个假设? +``` + +这一步是从“会训练”走向“会做训练实验”的分界。 + +## Trainer、手写循环与 Accelerate 怎么选 + +| 选择 | 适合 | 优势 | 风险 | +| --- | --- | --- | --- | +| Trainer | 标准监督任务、快速可靠 baseline | 少样板、内置 eval/save/log/distributed | 容易不知道默认值和隐藏行为 | +| Pure PyTorch | 非标准 loss、特殊 batch/step、机制学习 | 控制强、每一步可观察 | 易漏 eval mode、zero-grad、device、checkpoint 等细节 | +| Accelerate + loop | 需要自定义 loop 且要跨设备/分布式 | 保留控制力,减少硬件样板 | 仍需理解分布式 metric、进程与保存语义 | + +对当前 `lora-feedback-loop` topic,推荐顺序: + +```text +先用 Trainer 建可重复 baseline +-> 用最小手写 loop 解释隐藏职责 +-> 回到 Trainer / PEFT 做 LoRA 实验 +-> 只有需要自定义训练行为时才扩大手写 loop +``` + +这不是“高层 API 不专业”。专业判断是知道抽象隐藏了什么、何时足够、何时必须打开。 + +## 迁移到 LoRA / suggestion-next-action 任务 + +Chapter 3 的 BERT 分类只教监督微调骨架。迁移到当前业务任务时,以下部分可以忠实复用: + +```text +明确 train/validation/test split +可批处理的 preprocessing +collator 负责 batch shape +训练参数与 checkpoint 产物化 +train/eval 分离 +learning curves 与单变量实验 +baseline -> changed model -> compare +``` + +以下部分不能机械照搬: + +- MRPC 是二分类,suggestion next action 是结构化生成;model head、labels shape、loss 和 metric 都不同。 +- `accuracy/F1` 不能衡量建议是否推动购买、安全、相关且语义完整;需要业务 rubric、格式合规率和人工/模型评审的一致性校准。 +- 全参数 BERT fine-tuning 不等于 LoRA;LoRA 冻结大部分原参数,只训练低秩 adapter,但 optimizer/backward/eval 的训练闭环仍然成立。 +- 动态 padding、precision、batch size 和 gradient accumulation 的选择必须按生成模型长度分布与实际硬件重新 profiling。 + +## 本章建议的学习顺序 + +Chapter 3 已由用户确认读完。本文保留为 fine-tuning 机制与实验恢复入口;3/2-3/5 尚未明确提供的运行和曲线诊断证据继续保留,不冒充已验收。当前课程游标已推进到 Chapter 5/2,后续 Chapter 3 问题按需回看本文。 + +```text +Checkpoint A — Data contract +能解释 MRPC raw row、label mapping、sentence-pair tokenization、dynamic padding + +Checkpoint B — Trainer baseline +能跑通训练/eval,并保存参数、metric、checkpoint 证据 + +Checkpoint C — Full loop derivation +能闭卷写出 forward -> backward -> optimizer -> scheduler -> zero-grad + +Checkpoint D — Learning curve diagnosis +能基于 train/eval 曲线提出单变量实验,而不是拍脑袋调参 + +Checkpoint E — Transfer +能指出迁移到 LoRA 生成任务时哪些环节不变、哪些任务契约必须重做 +``` + +## Chapter 3 通过标准 + +不看教程,能够回答: + +1. `Dataset.map(batched=True)` 与 DataLoader batch 有什么区别? +2. 为什么 padding 通常推迟到 collator?什么时候可能不这样做? +3. tokenizer 为什么可能返回或不返回 `token_type_ids`? +4. Trainer 替你完成了哪些数据、优化、评估、保存与设备职责? +5. `loss.backward()`、`optimizer.step()`、`scheduler.step()`、`zero_grad()` 的顺序和作用是什么? +6. `model.eval()` 与 `torch.no_grad()` 有什么区别? +7. Accelerate 的 `prepare()` 与 `backward()` 抽象了什么,又没有证明什么? +8. 为什么 loss 下降时 accuracy 可能不变? +9. 如何从曲线区分过拟合、欠拟合与训练不稳定? +10. 从 MRPC/BERT 迁移到 LoRA 结构化生成时,哪些机制保留,哪些契约必须重建? + +## 来源 + +- [Chapter 3/1: Introduction](https://huggingface.co/learn/llm-course/en/chapter3/1) +- [Chapter 3/2: Processing the data](https://huggingface.co/learn/llm-course/en/chapter3/2) +- [Chapter 3/3: Fine-tuning a model with the Trainer API](https://huggingface.co/learn/llm-course/en/chapter3/3) +- [Chapter 3/4: A full training loop](https://huggingface.co/learn/llm-course/en/chapter3/4) +- [Chapter 3/5: Understanding Learning Curves](https://huggingface.co/learn/llm-course/en/chapter3/5) +- [Chapter 3/6: Fine-tuning, Check!](https://huggingface.co/learn/llm-course/en/chapter3/6) +- [Chapter 3/7: End-of-chapter Certificate](https://huggingface.co/learn/llm-course/en/chapter3/7) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/18-chapter5-2-local-remote-dataset-loading.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/18-chapter5-2-local-remote-dataset-loading.md new file mode 100644 index 0000000..c378f85 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/18-chapter5-2-local-remote-dataset-loading.md @@ -0,0 +1,103 @@ +# Chapter 5/2:本地与远程数据加载排障 + +## Lesson Lab + +- Lesson:用 `load_dataset("json", data_files=..., field="data")` 加载 SQuAD-it。 +- 本节概念目标:区分 loader format、数据来源、split mapping 与嵌套字段入口。 +- 用户先预测:同一组文件分别从 GitHub URL、最终 raw URL 和本地 `.json.gz` 加载时,应得到相同的 `DatasetDict`。 +- 必须打印:实际 URL 的 `repr()`、Python 可执行文件、`datasets` 版本、split、rows 与 columns。 +- Agent 实测:三种来源都得到 `train=442`、`test=48`;这不是用户已经完成的观察证明。 +- 下一迁移:换一个远程 CSV 或 JSON 数据集,独立验证 format、source、split 与 schema。 + +## 这次 `FileNotFoundError` 说明了什么 + +用户遇到: + +```text +FileNotFoundError: Unable to find 'https://github.com/.../SQuAD_it-train.json.gz' +``` + +这个异常发生在 `load_dataset()` 创建 builder、解析 `data_files` 的阶段,还没有进入 JSON 内容解析。因此它表达的是“当前这次 URL 探测没有把输入解析成可用文件”,不等于服务器上的文件永久不存在。 + +课程原代码仍在官方页面中。2026-07-21 使用 traceback 指向的同一个项目 `.venv` 重新运行时,以下三种输入均成功: + +```text +GitHub /raw/ URL -> train 442, test 48 +raw.githubusercontent -> train 442, test 48 +local .json.gz -> train 442, test 48 +``` + +所以当前证据不支持“Datasets API 已经变了”或“该链接失效”。更符合证据的判断是:先前那次远程文件探测受到了临时网络响应、GitHub 跳转链路或 notebook 内核现场的影响。 + +另外,聊天中显示的 `[URL](URL)` 是界面自动链接化;检查 notebook 后,实际变量中保存的是纯 URL,不是 Markdown 字符串。 + +## 为什么浏览器或 `wget` 能下载,loader 仍可能失败 + +它们不是同一条调用链: + +```text +浏览器 / wget + -> HTTP 请求 + -> 跟随 GitHub 重定向 + -> 下载字节 + +load_dataset + -> 识别 json loader + -> 解析 data_files + -> 远程文件存在性与路径探测 + -> 下载和解压 + -> 从 field="data" 构建 Arrow dataset +``` + +浏览器成功只证明 URL 在那次请求中可下载,不保证 loader 的预检在另一次请求中一定成功。 + +## 推荐恢复顺序 + +先原样重跑;如果仍失败,打印真实运行现场: + +```python +import sys +import datasets + +print(sys.executable) +print(datasets.__version__) +print(repr(data_files["train"])) +``` + +然后改用不经过 GitHub 页面跳转的最终 raw 地址: + +```python +from datasets import load_dataset + +url = "https://raw.githubusercontent.com/crux82/squad-it/master/" +data_files = { + "train": url + "SQuAD_it-train.json.gz", + "test": url + "SQuAD_it-test.json.gz", +} +squad_it_dataset = load_dataset("json", data_files=data_files, field="data") +``` + +如果远程链路仍不稳定,就使用已经下载的压缩文件;Datasets 会自动解压: + +```python +data_files = { + "train": "SQuAD_it-train.json.gz", + "test": "SQuAD_it-test.json.gz", +} +squad_it_dataset = load_dataset("json", data_files=data_files, field="data") +``` + +最后检查数据契约: + +```python +print(squad_it_dataset) +print(squad_it_dataset["train"].column_names) +print(squad_it_dataset["train"][0].keys()) +``` + +预期是两个 split,行数分别为 442 和 48,顶层列为 `title`、`paragraphs`。`field="data"` 的作用是进入原始 JSON 顶层的 `data` 数组,而不是选择 DatasetDict 的 split。 + +## 来源 + +- [Hugging Face LLM Course Chapter 5/2](https://huggingface.co/learn/llm-course/en/chapter5/2) +- [Hugging Face Datasets:Load](https://huggingface.co/docs/datasets/loading) diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/README.md new file mode 100644 index 0000000..2543164 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/04-lesson-lab/README.md @@ -0,0 +1,73 @@ +# 04 Lesson Lab:Transformers 可观察实验 + +本阶段把已经跑通的 Hugging Face Course 示例转成可观察实验。目标不是继续抄 recipe,而是让用户能解释每个对象在训练链路里的职责、输入输出和可验证证据。 + +## Current Lesson + +当前 lesson: + +```text +Chapter 1/5 task lab sweep + -> completed text-task core through summarization + -> skip/defer Translation / ASR / Image classification + -> Chapter 1/6 Transformer Architectures / attention mechanisms + -> complete Chapter 1/8 and Chapter 2 (user-confirmed) + -> complete reading Chapter 3/1-3/3 + -> complete reading Chapter 3 and Chapter 4 + -> complete Chapter 5/1 Introduction + -> enter Chapter 5/2 non-Hub dataset loading + -> map format + local/remote source + split + schema into DatasetDict +``` + +## Lesson Sequence + +| Lesson | Guide | 目标 | +| --- | --- | --- | +| Pipeline foundation | [`01-hf-course-ch1-ch2-foundation.md`](01-hf-course-ch1-ch2-foundation.md) | 把 `pipeline` 展开成 tokenizer、model、postprocess | +| Causal LM recipe | [`02-causal-lm-code-reading.md`](02-causal-lm-code-reading.md) | 解释 dataset -> tokenizer -> blocks -> labels | +| Trainer loop | [`03-trainer-train-under-the-hood.md`](03-trainer-train-under-the-hood.md) | 观察 batch -> forward -> loss -> backward | +| Sequence classification | [`04-sequence-classification-derivation.md`](04-sequence-classification-derivation.md) | 从任务契约反推 tokenizer、collator、model、metric、Trainer | +| Chapter 1/5 task sweep | [`05-chapter1-5-task-lab-sweep.md`](05-chapter1-5-task-lab-sweep.md) | 完成文本主线任务观察,记录剩余 lab 的跳过/延后边界 | +| Token classification | [`06-token-classification-derivation.md`](06-token-classification-derivation.md) | 理解 word-level 标签如何对齐到 token-level logits | +| Question answering | [`07-question-answering-derivation.md`](07-question-answering-derivation.md) | 理解字符级 answer span 如何对齐到 token 级 start/end positions | +| Summarization task shape | [`08-summarization-derivation.md`](08-summarization-derivation.md) | 理解 encoder-decoder 如何把长输入转换成短生成文本 | +| T5 task prefix | [`09-t5-task-prefix.md`](09-t5-task-prefix.md) | 理解 `summarize: ` 如何作为任务提示进入 input ids | +| BillSum dataset loading | [`10-billsum-dataset-loading.md`](10-billsum-dataset-loading.md) | 记录 BillSum 完整 ID、split 和 notebook 变量覆盖边界 | +| ROUGE metric | [`11-rouge-metric.md`](11-rouge-metric.md) | 理解 summarization 自动评估的重叠指标和边界 | +| Mixed precision | [`12-mixed-precision-fp16-bf16.md`](12-mixed-precision-fp16-bf16.md) | 区分 `fp16`、`bf16`、XPU 和本地 MPS 排障默认值 | +| Summarization metric debug | [`13-summarization-compute-metrics-overflow.md`](13-summarization-compute-metrics-overflow.md) | 排查 `batch_decode(predictions)` 的 `OverflowError` | +| Summarization inference | [`14-summarization-inference-generate.md`](14-summarization-inference-generate.md) | 理解 tokenizer、`generate`、`max_new_tokens`、`decode` 的推理链路 | +| Chapter 1/6 architectures | [`15-chapter1-6-transformer-architectures.md`](15-chapter1-6-transformer-architectures.md) | 把已做过的任务映射到 encoder-only、decoder-only、encoder-decoder | +| Chapter 1/8 LLM inference | [`16-chapter1-8-llm-inference.md`](16-chapter1-8-llm-inference.md) | 理解 prompt -> prefill -> decode -> sampling -> KV cache 的推理主线 | +| Chapter 3 fine-tuning | [`17-chapter3-finetuning-pretrained-model.md`](17-chapter3-finetuning-pretrained-model.md) | 从数据契约到 Trainer、full loop、Accelerate、learning curves 与 LoRA 迁移 | +| Chapter 4 Hub sharing | [官方 Chapter 4](https://huggingface.co/learn/llm-course/en/chapter4/1) | Hub 仓库、预训练模型复用、分享与 model card;已读,实践待验收 | +| Chapter 5 Datasets | [`18-chapter5-2-local-remote-dataset-loading.md`](18-chapter5-2-local-remote-dataset-loading.md) | 从本地/远程文件加载 DatasetDict,并排查 URL 探测、split mapping 与 JSON `field` | + +## Required Observation + +用户需要亲自运行或复查 notebook,并把观察写入 [`../../notes/04-lesson-lab/`](../../notes/04-lesson-lab/) 下的独立 note 文件;[`../../notes/04-lesson-lab/README.md`](../../notes/04-lesson-lab/README.md) 只做索引: + +- chapter1/5 text-task sweep:已完成 lab 的 input、batch keys/shape、model class、logits/generated output、postprocess、与上一个 task 的差异 +- chapter1/6 architecture summary:encoder-only、decoder-only、encoder-decoder 的 attention 可见范围、典型任务、head / decoder 和输出形态 +- chapter1/8 LLM inference:prefill/decode、sampling controls、TTFT/TPOT/throughput/VRAM 和 KV cache +- pipeline internals:tokenizer 输出、model logits、softmax scores、argmax label、`id2label` 映射 +- sequence classification:raw sample keys、tokenized sample keys、collated batch shapes、`outputs.loss`、`outputs.logits.shape` +- question answering:`offset_mapping`、`sequence_ids`、`start_positions/end_positions`、`start_logits/end_logits` 和 decoded span +- summarization:`input_ids/attention_mask/labels` shape、`outputs.logits.shape`、`generated_ids` 和 decoded summary +- causal LM / Trainer:`batch.keys()`、`input_ids / attention_mask / labels` 的 shape、dtype、device、shifted loss、至少一个 token 位置的 top-k prediction +- chapter5/2:当前打印 loader format、local/remote source、`data_files`、split、rows、columns 与 nested field;Chapter 3/4 未明确提供的实践证据继续保留 + +## Exit Criteria + +完成本阶段前,用户应能不用教程解释: + +```text +不同 task 如何改变输入、head、loss 和 postprocess +Pipeline 如何展开成 tokenizer -> model -> postprocess +Dataset 提供样本 +Collator 组装 batch +Model forward 计算 logits/loss +Trainer 驱动 backward、optimizer、eval、save +``` + +如果 lesson lab 的观察仍无法解释 hidden mechanism,再进入 `05-mechanism-deep-dive`。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/05-mechanism-deep-dive/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/05-mechanism-deep-dive/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/01-candidate-samples-v0.1.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/01-candidate-samples-v0.1.md new file mode 100644 index 0000000..544ef96 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/01-candidate-samples-v0.1.md @@ -0,0 +1,438 @@ +# 第一批候选样本 v0.1 + +用途:供用户校准 `keep / revise / weak / reject` 审核口径。当前样本还不是训练集,审核后才进入正式 dataset。 + +审核时优先看: + +- suggestion 是否具体、可执行。 +- reason 是否解释了阶段、风险或用户卡点。 +- 是否真的帮助用户往“买到自己要的商品”前进一步。 +- 是否存在事实错误、安全风险、太泛或脱离阶段的问题。 + +补充审核维度: + +- 样本是 typical、ambiguous、boundary,还是 negative / unsafe? +- 这个 case 里应该继续推进、补证据、继续比较、议价、暂停避开,还是进入维权? +- 如果建议太武断,例如信息不足却直接推荐购买,也应标为 `weak` 或 `reject`。 + +后续进入正式 dataset 时,`label_meta` 会补充: + +```json +{ + "case_type": "ambiguous", + "decision_mode": "ask_for_evidence" +} +``` + +## 样本 01 + +```json +{ + "sample_id": "phone_seed_001", + "stage": "need_clarification", + "task_goal": "买一台二手 iPhone 作为主力机", + "context": "用户预算大约 3500-4500 元,只说想买 iPhone,但没有说明用途、容量、续航要求和是否接受维修机。", + "target": { + "suggestion": "先确认你更看重拍照、续航、容量还是保修,再把预算拆成 2-3 个可接受机型范围。", + "reason": "当前需求还不清晰,直接看商品容易被单个低价吸引;先明确用途和硬性约束,才能避免后面对比时标准摇摆。" + }, + "label_meta": { + "stage_fit": "need_clarification", + "risk_tags": ["unclear_requirements", "budget_scope"], + "priority": "high" + } +} +``` + +## 样本 02 + +```json +{ + "sample_id": "phone_seed_002", + "stage": "need_clarification", + "task_goal": "给父母买一台二手安卓手机", + "context": "用户只说要便宜耐用,候选商品还没开始找,不确定是否需要大字体、长续航、双卡和售后方便。", + "target": { + "suggestion": "先列出父母每天最常用的 3 个场景,比如微信视频、拍照、刷短视频或导航,再确定电池、屏幕和存储底线。", + "reason": "给父母买手机时,参数强弱不是唯一标准;先确认真实使用场景,能避免买到便宜但续航、屏幕或存储不合适的机型。" + }, + "label_meta": { + "stage_fit": "need_clarification", + "risk_tags": ["usage_mismatch", "battery_life"], + "priority": "medium" + } +} +``` + +## 样本 03 + +```json +{ + "sample_id": "phone_seed_003", + "stage": "search", + "task_goal": "搜索一台二手 iPhone 14 Pro 256G", + "context": "用户已经确定预算 4000 左右,希望国行、无拆修、电池健康 85% 以上,但搜索结果很多,价格跨度从 3300 到 4600。", + "target": { + "suggestion": "先用“国行 256G 无拆修 电池85以上 当面验机”作为搜索条件,并把明显低于市场价太多的结果单独标记为高风险。", + "reason": "当前处于搜索阶段,先用硬性条件过滤能减少噪音;异常低价常伴随维修、锁机、监管机或描述不完整风险。" + }, + "label_meta": { + "stage_fit": "search", + "risk_tags": ["abnormal_low_price", "filtering"], + "priority": "high" + } +} +``` + +## 样本 04 + +```json +{ + "sample_id": "phone_seed_004", + "stage": "search", + "task_goal": "找一台二手小米或红米备用机", + "context": "用户预算 800 元以内,想要续航好,但搜索时只按价格从低到高排序。", + "target": { + "suggestion": "把搜索条件改成“大电池、内存 8G 起、存储 128G 起、支持线下验机”,再筛掉描述里没有电池和成色信息的商品。", + "reason": "备用机最容易被低价吸引,但续航和存储才决定实际体验;缺少关键信息的低价商品会增加后续沟通和踩坑成本。" + }, + "label_meta": { + "stage_fit": "search", + "risk_tags": ["low_price_bias", "missing_condition_info"], + "priority": "medium" + } +} +``` + +## 样本 05 + +```json +{ + "sample_id": "phone_seed_005", + "stage": "shortlist", + "task_goal": "从 8 个 iPhone 候选里先筛掉明显不合适的", + "context": "候选商品中有几台价格便宜,但描述包含“美版有锁”“换过屏”“商家批量出”等字样。", + "target": { + "suggestion": "先把“有锁机、换屏未说明来源、商家批量出且不支持当面验机”的商品移出候选列表。", + "reason": "初筛阶段应先排除高风险选项,而不是在所有商品上平均花时间;锁机、屏幕更换和无法验机会显著提高交易风险。" + }, + "label_meta": { + "stage_fit": "shortlist", + "risk_tags": ["locked_phone", "screen_replacement", "seller_risk"], + "priority": "high" + } +} +``` + +## 样本 06 + +```json +{ + "sample_id": "phone_seed_006", + "stage": "shortlist", + "task_goal": "筛选二手安卓旗舰", + "context": "用户看到几台骁龙旗舰,价格接近,但有的只写“功能正常”,没有任何成色、维修或电池说明。", + "target": { + "suggestion": "优先保留描述里明确写出成色、维修史、电池状态和支持验机的商品,把只写“功能正常”的商品降级处理。", + "reason": "初筛不是选最便宜,而是选信息透明度更高的候选;信息越少,后续隐藏维修或成色争议的概率越高。" + }, + "label_meta": { + "stage_fit": "shortlist", + "risk_tags": ["information_asymmetry", "repair_history"], + "priority": "medium" + } +} +``` + +## 样本 07 + +```json +{ + "sample_id": "phone_seed_007", + "stage": "compare", + "task_goal": "比较三台 iPhone 14 Pro", + "context": "三台价格分别为 3880、4050、4180 元。最低价电池 82%,中间价电池 88% 但无验机报告,最高价有原盒和保修截图。", + "target": { + "suggestion": "先不要只看价格,把三台按电池健康、保修证据、验机支持和维修记录做一张对比表。", + "reason": "当前价格差异不大,真正影响决策的是风险证据和后续使用成本;电池偏低或缺少验机证据可能抵消低价优势。" + }, + "label_meta": { + "stage_fit": "compare", + "risk_tags": ["battery_health", "warranty_evidence", "comparison"], + "priority": "high" + } +} +``` + +## 样本 08 + +```json +{ + "sample_id": "phone_seed_008", + "stage": "compare", + "task_goal": "在 iPhone 13 Pro 和 iPhone 14 之间选择", + "context": "用户纠结性能、拍照和价格,候选商品价格接近,但没有考虑屏幕、重量、续航和维修成本。", + "target": { + "suggestion": "把选择标准从“哪台更新”改成“高刷屏、长焦拍照、续航、维修成本”四项,按你的真实使用频率打分。", + "reason": "当前是跨机型对比,单看年份容易误判;用使用场景打分能让建议从参数比较推进到个人决策。" + }, + "label_meta": { + "stage_fit": "compare", + "risk_tags": ["model_tradeoff", "usage_fit"], + "priority": "medium" + } +} +``` + +## 样本 09 + +```json +{ + "sample_id": "phone_seed_009", + "stage": "item_understanding", + "task_goal": "了解一台标称无拆修的 iPhone", + "context": "卖家展示了外观图和电池健康截图,但没有主板、屏幕、摄像头和 Face ID 状态说明。", + "target": { + "suggestion": "让卖家补充关于屏幕、Face ID、摄像头、扬声器和是否拆修的功能检查视频。", + "reason": "当前商品信息还停留在外观和电池,关键功能缺少证据;这些项目一旦有问题,维修成本和使用体验都会明显受影响。" + }, + "label_meta": { + "stage_fit": "item_understanding", + "risk_tags": ["function_check", "repair_history"], + "priority": "high" + } +} +``` + +## 样本 10 + +```json +{ + "sample_id": "phone_seed_010", + "stage": "item_understanding", + "task_goal": "确认一台安卓手机是否值得继续沟通", + "context": "商品描述写着“轻微烧屏不影响使用”,价格比同款低 300 元。", + "target": { + "suggestion": "让卖家在纯白、纯灰和深色背景下各拍一张屏幕照片,再确认烧屏位置和日常使用是否明显。", + "reason": "烧屏问题仅靠一句“不影响使用”无法判断;不同背景照片能帮助评估真实程度,决定低价是否值得接受。" + }, + "label_meta": { + "stage_fit": "item_understanding", + "risk_tags": ["screen_burn_in", "condition_uncertainty"], + "priority": "high" + } +} +``` + +## 样本 11 + +```json +{ + "sample_id": "phone_seed_011", + "stage": "seller_chat", + "task_goal": "向卖家追问 iPhone 维修和锁机风险", + "context": "用户担心直接问太多显得麻烦,卖家目前只回复“机器没问题”。", + "target": { + "suggestion": "可以礼貌地问:“方便录个设置页、关于本机、Apple ID 已退出和查找已关闭的视频吗?我主要想确认不是监管机或隐藏锁。”", + "reason": "当前进入卖家沟通阶段,具体而礼貌的问法能降低对方抵触,同时直接索取排除锁机风险的关键证据。" + }, + "label_meta": { + "stage_fit": "seller_chat", + "risk_tags": ["id_lock", "managed_device", "communication"], + "priority": "high" + } +} +``` + +## 样本 12 + +```json +{ + "sample_id": "phone_seed_012", + "stage": "seller_chat", + "task_goal": "和卖家沟通线下验机", + "context": "卖家同城,支持面交,但用户不知道如何提出验机要求。", + "target": { + "suggestion": "先问卖家是否接受在公开地点面交,并预留 15 分钟现场检查屏幕、摄像头、Face ID、充电和网络功能。", + "reason": "线下交易的关键不是只见面付款,而是提前约定检查项目和时间;这样能减少现场仓促付款后的争议。" + }, + "label_meta": { + "stage_fit": "seller_chat", + "risk_tags": ["offline_inspection", "transaction_safety"], + "priority": "high" + } +} +``` + +## 样本 13 + +```json +{ + "sample_id": "phone_seed_013", + "stage": "risk_check", + "task_goal": "确认低价 iPhone 是否有隐藏风险", + "context": "商品比同款低 600 元,卖家说“急出”,但不愿提供序列号截图,也不支持验机。", + "target": { + "suggestion": "把这台先标为高风险,不要继续议价;优先追问为什么不能提供序列号截图和验机证据。", + "reason": "异常低价叠加拒绝提供证据,是二手交易中的强风险信号;此时继续压价不如先确认是否存在锁机、维修或来源问题。" + }, + "label_meta": { + "stage_fit": "risk_check", + "risk_tags": ["abnormal_low_price", "evidence_refusal"], + "priority": "high" + } +} +``` + +## 样本 14 + +```json +{ + "sample_id": "phone_seed_014", + "stage": "risk_check", + "task_goal": "判断一台“官换机”描述是否可信", + "context": "卖家说手机是官换机,价格合理,但没有提供保修记录或官方换机凭证。", + "target": { + "suggestion": "让卖家提供可核验的保修记录、换机凭证或序列号查询截图,不要只接受“官换机”口头描述。", + "reason": "官换机会影响价值判断,但必须有证据支撑;没有凭证时,它可能只是卖家解释维修或来源不清的说法。" + }, + "label_meta": { + "stage_fit": "risk_check", + "risk_tags": ["replacement_device", "evidence_required"], + "priority": "medium" + } +} +``` + +## 样本 15 + +```json +{ + "sample_id": "phone_seed_015", + "stage": "order_decision", + "task_goal": "准备下单一台已沟通过的二手手机", + "context": "卖家已补充照片和视频,价格谈妥,用户准备直接付款。", + "target": { + "suggestion": "下单前把关键承诺写进聊天记录,包括无拆修、无锁、功能正常、支持到货当天验机和问题处理方式。", + "reason": "下单决策阶段需要把口头承诺转成平台内可追溯记录;这能降低到货争议时举证困难。" + }, + "label_meta": { + "stage_fit": "order_decision", + "risk_tags": ["evidence_preservation", "platform_record"], + "priority": "high" + } +} +``` + +## 样本 16 + +```json +{ + "sample_id": "phone_seed_016", + "stage": "order_decision", + "task_goal": "决定是否接受卖家要求脱离平台交易", + "context": "卖家说平台手续费麻烦,希望用户微信转账并承诺更便宜 100 元。", + "target": { + "suggestion": "不要脱离平台付款,继续要求在平台内交易,并保留所有关于商品状态和售后承诺的聊天记录。", + "reason": "脱离平台会削弱交易保护和维权证据;100 元优惠无法覆盖被骗、货不对板或售后无门的风险。" + }, + "label_meta": { + "stage_fit": "order_decision", + "risk_tags": ["off_platform_payment", "fraud_risk"], + "priority": "high" + } +} +``` + +## 样本 17 + +```json +{ + "sample_id": "phone_seed_017", + "stage": "after_sale", + "task_goal": "到货后检查二手 iPhone", + "context": "用户刚收到手机,外观看起来正常,但还没确认功能和卖家承诺是否一致。", + "target": { + "suggestion": "先录制开箱和功能检查视频,依次检查屏幕、摄像头、Face ID、扬声器、充电、网络、电池健康和 Apple ID 状态。", + "reason": "到货初期是发现问题和留证的关键窗口;完整视频能帮助确认商品是否符合承诺,也便于后续沟通或维权。" + }, + "label_meta": { + "stage_fit": "after_sale", + "risk_tags": ["arrival_inspection", "evidence_preservation"], + "priority": "high" + } +} +``` + +## 样本 18 + +```json +{ + "sample_id": "phone_seed_018", + "stage": "after_sale", + "task_goal": "收到安卓手机后发现续航很差", + "context": "卖家之前说电池正常,但用户半天就掉电很多,不确定是不是正常老化。", + "target": { + "suggestion": "先记录电池使用截图、亮屏时间和异常掉电过程,再和卖家对照他之前关于电池正常的描述沟通。", + "reason": "续航问题容易变成主观争议;用系统记录和聊天承诺对齐,能让沟通从感受变成证据。" + }, + "label_meta": { + "stage_fit": "after_sale", + "risk_tags": ["battery_issue", "evidence_preservation"], + "priority": "medium" + } +} +``` + +## 样本 19 + +```json +{ + "sample_id": "phone_seed_019", + "stage": "dispute", + "task_goal": "处理到货后发现屏幕非原装的问题", + "context": "用户验机发现屏幕可能不是原装,卖家之前承诺无拆修,现在卖家说“能用就行”。", + "target": { + "suggestion": "先整理卖家承诺无拆修的聊天记录、验机结果、屏幕异常证据,再通过平台发起沟通或售后流程。", + "reason": "维权阶段不要陷入口头争吵,关键是把承诺、检测结果和商品不一致点整理成平台可判断的证据链。" + }, + "label_meta": { + "stage_fit": "dispute", + "risk_tags": ["screen_replacement", "evidence_chain", "platform_dispute"], + "priority": "high" + } +} +``` + +## 样本 20 + +```json +{ + "sample_id": "phone_seed_020", + "stage": "dispute", + "task_goal": "处理卖家拒绝承认隐藏 ID 锁风险", + "context": "用户收到手机后发现无法正常退出某些账号状态,卖家之前没有说明相关限制。", + "target": { + "suggestion": "立即停止继续使用和重置操作,先录屏保留账号状态异常、设置页面和卖家原始描述,再联系平台说明商品与描述不符。", + "reason": "账号锁或限制状态可能影响手机所有权和正常使用;维权时需要保留原始状态证据,避免操作后证据消失。" + }, + "label_meta": { + "stage_fit": "dispute", + "risk_tags": ["id_lock", "evidence_preservation", "platform_dispute"], + "priority": "high" + } +} +``` + +## 用户审核模板 + +请按下面格式给反馈即可: + +```text +001 keep +002 revise:原因... +003 weak:原因... +004 reject:原因... +``` + +你也可以只先审 5-10 条;校准口径比一次审完更重要。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/02-candidate-samples-v0.2.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/02-candidate-samples-v0.2.md new file mode 100644 index 0000000..03022d0 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/02-candidate-samples-v0.2.md @@ -0,0 +1,590 @@ +# 第一批候选样本 v0.2 + +用途:根据用户反馈重生成 suggestion next action 样本。v0.2 的核心修正是:suggestion 必须是短句、可点击、可直接作为用户给 Agent 的下一步指令;每条样本最多 3 个 suggestion,且彼此互补。 + +v0.1 的问题: + +- suggestion 太长,更像解释型建议。 +- suggestion 无法直接点击发送给 Agent。 +- 单条 suggestion 承载太多意图,没有形成互补行动组。 + +v0.2 的目标输出: + +```json +{ + "target": { + "suggestions": [ + "补充我的使用场景", + "按预算推荐候选机型", + "列出必须避开的风险" + ] + } +} +``` + +标注和评测侧仍保留 reason。更准确地说: + +- `target.suggestions` 是 action-only 模型输出候选。 +- `label_meta.action_reasons` 是逐条 reason,用于审核、评测和 action-with-reason ablation。 +- `label_meta.stage_fit / risk_tags / priority / case_type / decision_mode` 是评测元信息。 + +如果后续实验显示小模型可以稳定输出短 reason,可以把训练目标切到 action-with-reason: + +```json +{ + "suggestions": [ + { + "action": "补充我的主力机场景", + "reason": "当前用途不清晰" + } + ] +} +``` + +## 样本 01 + +```json +{ + "sample_id": "phone_seed_v02_001", + "stage": "need_clarification", + "task_goal": "买一台二手 iPhone 作为主力机", + "context": "用户预算 3500-4500 元,只说想买 iPhone,没有说明用途、容量、续航要求和是否接受维修机。", + "target": { + "suggestions": [ + "补充我的主力机场景", + "帮我确定容量底线", + "列出不能接受的风险" + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "ask_for_evidence", + "stage_fit": "need_clarification", + "risk_tags": ["unclear_requirements", "budget_scope"], + "priority": "high", + "action_reasons": [ + "当前主力机用途不清晰,无法判断拍照、性能和续航优先级。", + "容量会影响长期使用体验和二手价格,需要先确定底线。", + "维修机、锁机和电池问题会直接改变是否值得继续搜索。" + ], + "review_reason": "当前需求不清晰,下一步应先补用途、容量和风险边界,而不是直接搜索商品。" + } +} +``` + +## 样本 02 + +```json +{ + "sample_id": "phone_seed_v02_002", + "stage": "need_clarification", + "task_goal": "给父母买一台二手安卓手机", + "context": "用户只说要便宜耐用,不确定是否需要大字体、长续航、双卡和售后方便。", + "target": { + "suggestions": [ + "确认父母常用功能", + "优先筛长续航机型", + "列出老人机避坑点" + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "ask_for_evidence", + "stage_fit": "need_clarification", + "risk_tags": ["usage_mismatch", "battery_life"], + "priority": "medium", + "action_reasons": [ + "父母的真实使用场景决定屏幕、续航、声音和系统流畅度优先级。", + "长续航通常比性能参数更影响老人日常体验。", + "老人机需要额外关注字体、存储、售后和误触等问题。" + ], + "review_reason": "给父母买手机需要先确认真实使用场景,再决定电池、屏幕、双卡和存储底线。" + } +} +``` + +## 样本 03 + +```json +{ + "sample_id": "phone_seed_v02_003", + "stage": "search", + "task_goal": "搜索一台二手 iPhone 14 Pro 256G", + "context": "用户预算 4000 左右,希望国行、无拆修、电池健康 85% 以上,但搜索结果很多,价格跨度从 3300 到 4600。", + "target": { + "suggestions": [ + "按硬性条件重新筛选", + "标记异常低价商品", + "生成搜索关键词" + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "compare_more", + "stage_fit": "search", + "risk_tags": ["abnormal_low_price", "filtering"], + "priority": "high", + "review_reason": "搜索阶段应先减少噪音,并把异常低价商品单独标记为后续风险确认对象。" + } +} +``` + +## 样本 04 + +```json +{ + "sample_id": "phone_seed_v02_004", + "stage": "search", + "task_goal": "找一台二手小米或红米备用机", + "context": "用户预算 800 元以内,想要续航好,但搜索时只按价格从低到高排序。", + "target": { + "suggestions": [ + "筛选大电池机型", + "排除低存储版本", + "过滤信息太少的商品" + ] + }, + "label_meta": { + "case_type": "ambiguous", + "decision_mode": "compare_more", + "stage_fit": "search", + "risk_tags": ["low_price_bias", "missing_condition_info"], + "priority": "medium", + "review_reason": "低价不是备用机的唯一标准,续航、存储和描述完整度会直接影响真实体验。" + } +} +``` + +## 样本 05 + +```json +{ + "sample_id": "phone_seed_v02_005", + "stage": "shortlist", + "task_goal": "从 8 个 iPhone 候选里先筛掉明显不合适的", + "context": "候选商品中有几台价格便宜,但描述包含“美版有锁”“换过屏”“商家批量出”等字样。", + "target": { + "suggestions": [ + "先排除有锁机", + "筛掉不支持验机的", + "保留信息完整商品" + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "pause_or_avoid", + "stage_fit": "shortlist", + "risk_tags": ["locked_phone", "screen_replacement", "seller_risk"], + "priority": "high", + "review_reason": "初筛阶段应先排除锁机、换屏不明和无法验机等高风险候选。" + } +} +``` + +## 样本 06 + +```json +{ + "sample_id": "phone_seed_v02_006", + "stage": "shortlist", + "task_goal": "筛选二手安卓旗舰", + "context": "用户看到几台骁龙旗舰,价格接近,但有的只写“功能正常”,没有任何成色、维修或电池说明。", + "target": { + "suggestions": [ + "按信息完整度排序", + "追问维修和电池情况", + "移除描述含糊商品" + ] + }, + "label_meta": { + "case_type": "ambiguous", + "decision_mode": "ask_for_evidence", + "stage_fit": "shortlist", + "risk_tags": ["information_asymmetry", "repair_history"], + "priority": "medium", + "review_reason": "信息不完整不等于一定不能买,但需要先补证据再进入对比。" + } +} +``` + +## 样本 07 + +```json +{ + "sample_id": "phone_seed_v02_007", + "stage": "compare", + "task_goal": "比较三台 iPhone 14 Pro", + "context": "三台价格分别为 3880、4050、4180 元。最低价电池 82%,中间价电池 88% 但无验机报告,最高价有原盒和保修截图。", + "target": { + "suggestions": [ + "生成三台对比表", + "优先比较电池成本", + "检查保修和验机证据" + ] + }, + "label_meta": { + "case_type": "boundary", + "decision_mode": "compare_more", + "stage_fit": "compare", + "risk_tags": ["battery_health", "warranty_evidence", "comparison"], + "priority": "high", + "review_reason": "价格差异不大时,应比较风险证据和后续使用成本,而不是只看标价。" + } +} +``` + +## 样本 08 + +```json +{ + "sample_id": "phone_seed_v02_008", + "stage": "compare", + "task_goal": "在 iPhone 13 Pro 和 iPhone 14 之间选择", + "context": "用户纠结性能、拍照和价格,候选商品价格接近,但没有考虑屏幕、重量、续航和维修成本。", + "target": { + "suggestions": [ + "按使用场景打分", + "比较维修成本", + "列出取舍理由" + ] + }, + "label_meta": { + "case_type": "ambiguous", + "decision_mode": "compare_more", + "stage_fit": "compare", + "risk_tags": ["model_tradeoff", "usage_fit"], + "priority": "medium", + "review_reason": "跨机型对比需要把参数讨论转成个人使用场景下的取舍。" + } +} +``` + +## 样本 09 + +```json +{ + "sample_id": "phone_seed_v02_009", + "stage": "item_understanding", + "task_goal": "了解一台标称无拆修的 iPhone", + "context": "卖家展示了外观图和电池健康截图,但没有主板、屏幕、摄像头和 Face ID 状态说明。", + "target": { + "suggestions": [ + "要求补功能检查视频", + "追问是否拆修换件", + "核对 Face ID 状态" + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "ask_for_evidence", + "stage_fit": "item_understanding", + "risk_tags": ["function_check", "repair_history"], + "priority": "high", + "review_reason": "外观和电池截图不足以证明整机状态,需要补关键功能和拆修证据。" + } +} +``` + +## 样本 10 + +```json +{ + "sample_id": "phone_seed_v02_010", + "stage": "item_understanding", + "task_goal": "确认一台安卓手机是否值得继续沟通", + "context": "商品描述写着“轻微烧屏不影响使用”,价格比同款低 300 元。", + "target": { + "suggestions": [ + "让卖家拍纯色屏幕", + "确认烧屏具体位置", + "评估低价是否值得" + ] + }, + "label_meta": { + "case_type": "boundary", + "decision_mode": "ask_for_evidence", + "stage_fit": "item_understanding", + "risk_tags": ["screen_burn_in", "condition_uncertainty"], + "priority": "high", + "review_reason": "轻微烧屏可能可接受,也可能影响体验,应先补证据而不是直接排除或下单。" + } +} +``` + +## 样本 11 + +```json +{ + "sample_id": "phone_seed_v02_011", + "stage": "seller_chat", + "task_goal": "向卖家追问 iPhone 维修和锁机风险", + "context": "用户担心直接问太多显得麻烦,卖家目前只回复“机器没问题”。", + "target": { + "suggestions": [ + "生成礼貌追问话术", + "要求录屏设置页面", + "确认已退出 Apple ID" + ] + }, + "label_meta": { + "case_type": "ambiguous", + "decision_mode": "ask_for_evidence", + "stage_fit": "seller_chat", + "risk_tags": ["id_lock", "managed_device", "communication"], + "priority": "high", + "review_reason": "卖家笼统承诺不够,需要用具体、礼貌的话术索取锁机和监管风险证据。" + } +} +``` + +## 样本 12 + +```json +{ + "sample_id": "phone_seed_v02_012", + "stage": "seller_chat", + "task_goal": "和卖家沟通线下验机", + "context": "卖家同城,支持面交,但用户不知道如何提出验机要求。", + "target": { + "suggestions": [ + "约定公开地点面交", + "生成验机清单", + "确认预留检查时间" + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "proceed", + "stage_fit": "seller_chat", + "risk_tags": ["offline_inspection", "transaction_safety"], + "priority": "high", + "review_reason": "线下交易可以推进,但应提前约定地点、验机项目和检查时间。" + } +} +``` + +## 样本 13 + +```json +{ + "sample_id": "phone_seed_v02_013", + "stage": "risk_check", + "task_goal": "确认低价 iPhone 是否有隐藏风险", + "context": "商品比同款低 600 元,卖家说“急出”,但不愿提供序列号截图,也不支持验机。", + "target": { + "suggestions": [ + "标记为高风险", + "追问拒绝验机原因", + "暂停继续议价" + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "pause_or_avoid", + "stage_fit": "risk_check", + "risk_tags": ["abnormal_low_price", "evidence_refusal"], + "priority": "high", + "review_reason": "异常低价叠加拒绝提供证据是强风险信号,应先暂停推进。" + } +} +``` + +## 样本 14 + +```json +{ + "sample_id": "phone_seed_v02_014", + "stage": "risk_check", + "task_goal": "判断一台“官换机”描述是否可信", + "context": "卖家说手机是官换机,价格合理,但没有提供保修记录或官方换机凭证。", + "target": { + "suggestions": [ + "要求换机凭证", + "核对保修记录", + "不要只信口头描述" + ] + }, + "label_meta": { + "case_type": "boundary", + "decision_mode": "ask_for_evidence", + "stage_fit": "risk_check", + "risk_tags": ["replacement_device", "evidence_required"], + "priority": "medium", + "review_reason": "官换机并非一定不可买,但必须有可核验证据。" + } +} +``` + +## 样本 15 + +```json +{ + "sample_id": "phone_seed_v02_015", + "stage": "order_decision", + "task_goal": "准备下单一台已沟通过的二手手机", + "context": "卖家已补充照片和视频,价格谈妥,用户准备直接付款。", + "target": { + "suggestions": [ + "整理卖家关键承诺", + "确认到货验机规则", + "保存平台聊天记录" + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "proceed", + "stage_fit": "order_decision", + "risk_tags": ["evidence_preservation", "platform_record"], + "priority": "high", + "review_reason": "下单前应把承诺转成平台内可追溯记录,降低后续争议成本。" + } +} +``` + +## 样本 16 + +```json +{ + "sample_id": "phone_seed_v02_016", + "stage": "order_decision", + "task_goal": "决定是否接受卖家要求脱离平台交易", + "context": "卖家说平台手续费麻烦,希望用户微信转账并承诺更便宜 100 元。", + "target": { + "suggestions": [ + "拒绝脱离平台付款", + "要求平台内继续交易", + "保留卖家转账要求" + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "pause_or_avoid", + "stage_fit": "order_decision", + "risk_tags": ["off_platform_payment", "fraud_risk"], + "priority": "high", + "review_reason": "脱离平台付款会削弱交易保护,不能为了小额优惠牺牲维权能力。" + } +} +``` + +## 样本 17 + +```json +{ + "sample_id": "phone_seed_v02_017", + "stage": "after_sale", + "task_goal": "到货后检查二手 iPhone", + "context": "用户刚收到手机,外观看起来正常,但还没确认功能和卖家承诺是否一致。", + "target": { + "suggestions": [ + "录制开箱验机视频", + "逐项检查核心功能", + "核对卖家原承诺" + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "ask_for_evidence", + "stage_fit": "after_sale", + "risk_tags": ["arrival_inspection", "evidence_preservation"], + "priority": "high", + "review_reason": "到货初期应先留证和核对承诺,避免错过争议处理窗口。" + } +} +``` + +## 样本 18 + +```json +{ + "sample_id": "phone_seed_v02_018", + "stage": "after_sale", + "task_goal": "收到安卓手机后发现续航很差", + "context": "卖家之前说电池正常,但用户半天就掉电很多,不确定是不是正常老化。", + "target": { + "suggestions": [ + "记录掉电过程", + "截图电池使用情况", + "对照卖家电池承诺" + ] + }, + "label_meta": { + "case_type": "ambiguous", + "decision_mode": "ask_for_evidence", + "stage_fit": "after_sale", + "risk_tags": ["battery_issue", "evidence_preservation"], + "priority": "medium", + "review_reason": "续航争议需要从主观感受转成系统记录和聊天承诺对照。" + } +} +``` + +## 样本 19 + +```json +{ + "sample_id": "phone_seed_v02_019", + "stage": "dispute", + "task_goal": "处理到货后发现屏幕非原装的问题", + "context": "用户验机发现屏幕可能不是原装,卖家之前承诺无拆修,现在卖家说“能用就行”。", + "target": { + "suggestions": [ + "整理无拆修承诺", + "保存验机结果", + "发起平台售后" + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "dispute_or_escalate", + "stage_fit": "dispute", + "risk_tags": ["screen_replacement", "evidence_chain", "platform_dispute"], + "priority": "high", + "review_reason": "维权阶段要整理承诺、检测结果和不一致点,形成平台可判断的证据链。" + } +} +``` + +## 样本 20 + +```json +{ + "sample_id": "phone_seed_v02_020", + "stage": "dispute", + "task_goal": "处理卖家拒绝承认隐藏 ID 锁风险", + "context": "用户收到手机后发现无法正常退出某些账号状态,卖家之前没有说明相关限制。", + "target": { + "suggestions": [ + "停止重置和继续使用", + "录屏保留账号异常", + "联系平台说明不符" + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "dispute_or_escalate", + "stage_fit": "dispute", + "risk_tags": ["id_lock", "evidence_preservation", "platform_dispute"], + "priority": "high", + "review_reason": "账号锁或限制状态会影响正常使用和所有权判断,应先保留原始证据再走平台流程。" + } +} +``` + +## 用户审核模板 + +请按下面格式给反馈即可: + +```text +001 keep +002 revise:第二条还是太泛,建议改成... +003 weak:三条之间重复 +004 reject:不适合直接点击发送 +``` + +v0.2 审核时请重点看: + +- 每条是否足够短。 +- 是否能直接点击发送给 Agent。 +- 3 条之间是否互补。 +- 是否缺少更关键的下一步动作。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/03-candidate-samples-v0.3.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/03-candidate-samples-v0.3.md new file mode 100644 index 0000000..e48d016 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/03-candidate-samples-v0.3.md @@ -0,0 +1,261 @@ +# 第一批候选样本 v0.3 + +用途:根据用户反馈,将 target schema 收敛为 `{ "suggestions": [{ "s": "建议", "r": "原因" }] }`。 + +v0.3 规则: + +- `suggestions` 最多 3 条。 +- `s` 是短建议,可被用户直接点击发送给 Agent。 +- `r` 是短原因,解释为什么当前应该做这一步。 +- 多条 suggestions 之间要互补。 +- 不输出 stage、risk_tags、priority 等内部评测字段。 + +## 样本 01 + +```json +{ + "sample_id": "phone_seed_v03_001", + "stage": "need_clarification", + "task_goal": "买一台二手 iPhone 作为主力机", + "context": "用户预算 3500-4500 元,只说想买 iPhone,没有说明用途、容量、续航要求和是否接受维修机。", + "target": { + "suggestions": [ + {"s": "补充我的主力机场景", "r": "用途会决定机型优先级"}, + {"s": "帮我确定容量底线", "r": "容量影响长期体验和价格"}, + {"s": "列出不能接受的风险", "r": "先明确维修锁机等红线"} + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "ask_for_evidence", + "stage_fit": "need_clarification", + "risk_tags": ["unclear_requirements", "budget_scope"], + "priority": "high" + } +} +``` + +## 样本 02 + +```json +{ + "sample_id": "phone_seed_v03_002", + "stage": "search", + "task_goal": "搜索一台二手 iPhone 14 Pro 256G", + "context": "用户预算 4000 左右,希望国行、无拆修、电池健康 85% 以上,但搜索结果很多,价格跨度从 3300 到 4600。", + "target": { + "suggestions": [ + {"s": "按硬性条件重新筛选", "r": "先减少不匹配商品"}, + {"s": "标记异常低价商品", "r": "低价可能隐藏风险"}, + {"s": "生成搜索关键词", "r": "提高搜索结果相关性"} + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "compare_more", + "stage_fit": "search", + "risk_tags": ["abnormal_low_price", "filtering"], + "priority": "high" + } +} +``` + +## 样本 03 + +```json +{ + "sample_id": "phone_seed_v03_003", + "stage": "shortlist", + "task_goal": "从 8 个 iPhone 候选里先筛掉明显不合适的", + "context": "候选商品中有几台价格便宜,但描述包含“美版有锁”“换过屏”“商家批量出”等字样。", + "target": { + "suggestions": [ + {"s": "先排除有锁机", "r": "锁机会影响正常使用"}, + {"s": "筛掉不支持验机的", "r": "无法验机会放大风险"}, + {"s": "保留信息完整商品", "r": "透明度高更便于判断"} + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "pause_or_avoid", + "stage_fit": "shortlist", + "risk_tags": ["locked_phone", "screen_replacement", "seller_risk"], + "priority": "high" + } +} +``` + +## 样本 04 + +```json +{ + "sample_id": "phone_seed_v03_004", + "stage": "compare", + "task_goal": "比较三台 iPhone 14 Pro", + "context": "三台价格分别为 3880、4050、4180 元。最低价电池 82%,中间价电池 88% 但无验机报告,最高价有原盒和保修截图。", + "target": { + "suggestions": [ + {"s": "生成三台对比表", "r": "价格接近时要比证据"}, + {"s": "优先比较电池成本", "r": "低电池会增加后续支出"}, + {"s": "检查保修和验机证据", "r": "证据影响下单风险"} + ] + }, + "label_meta": { + "case_type": "boundary", + "decision_mode": "compare_more", + "stage_fit": "compare", + "risk_tags": ["battery_health", "warranty_evidence", "comparison"], + "priority": "high" + } +} +``` + +## 样本 05 + +```json +{ + "sample_id": "phone_seed_v03_005", + "stage": "item_understanding", + "task_goal": "确认一台安卓手机是否值得继续沟通", + "context": "商品描述写着“轻微烧屏不影响使用”,价格比同款低 300 元。", + "target": { + "suggestions": [ + {"s": "让卖家拍纯色屏幕", "r": "纯色图更容易看烧屏"}, + {"s": "确认烧屏具体位置", "r": "位置影响日常使用"}, + {"s": "评估低价是否值得", "r": "要权衡折价和体验损失"} + ] + }, + "label_meta": { + "case_type": "boundary", + "decision_mode": "ask_for_evidence", + "stage_fit": "item_understanding", + "risk_tags": ["screen_burn_in", "condition_uncertainty"], + "priority": "high" + } +} +``` + +## 样本 06 + +```json +{ + "sample_id": "phone_seed_v03_006", + "stage": "seller_chat", + "task_goal": "向卖家追问 iPhone 维修和锁机风险", + "context": "用户担心直接问太多显得麻烦,卖家目前只回复“机器没问题”。", + "target": { + "suggestions": [ + {"s": "生成礼貌追问话术", "r": "降低卖家抵触感"}, + {"s": "要求录屏设置页面", "r": "核对设备真实状态"}, + {"s": "确认已退出 Apple ID", "r": "排除隐藏锁风险"} + ] + }, + "label_meta": { + "case_type": "ambiguous", + "decision_mode": "ask_for_evidence", + "stage_fit": "seller_chat", + "risk_tags": ["id_lock", "managed_device", "communication"], + "priority": "high" + } +} +``` + +## 样本 07 + +```json +{ + "sample_id": "phone_seed_v03_007", + "stage": "risk_check", + "task_goal": "确认低价 iPhone 是否有隐藏风险", + "context": "商品比同款低 600 元,卖家说“急出”,但不愿提供序列号截图,也不支持验机。", + "target": { + "suggestions": [ + {"s": "标记为高风险", "r": "低价叠加拒证很危险"}, + {"s": "追问拒绝验机原因", "r": "先确认卖家顾虑"}, + {"s": "暂停继续议价", "r": "风险未清不应推进"} + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "pause_or_avoid", + "stage_fit": "risk_check", + "risk_tags": ["abnormal_low_price", "evidence_refusal"], + "priority": "high" + } +} +``` + +## 样本 08 + +```json +{ + "sample_id": "phone_seed_v03_008", + "stage": "order_decision", + "task_goal": "决定是否接受卖家要求脱离平台交易", + "context": "卖家说平台手续费麻烦,希望用户微信转账并承诺更便宜 100 元。", + "target": { + "suggestions": [ + {"s": "拒绝脱离平台付款", "r": "平台外交易缺少保障"}, + {"s": "要求平台内继续交易", "r": "保留平台维权路径"}, + {"s": "保存卖家转账要求", "r": "异常要求需要留证"} + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "pause_or_avoid", + "stage_fit": "order_decision", + "risk_tags": ["off_platform_payment", "fraud_risk"], + "priority": "high" + } +} +``` + +## 样本 09 + +```json +{ + "sample_id": "phone_seed_v03_009", + "stage": "after_sale", + "task_goal": "到货后检查二手 iPhone", + "context": "用户刚收到手机,外观看起来正常,但还没确认功能和卖家承诺是否一致。", + "target": { + "suggestions": [ + {"s": "录制开箱验机视频", "r": "保留到货原始证据"}, + {"s": "逐项检查核心功能", "r": "确认是否符合描述"}, + {"s": "核对卖家原承诺", "r": "发现不符便于沟通"} + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "ask_for_evidence", + "stage_fit": "after_sale", + "risk_tags": ["arrival_inspection", "evidence_preservation"], + "priority": "high" + } +} +``` + +## 样本 10 + +```json +{ + "sample_id": "phone_seed_v03_010", + "stage": "dispute", + "task_goal": "处理到货后发现屏幕非原装的问题", + "context": "用户验机发现屏幕可能不是原装,卖家之前承诺无拆修,现在卖家说“能用就行”。", + "target": { + "suggestions": [ + {"s": "整理无拆修承诺", "r": "先固定卖家原描述"}, + {"s": "保存验机结果", "r": "用检测证据支撑判断"}, + {"s": "发起平台售后", "r": "让平台介入争议处理"} + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "dispute_or_escalate", + "stage_fit": "dispute", + "risk_tags": ["screen_replacement", "evidence_chain", "platform_dispute"], + "priority": "high" + } +} +``` diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/04-candidate-samples-v0.4.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/04-candidate-samples-v0.4.md new file mode 100644 index 0000000..87eeea0 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/04-candidate-samples-v0.4.md @@ -0,0 +1,262 @@ +# 第一批候选样本 v0.4 + +用途:根据用户反馈,将 `s` 修正为“用户点击后直接发送给买家 Agent 的自然指令”。 + +v0.4 规则: + +- target schema:`{ "suggestions": [{ "s": "建议", "r": "原因" }] }`。 +- `s` 不是菜单标签,而是用户口吻的指令。 +- 点击某条 suggestion 后,`s` 会作为用户消息发给买家 Agent。 +- `s` 应减少交互摩擦,让用户不用自己组织下一句话。 +- `r` 是短原因,解释为什么这个 action 是当前好下一步。 +- 最多 3 条,彼此互补。 + +## 样本 01 + +```json +{ + "sample_id": "phone_seed_v04_001", + "stage": "need_clarification", + "task_goal": "买一台二手 iPhone 作为主力机", + "context": "用户预算 3500-4500 元,只说想买 iPhone,没有说明用途、容量、续航要求和是否接受维修机。", + "target": { + "suggestions": [ + {"s": "帮我先问清楚这台主力机最该满足哪些使用场景", "r": "用途会决定机型优先级"}, + {"s": "帮我判断 3500-4500 预算适合看哪些 iPhone", "r": "先缩小候选范围"}, + {"s": "帮我列出买二手 iPhone 必须提前排除的风险", "r": "先明确维修锁机等红线"} + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "ask_for_evidence", + "stage_fit": "need_clarification", + "risk_tags": ["unclear_requirements", "budget_scope"], + "priority": "high" + } +} +``` + +## 样本 02 + +```json +{ + "sample_id": "phone_seed_v04_002", + "stage": "search", + "task_goal": "搜索一台二手 iPhone 14 Pro 256G", + "context": "用户预算 4000 左右,希望国行、无拆修、电池健康 85% 以上,但搜索结果很多,价格跨度从 3300 到 4600。", + "target": { + "suggestions": [ + {"s": "帮我按国行、无拆修、电池 85% 以上重新筛一遍", "r": "先减少不匹配商品"}, + {"s": "帮我把明显低于市场价的商品标出来", "r": "低价可能隐藏风险"}, + {"s": "帮我生成一组更精准的闲鱼搜索关键词", "r": "提高搜索结果相关性"} + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "compare_more", + "stage_fit": "search", + "risk_tags": ["abnormal_low_price", "filtering"], + "priority": "high" + } +} +``` + +## 样本 03 + +```json +{ + "sample_id": "phone_seed_v04_003", + "stage": "shortlist", + "task_goal": "从 8 个 iPhone 候选里先筛掉明显不合适的", + "context": "候选商品中有几台价格便宜,但描述包含“美版有锁”“换过屏”“商家批量出”等字样。", + "target": { + "suggestions": [ + {"s": "帮我先排除这些候选里锁机风险高的商品", "r": "锁机会影响正常使用"}, + {"s": "帮我筛掉不支持验机或描述太含糊的商品", "r": "无法验机会放大风险"}, + {"s": "帮我保留信息最完整、最值得继续问的几台", "r": "透明度高更便于判断"} + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "pause_or_avoid", + "stage_fit": "shortlist", + "risk_tags": ["locked_phone", "screen_replacement", "seller_risk"], + "priority": "high" + } +} +``` + +## 样本 04 + +```json +{ + "sample_id": "phone_seed_v04_004", + "stage": "compare", + "task_goal": "比较三台 iPhone 14 Pro", + "context": "三台价格分别为 3880、4050、4180 元。最低价电池 82%,中间价电池 88% 但无验机报告,最高价有原盒和保修截图。", + "target": { + "suggestions": [ + {"s": "帮我把这三台按电池、保修和验机证据做个对比表", "r": "价格接近时要比证据"}, + {"s": "帮我算一下电池 82% 那台后续可能多花多少钱", "r": "低电池会增加后续支出"}, + {"s": "帮我判断哪台的下单风险最低", "r": "证据影响下单风险"} + ] + }, + "label_meta": { + "case_type": "boundary", + "decision_mode": "compare_more", + "stage_fit": "compare", + "risk_tags": ["battery_health", "warranty_evidence", "comparison"], + "priority": "high" + } +} +``` + +## 样本 05 + +```json +{ + "sample_id": "phone_seed_v04_005", + "stage": "item_understanding", + "task_goal": "确认一台安卓手机是否值得继续沟通", + "context": "商品描述写着“轻微烧屏不影响使用”,价格比同款低 300 元。", + "target": { + "suggestions": [ + {"s": "帮我生成一句话,让卖家补纯色背景的屏幕照片", "r": "纯色图更容易看烧屏"}, + {"s": "帮我判断这个烧屏程度会不会影响日常使用", "r": "位置影响使用体验"}, + {"s": "帮我评估便宜 300 元值不值得接受烧屏风险", "r": "要权衡折价和体验损失"} + ] + }, + "label_meta": { + "case_type": "boundary", + "decision_mode": "ask_for_evidence", + "stage_fit": "item_understanding", + "risk_tags": ["screen_burn_in", "condition_uncertainty"], + "priority": "high" + } +} +``` + +## 样本 06 + +```json +{ + "sample_id": "phone_seed_v04_006", + "stage": "seller_chat", + "task_goal": "向卖家追问 iPhone 维修和锁机风险", + "context": "用户担心直接问太多显得麻烦,卖家目前只回复“机器没问题”。", + "target": { + "suggestions": [ + {"s": "帮我写一句礼貌的话,问卖家有没有拆修或换件", "r": "降低卖家抵触感"}, + {"s": "帮我生成一段话,请卖家录屏展示设置页面", "r": "核对设备真实状态"}, + {"s": "帮我问清楚 Apple ID 和查找功能是否已退出", "r": "排除隐藏锁风险"} + ] + }, + "label_meta": { + "case_type": "ambiguous", + "decision_mode": "ask_for_evidence", + "stage_fit": "seller_chat", + "risk_tags": ["id_lock", "managed_device", "communication"], + "priority": "high" + } +} +``` + +## 样本 07 + +```json +{ + "sample_id": "phone_seed_v04_007", + "stage": "risk_check", + "task_goal": "确认低价 iPhone 是否有隐藏风险", + "context": "商品比同款低 600 元,卖家说“急出”,但不愿提供序列号截图,也不支持验机。", + "target": { + "suggestions": [ + {"s": "帮我判断这台低价 iPhone 现在是不是高风险", "r": "低价叠加拒证很危险"}, + {"s": "帮我生成一句话,追问卖家为什么不能验机", "r": "先确认卖家顾虑"}, + {"s": "帮我找几台更稳妥的同款候选", "r": "风险未清不应推进"} + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "pause_or_avoid", + "stage_fit": "risk_check", + "risk_tags": ["abnormal_low_price", "evidence_refusal"], + "priority": "high" + } +} +``` + +## 样本 08 + +```json +{ + "sample_id": "phone_seed_v04_008", + "stage": "order_decision", + "task_goal": "决定是否接受卖家要求脱离平台交易", + "context": "卖家说平台手续费麻烦,希望用户微信转账并承诺更便宜 100 元。", + "target": { + "suggestions": [ + {"s": "帮我判断要不要接受卖家微信转账的要求", "r": "平台外交易缺少保障"}, + {"s": "帮我写一句话,坚持在平台内继续交易", "r": "保留平台维权路径"}, + {"s": "帮我总结这次交易里需要保留的证据", "r": "异常要求需要留证"} + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "pause_or_avoid", + "stage_fit": "order_decision", + "risk_tags": ["off_platform_payment", "fraud_risk"], + "priority": "high" + } +} +``` + +## 样本 09 + +```json +{ + "sample_id": "phone_seed_v04_009", + "stage": "after_sale", + "task_goal": "到货后检查二手 iPhone", + "context": "用户刚收到手机,外观看起来正常,但还没确认功能和卖家承诺是否一致。", + "target": { + "suggestions": [ + {"s": "帮我列一个到货后 iPhone 验机清单", "r": "确认是否符合描述"}, + {"s": "帮我设计一套开箱留证步骤", "r": "保留到货原始证据"}, + {"s": "帮我对照卖家承诺逐项检查", "r": "发现不符便于沟通"} + ] + }, + "label_meta": { + "case_type": "typical", + "decision_mode": "ask_for_evidence", + "stage_fit": "after_sale", + "risk_tags": ["arrival_inspection", "evidence_preservation"], + "priority": "high" + } +} +``` + +## 样本 10 + +```json +{ + "sample_id": "phone_seed_v04_010", + "stage": "dispute", + "task_goal": "处理到货后发现屏幕非原装的问题", + "context": "用户验机发现屏幕可能不是原装,卖家之前承诺无拆修,现在卖家说“能用就行”。", + "target": { + "suggestions": [ + {"s": "帮我整理卖家承诺无拆修的聊天证据", "r": "先固定卖家原描述"}, + {"s": "帮我把验机结果整理成维权说明", "r": "用检测证据支撑判断"}, + {"s": "帮我起草一段发给平台客服的话", "r": "让平台介入争议处理"} + ] + }, + "label_meta": { + "case_type": "negative_unsafe", + "decision_mode": "dispute_or_escalate", + "stage_fit": "dispute", + "risk_tags": ["screen_replacement", "evidence_chain", "platform_dispute"], + "priority": "high" + } +} +``` diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/README.md new file mode 100644 index 0000000..26e1d5d --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/06-practice-transfer/README.md @@ -0,0 +1,197 @@ +# 06 Practice Transfer:买家 Agent 数据与评测路线图 + +本阶段目标:把 `02-syllabus-mapper` 的默认方案转成可执行的数据生成、用户审核、baseline 设计和 demo 前置问题,并迁移到闲鱼二手买家 Agent 的 suggestion next action 任务。 + +> 本文件是 Agent 生成的问题路线图,不是用户学习笔记。用户自己的观察和理解应另行写入 notes。 + +## 当前默认方案 + +- 业务任务:闲鱼二手买家 Agent 的 suggestion next action。 +- 第一轮品类:手机。 +- 模型输出:`{"suggestions":[{"s":"短建议","r":"短原因"}]}`,最多 3 条;`s` 是用户口吻、可直接发给买家 Agent 的短指令,`r` 是短解释。 +- 标注/eval 元信息:保留 `stage_fit / risk_tags / priority`。 +- 第一轮模型:`Qwen/Qwen2.5-0.5B-Instruct`,必要时升级 `Qwen/Qwen2.5-1.5B-Instruct`。 +- 第一批数据:train 80 / eval 20 / holdout 20。 +- 工程形态:script-first / config-first / artifact-first。 + +## 本阶段要回答的问题 + +### 1. 数据是否真的覆盖购买任务? + +每条样本都必须回答: + +- 用户最终想买什么? +- 当前处于哪个购买阶段? +- 用户已经知道什么? +- 用户还不知道但应该知道什么? +- 当前最可能卡在哪里? +- 下一步行动是否能把用户推向“买到自己要的商品”? + +不满足这些问题的样本,即使语言流畅,也不能进入训练集。 + +### 2. suggestion 是否真的有行动价值? + +合格 suggestion 必须满足: + +- 短:每条是用户会直接发给 Agent 的一句话,而不是解释型长句。 +- 可点击:用户点击后,这句话会作为用户消息发送给买家 Agent。 +- 用户口吻:`s` 应该像“帮我... / 帮我问... / 帮我对比...”,而不是内部动作标签。 +- 具体:用户知道下一句该问什么、下一步该查什么或该比较什么。 +- 相关:不脱离当前候选商品、预算、阶段和约束。 +- 前进一步:不是重复已有信息,而是解锁下一步判断。 +- 多想一步:能指出用户可能忽略的风险、证据或决策点。 +- 互补:最多 3 条之间覆盖不同行动方向,不能只是同义改写。 +- 安全:不鼓励欺诈、骚扰、绕过平台规则或侵犯隐私。 + +### 3. reason 应该放在哪里? + +`reason` 必须存在,并作为 target 的 `r` 字段输出: + +- 它应说明为什么这个 suggestion 适合当前阶段。 +- 它应点出依据的风险、知识或用户卡点。 +- 它不能只是复述 suggestion。 + +第一轮直接使用短字段 `{s, r}`;如果小模型格式不稳,再降级为只输出 `s`。 + +`s` 的正反例: + +- 好:`帮我把这三台按电池、保修和验机证据做个对比表` +- 好:`帮我生成一句礼貌话术,问卖家是否退出 Apple ID` +- 差:`生成三台对比表` +- 差:`确认已退出 Apple ID` + +### 4. eval 是否能区分好坏? + +第一轮 eval 至少要能区分: + +- 格式正确但内容空泛。 +- 和阶段相关但没有推动下一步。 +- 覆盖风险但太泛,用户无法执行。 +- 能行动但忽略安全或交易风险。 +- 真正推动任务前进的高质量建议。 + +如果 eval 分不出这些差异,训练对比报告就不可信。 + +## 第一批数据生成标准 + +Agent 生成候选样本时必须按阶段覆盖: + +| stage | 目标样本数 | 审核重点 | +| --- | ---: | --- | +| need_clarification | 10 | 需求、预算、用途是否明确 | +| search | 10 | 搜索关键词和筛选条件是否合理 | +| shortlist | 10 | 是否能排除明显不合适商品 | +| compare | 15 | 是否能比较价格、成色、风险和卖家可信度 | +| item_understanding | 15 | 是否能追问关键商品信息 | +| seller_chat | 15 | 是否能给出自然、有效的沟通动作 | +| risk_check | 20 | 是否覆盖手机二手交易高风险点 | +| order_decision | 10 | 是否能帮助用户做下单前确认 | +| after_sale | 8 | 是否能帮助到货检查和留证 | +| dispute | 7 | 是否能帮助整理证据和走平台规则 | + +第一版不要追求每条都完美。目标是让样本覆盖足够多的真实购买状态,并让错误分析能发现缺口。 + +## 难度与边界 Case 覆盖 + +需要。只覆盖典型 case 会让数据集变得“太干净”,模型和 eval 都可能虚高。真实二手购买场景里,最需要 Agent 帮忙的往往是模糊、冲突和边界情况。 + +第一版数据集应同时覆盖: + +| case 类型 | 含义 | 例子 | 目标占比 | +| --- | --- | --- | ---: | +| typical | 典型清晰场景 | 明确预算、明确机型、缺少验机证据 | 50% | +| ambiguous | 信息模糊但未必高风险 | 卖家描述含糊、用户目标摇摆、照片不完整 | 25% | +| boundary | 是否继续推进不明显 | 价格很香但证据不足、轻微维修但可接受、卖家态度一般 | 15% | +| negative / unsafe | 应该阻止或降级的场景 | 脱离平台交易、拒绝验机、疑似锁机、诱导隐私或违规 | 10% | + +模糊和边界样本不是为了让模型“大胆决策”,而是训练它学会: + +- 先补证据,而不是直接推荐买或不买。 +- 明确哪些信息缺失会改变结论。 +- 在风险和收益冲突时给出下一步验证动作。 +- 遇到安全/合规风险时阻止推进购买。 + +对应到 label 侧,每条样本可以增加一个难度标签: + +```json +{ + "label_meta": { + "case_type": "ambiguous", + "decision_mode": "ask_for_evidence" + } +} +``` + +`decision_mode` 第一版建议只用少量枚举: + +| decision_mode | 含义 | +| --- | --- | +| proceed | 可以继续推进 | +| ask_for_evidence | 先补证据 | +| compare_more | 需要更多候选对比 | +| negotiate | 可以进入议价或沟通 | +| pause_or_avoid | 应暂停或避开 | +| dispute_or_escalate | 售后/维权阶段应整理证据并走平台流程 | + +## 用户审核方式 + +用户审核不需要逐字润色每条,而是按下面 4 类标记: + +| 标记 | 含义 | 后续处理 | +| --- | --- | --- | +| keep | 可直接进入数据集 | 保留 | +| revise | 方向对,但需要修改 | 用户或 Agent 修改后再入库 | +| weak | 太泛、太浅或不够行动化 | 进入反例/错误分析 | +| reject | 事实错误、安全风险或脱离任务 | 丢弃 | + +每 20 条样本做一次小结: + +- 哪个 stage 样本最弱? +- 哪类 suggestion 最容易空泛? +- 哪些手机风险知识需要补充? +- 是否需要调整 schema 或 rubric? + +## Baseline 设计问题 + +baseline 不能太弱,否则 LoRA 改善没有意义;也不能太强,否则第一轮小模型很难看出差异。 + +默认 baseline: + +- 同一个 Qwen2.5 小模型。 +- 使用 prompt + context + 少量手机风险知识。 +- 不使用训练数据中的 label。 +- 输出同样的 `{"suggestions":[{"s":"...","r":"..."}]}`。 + +baseline 必须保存: + +- 输入样本。 +- prompt 版本。 +- 原始输出。 +- 解析后输出。 +- eval 分数。 +- 错误样本。 + +## 进入 Demo 设计前的完成标准 + +- [ ] 生成第一批候选样本草案。 +- [ ] 用户至少审核 20 条样本,校准 keep/revise/weak/reject 标准。 +- [ ] 固定第一版 prompt baseline。 +- [ ] 固定第一版 eval rubric 和 judge prompt。 +- [ ] 明确哪些样本进入 train/eval/holdout。 +- [ ] 明确第一版 demo 脚本入口和 artifact run id 约定。 + +## Agent 下一步 + +第一批 20 条候选样本已生成: + +- [`01-candidate-samples-v0.1.md`](01-candidate-samples-v0.1.md) +- [`02-candidate-samples-v0.2.md`](02-candidate-samples-v0.2.md) +- [`03-candidate-samples-v0.3.md`](03-candidate-samples-v0.3.md) +- [`04-candidate-samples-v0.4.md`](04-candidate-samples-v0.4.md) + +v0.1 暴露出产品形态问题:suggestion 太长,无法直接点击发送给 Agent。v0.2 已改为最多 3 条互补短句数组。 + +v0.3 将 target schema 收敛为 `{ "suggestions": [{ "s": "建议", "r": "原因" }] }`。 +v0.4 进一步修正 `s` 的说话对象:`s` 不是给用户看的菜单标签,而是用户点击后直接发给买家 Agent 的自然指令。 + +下一步由用户按 `keep / revise / weak / reject` 审核 v0.4 样本。不要一次性扩展到 120 条;先用 20 条验证 schema、rubric 和审核口径。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/07-capstone-lab/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/07-capstone-lab/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/08-review-loop/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/08-review-loop/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/09-closeout-archive/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/guides/09-closeout-archive/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-16-transformers-pipeline-smoke-test.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-16-transformers-pipeline-smoke-test.md new file mode 100644 index 0000000..53f8c6b --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-16-transformers-pipeline-smoke-test.md @@ -0,0 +1,44 @@ +# 2026-06-16 Transformers Pipeline Smoke Test + +### 运行目标 + +确认 Hugging Face Transformers 的最小 pipeline 能在当前 demo 环境中运行,并验证 Cursor / Pyright 配置与运行时环境的差异。 + +### 运行环境 + +- Demo:`demo/hugging-face-course-learning` +- Python env:`.venv` +- 运行方式:`uv run transformer-lib/pipeline.py` +- 关键依赖:`transformers==5.12.0`,`torch>=2.12.0` + +### 运行结果 + +用户已跑通 sentiment/text classification pipeline。 + +观察到的输出: + +```text +[{'label': 'POSITIVE', 'score': 0.9982948899269104}] +``` + +### 重要现象 + +1. `pipeline("sentiment-analysis")` 运行时可成功,但 BasedPyright 报 `reportCallIssue`。 +2. 原因是 Transformers runtime 支持 `sentiment-analysis` alias,但类型 overload 中 canonical task 是 `text-classification`。 +3. demo 已改为 `pipeline("text-classification")`,保持运行结果等价,同时减少静态类型误报。 +4. 运行时出现 Hugging Face Hub unauthenticated warning;当前 smoke test 可忽略,后续大量下载或 gated model 再配置 `HF_TOKEN`。 + +### 当前结论 + +第一个 HF pipeline 已跑通。下一步不急着进入 LoRA,而是要把 pipeline 的机制拆开: + +- task name 如何映射到默认 model。 +- model/tokenizer/config 如何被下载和缓存。 +- pipeline 输入如何经过 tokenizer、model、postprocess。 +- 为什么不指定 model 在生产中不推荐。 + +### 下一步动作 + +- 把当前脚本从“能跑”改成“可观察”:打印 pipeline 的 task、model name、tokenizer、config、缓存/下载行为。 +- 固定 model name,避免依赖 Transformers 默认模型选择。 +- 将运行命令和输出沉淀到 demo README 或 runbook。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-eli5-dataset-inspection.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-eli5-dataset-inspection.md new file mode 100644 index 0000000..32b5fac --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-eli5-dataset-inspection.md @@ -0,0 +1,53 @@ +# 2026-06-22 ELI5 Dataset Inspection + +### 运行目标 + +观察 causal LM recipe 的第一步:原始 ELI5 数据集长什么样,为什么后续需要 flatten、抽取 `answers.text`、再 tokenizer。 + +### 运行环境 + +- Demo:`demo/hugging-face-course-learning` +- 运行方式:`uv run transformer-work/casual_lm/inspect_dataset.py` + +### 运行结果 + +用户已跑通数据集观察脚本。 + +观察到: + +```text +DatasetDict({ + train: Dataset({ + features: ['q_id', 'title', 'selftext', 'category', 'subreddit', 'answers', 'title_urls', 'selftext_urls'], + num_rows: 4000 + }) + test: Dataset({ + features: ['q_id', 'title', 'selftext', 'category', 'subreddit', 'answers', 'title_urls', 'selftext_urls'], + num_rows: 1000 + }) +}) +``` + +关键样本结构: + +- 每条样本是一个 Reddit 问题。 +- `title` / `selftext` 是问题文本。 +- `answers` 是嵌套结构,包含多个回答的 `text`、`score`、`a_id` 等。 +- `answers.text` 是 list,不是单个字符串。 + +### 当前结论 + +ELI5 原始数据不是“干净的一段训练文本”,而是“问题 + 多个回答 + 元数据”的结构化样本。 + +因此后续代码的必要性变清楚了: + +- `flatten()`:把嵌套字段摊平,方便访问 `answers.text`。 +- `" ".join(x)`:把多个回答文本合成一段可训练文本。 +- `tokenizer(...)`:把文本转成 token ids。 +- `remove_columns`:删掉 Trainer/collator 不需要的原始元数据字段。 + +### 下一步动作 + +- 运行 `inspect_tokenization.py`,观察文本如何变成 `input_ids` 和 `attention_mask`。 +- 对比 tokenizer 前后的字段变化。 +- 手动 decode 一小段 `input_ids`,确认 token ids 仍能还原成人类可读文本。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-eli5-tokenization-inspection.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-eli5-tokenization-inspection.md new file mode 100644 index 0000000..79e710d --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-eli5-tokenization-inspection.md @@ -0,0 +1,54 @@ +# 2026-06-22 ELI5 Tokenization Inspection + +### 运行目标 + +观察 tokenizer 如何把 `answers.text` 里的自然语言文本转换成模型可读的 token ids,并确认 token ids 可以 decode 回文本。 + +### 运行环境 + +- Demo:`demo/hugging-face-course-learning` +- Notebook:`transformer-work/casual_lm/inspect_tokenizer.ipynb` + +### 运行结果 + +用户已观察到 tokenization 后的数据结构: + +- 原始字段仍然保留时,dataset 增加了 `input_ids` 和 `attention_mask`。 +- 使用 `remove_columns` 后,样本只剩下训练需要的 token 字段。 +- `tokenizer.decode(input_ids)` 可以还原出拼接后的回答文本。 + +观察到的重要 warning: + +```text +Token indices sequence length is longer than the specified maximum sequence length for this model +``` + +这说明某些拼接后的回答 token 数超过模型上下文上限 `1024`。当前阶段这不是 tokenizer 错误,而是在提醒:这些长序列不能直接喂给 DistilGPT2,需要后续 `group_texts` 按 `block_size` 切成模型可接受的固定长度片段。 + +### 当前结论 + +tokenizer 只完成了这一步: + +```text +自然语言文本 -> input_ids / attention_mask +``` + +它还没有完成 causal LM 训练需要的: + +```text +长 token stream -> fixed-size blocks -> labels +``` + +因此下一步必须观察 `group_texts`: + +- 为什么要把 token 拼成长序列。 +- 为什么要按 `block_size=128` 切块。 +- 为什么 `labels = input_ids.copy()`。 +- 为什么超过 1024 的 warning 会在切块后被解决。 + +### 下一步动作 + +- 运行或编写 `inspect_lm_blocks.py` / notebook。 +- 打印切块前后的 token 长度。 +- 打印第一条 block 的 `input_ids`、`labels`。 +- decode 第一条 block,确认 block 仍然是连续文本片段。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-lm-block-inspection.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-lm-block-inspection.md new file mode 100644 index 0000000..1b6c083 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-22-lm-block-inspection.md @@ -0,0 +1,51 @@ +# 2026-06-22 LM Block Inspection + +### 运行目标 + +观察 `group_texts` 如何把 tokenizer 产生的长短不一 token 序列,改造成 causal LM 训练需要的固定长度 block,并生成 `labels`。 + +### 运行环境 + +- Demo:`demo/hugging-face-course-learning` +- Notebook:`transformer-work/casual_lm/inspect_tokenizer.ipynb` +- 参数:`block_size = 128` + +### 运行结果 + +用户已观察到 `group_texts` 后第一条样本: + +- `input_ids` 长度为 `128`。 +- `labels` 长度为 `128`。 +- `input_ids == labels` 为 `True`。 +- `tokenizer.decode(input_ids)` 后是一段连续文本片段。 + +### 当前结论 + +`group_texts` 是 causal LM 数据构造的关键转折点: + +```text +tokenized samples + -> concatenate into one token stream + -> split into fixed-size blocks + -> labels = input_ids.copy() +``` + +这一步解决两个问题: + +- 训练样本长度统一,方便 batch 和模型处理。 +- 把普通 token 序列变成 next-token prediction 的监督样本。 + +`labels == input_ids` 并不表示模型在“复制输入”。对于 `AutoModelForCausalLM`,loss 计算时模型内部会 shift: + +```text +输入位置: token_0 token_1 token_2 ... +预测目标: token_1 token_2 token_3 ... +``` + +所以表面上 labels 和 input_ids 一样,实际训练目标仍然是预测下一个 token。 + +### 下一步动作 + +- 观察 `DataCollatorForLanguageModeling(mlm=False)` 对 batch 做了什么。 +- 确认 GPT2/DistilGPT2 为什么需要设置 `tokenizer.pad_token = tokenizer.eos_token`。 +- 进入 model forward:看 `AutoModelForCausalLM` 输出 logits 的 shape。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-causal-lm-training-completion.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-causal-lm-training-completion.md new file mode 100644 index 0000000..a39f2ad --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-causal-lm-training-completion.md @@ -0,0 +1,73 @@ +# 2026-06-23 Causal LM Training Completion + +### 运行目标 + +完成一次小步 Causal LM fine-tuning,从 ELI5 文本数据进入 `Trainer.train()`,并观察训练结果、评估指标、Hub 上传和生成表现。 + +### 运行环境 + +- Notebook:`demo/hugging-face-course-learning/transformer-work/casual_language_model.ipynb` +- Base model:`distilbert/distilgpt2` +- Training args:`max_steps=200`,`per_device_train_batch_size=16`,`eval_strategy="steps"`,`eval_steps=50`,`save_strategy="no"`,`dataloader_pin_memory=False` + +### 运行结果 + +训练完成: + +```text +TrainOutput( + global_step=200, + training_loss=3.9759389686584474, + train_runtime=140.8586, + train_samples_per_second=22.718, + train_steps_per_second=1.42, + epoch=0.3058103975535168 +) +``` + +评估完成: + +```text +Perplexity: 47.81 +``` + +模型已上传到 Hugging Face Hub: + +```text +https://huggingface.co/hedonwang/my_awesome_eli5_clm-model +commit: 147c2fc05c36740c96eea2ab572ee1c4c3cece70 +``` + +生成测试使用 prompt: + +```text +Somatic hypermutation allows the immune system to +``` + +生成结果能延续生物学相关开头,但很快漂移到 NES / console 的重复片段,说明当前小步训练已经跑通链路,但生成质量仍受训练步数、数据规模、采样策略、上下文主题一致性和 base model 能力约束。 + +### 当前结论 + +这次 notebook 跑通的是完整的 Causal LM fine-tuning 最小闭环: + +```text +ELI5 raw records + -> flatten nested answers + -> tokenize answer text + -> concatenate token streams + -> split into fixed-size blocks + -> create labels for next-token prediction + -> collate batches + -> fine-tune AutoModelForCausalLM + -> evaluate perplexity + -> push artifact + -> run text generation +``` + +训练 loss / perplexity 只能说明模型在 held-out token prediction 上的平均不确定性;它不能直接保证生成内容事实正确、结构稳定或不会重复。生成结果里的主题漂移是后续学习 decoding、eval 和 task-specific fine-tuning 时必须正视的问题。 + +### 下一步动作 + +- 解释 `Trainer.train()` 背后实际发生的 forward / loss / backward / optimizer step。 +- 修正推理 cell:从 Hub reload model 后需要重新构造 `inputs = tokenizer(prompt, return_tensors="pt").input_ids`,并注意 device 对齐。 +- 进入 Chapter 2 的 pipeline 拆解:把 `pipeline("text-generation")` 展开成 tokenizer、model.generate、decode 三步。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-data-collator-trainer-warning-inspection.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-data-collator-trainer-warning-inspection.md new file mode 100644 index 0000000..09d448c --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-data-collator-trainer-warning-inspection.md @@ -0,0 +1,33 @@ +# 2026-06-23 Data Collator / Trainer Warning Inspection + +### 运行目标 + +观察进入 data collator / Trainer 后出现的运行 warning,并区分哪些是训练语义相关提示,哪些只是设备后端能力提示。 + +### 运行结果 + +用户观察到两个 warning: + +```text +The tokenizer has new PAD/BOS/EOS tokens that differ from the model config and generation config. +The model config and generation config were aligned accordingly... +Updated tokens: {'pad_token_id': 50256}. +``` + +```text +'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used. +``` + +### 当前结论 + +第一个 warning 来自 `tokenizer.pad_token = tokenizer.eos_token` 这一类设置。GPT2 / DistilGPT2 默认没有独立的 `pad_token`,但 batch padding 需要一个 padding token id。当前运行把 tokenizer 的 `pad_token_id` 设置为 `50256`,也就是 GPT2 的 `eos_token_id`,Transformers 随后把 model config / generation config 同步到 tokenizer 的值。 + +这不是错误,而是提醒:tokenizer、model config、generation config 原本的特殊 token 配置不完全一致,库已自动对齐。当前 causal LM 学习阶段可以接受;后续做正式训练或生成评测时,应显式保存并检查 tokenizer / model config,避免训练和推理阶段使用不同的 special token 配置。 + +第二个 warning 是 PyTorch 在 Apple MPS 后端上的设备能力提示。`pin_memory=True` 主要服务于 CPU 到 CUDA GPU 的数据拷贝优化;MPS 当前不支持 pinned memory,因此该优化不会生效。它不影响训练语义,也不表示 batch、labels 或 loss 有问题。 + +### 下一步动作 + +- 在 notebook 或脚本里打印 `tokenizer.pad_token`、`tokenizer.pad_token_id`、`model.config.pad_token_id`,确认三者对齐。 +- 观察 collator 输出 batch 的 keys、shape 和 padding 位置。 +- 进入 model forward,打印 `loss` 与 `logits.shape`,确认 causal LM 训练链路已经从样本构造走到模型计算。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-mps-training-speed-inspection.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-mps-training-speed-inspection.md new file mode 100644 index 0000000..eb4aa12 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-23-mps-training-speed-inspection.md @@ -0,0 +1,44 @@ +# 2026-06-23 MPS Training Speed Inspection + +### 运行目标 + +判断当前 Causal LM 训练在 Apple Silicon 上偏慢时,应该优先优化硬件使用、训练参数,还是学习实验规模。 + +### 运行结果 + +当前环境检查结果: + +```text +torch 2.12.0 +mps available True +mps built True +``` + +用户观察到训练进度约为: + +```text +299/3924, 2.51 it/s, Epoch 0.23/3, ETA about 24 min +``` + +### 当前结论 + +当前慢的主要原因不是 MPS 不可用,而是课程默认训练参数更接近“完整跑一遍 recipe”: + +- `num_train_epochs` 默认约等于 3。 +- `per_device_train_batch_size` 未显式设置,使用 Trainer 默认值。 +- `lm_dataset["train"]` 已经被 `group_texts` 扩展到上万条 block。 +- 当前学习目标是理解 causal LM pipeline,不需要先完整训练 3 epoch。 + +M 芯片能通过 MPS 提供 GPU 加速,但它不是 CUDA 训练卡。对本 topic 的学习节奏来说,优先级应该是: + +```text +先缩小实验规模,快速观察链路 +再做 MPS 参数微调 +最后再迁移到云 GPU / 百炼类平台做更完整训练 +``` + +### 下一步动作 + +- 快速学习 run:使用小数据子集、`max_steps`、关闭 push/save,先把 `dataset -> collator -> model forward -> loss -> train` 跑通。 +- MPS 参数 run:确认 `trainer.args.device` 为 `mps`,尝试 `bf16=True` / `fp16=True`,并设置 `dataloader_pin_memory=False`。 +- 正式训练 run:再恢复更大数据量、更长训练、完整 eval 和 artifact 保存。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-26-sequence-classification-lab-completion.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-26-sequence-classification-lab-completion.md new file mode 100644 index 0000000..419527c --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-26-sequence-classification-lab-completion.md @@ -0,0 +1,38 @@ +# 2026-06-26 Sequence Classification Lab Completion + +### 运行目标 + +完成 Hugging Face Transformers text / sequence classification recipe,建立从 IMDb 文本分类数据到 DistilBERT fine-tuning 和 pipeline 推理的整体体感。 + +### 运行环境 + +- Notebook:`demo/hugging-face-course-learning/transformer-work/sequence_classification.ipynb` +- Dataset:`stanfordnlp/imdb` +- Base model:`distilbert/distilbert-base-uncased` +- 关键组件:`AutoTokenizer`、`DataCollatorWithPadding`、`AutoModelForSequenceClassification`、`TrainingArguments`、`Trainer`、`pipeline` + +### 用户观察 + +用户已完成第二个 lab,对大致流程有一些理解,但整体仍是模糊印象;当前主要卡点不是“代码是否能跑”,而是仍然只能按教程抄,无法自己推导出这条链路。 + +### 当前结论 + +Sequence classification 的主链路应被理解为: + +```text +raw text + label + -> tokenizer(text, truncation=True) + -> DataCollatorWithPadding 动态组成 batch + -> AutoModelForSequenceClassification 输出 [batch_size, num_labels] logits + -> compute_metrics 把 logits argmax 后与 labels 比较 + -> Trainer 驱动 train / eval / save / push + -> pipeline 或手动 forward 做推理 +``` + +当前退出标准不是“能复现官方代码”,而是能从任务契约反推每个对象为什么存在、输入输出是什么、删掉会坏在哪里。 + +### 下一步动作 + +- 按 [`../../guides/04-lesson-lab/04-sequence-classification-derivation.md`](../../guides/04-lesson-lab/04-sequence-classification-derivation.md) 复查 notebook。 +- 补 raw sample、tokenized sample、collated batch、model forward、logits -> label 的实际打印观察。 +- 盖住教程,用 guide 里的 8 段 skeleton 重新写一遍最小 sequence classification flow。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-26-sequence-classification-pipeline-load-error.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-26-sequence-classification-pipeline-load-error.md new file mode 100644 index 0000000..ab55262 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-26-sequence-classification-pipeline-load-error.md @@ -0,0 +1,46 @@ +# 2026-06-26 Sequence Classification Pipeline Load Error + +### 运行目标 + +在 sequence classification 训练后,用 `pipeline` 加载 fine-tuned model 并做一次 sentiment/text-classification 推理。 + +### 用户观察 + +运行: + +```python +from transformers import pipeline + +classifier = pipeline("sentiment-analysis", model="my_awesome_imdb_beat-model") +classifier("I've been waiting for a Hugging Face course my whole life.") +``` + +报错: + +```text +ValueError: Unrecognized model in my_awesome_imdb_beat-model. +Should have a `model_type` key in its config.json. +``` + +### 当前诊断 + +`pipeline` 会把字符串形式的 `model` 当作本地目录或 Hub repo 来解析。当前本地存在 `transformer-work/my_awesome_imdb_beat-model`,但该目录为空,不是一个完整的 Transformers model artifact,因此无法从 `config.json` 识别 `model_type`。 + +一个可加载目录至少需要能提供模型 config、权重和 tokenizer 文件,例如: + +```text +config.json +model.safetensors +tokenizer.json +tokenizer_config.json +``` + +### 下一步动作 + +- 如果只是快速验证推理,用 `pipeline("text-classification", model=trainer.model, tokenizer=tokenizer)`。 +- 如果要验证保存/加载链路,先执行 `trainer.save_model(save_dir)` 和 `tokenizer.save_pretrained(save_dir)`,再用 `pipeline(..., model=save_dir, tokenizer=save_dir)`。 +- 相关排障已补入 [`../../guides/04-lesson-lab/04-sequence-classification-derivation.md`](../../guides/04-lesson-lab/04-sequence-classification-derivation.md)。 + +### 2026-06-27 更新 + +用户报告该问题已跑通。当前不继续追新实验,下一步转入 sequence classification derivation review:补 raw sample、tokenized sample、collated batch、model forward、logits -> label 的实际观察,并尝试盖住教程重写最小 flow。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-27-chapter1-5-task-lab-sweep-route.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-27-chapter1-5-task-lab-sweep-route.md new file mode 100644 index 0000000..372a536 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-27-chapter1-5-task-lab-sweep-route.md @@ -0,0 +1,40 @@ +# 2026-06-27 Chapter 1/5 Task Lab Sweep 路线调整 + +### 用户判断 + +用户希望先把 Hugging Face LLM Course Chapter 1/5 `How Transformers solve tasks` 这一节的 task labs 都完成,再进入 Chapter 2 `Behind the pipeline`。 + +用户的理由是:这一节覆盖了大模型多个方面的能力;虽然会有很多重复,但对初学者来说,前期机械式重复有助于建立手感。 + +### 当前结论 + +接受这个调整。当前阶段机械重复不是问题,前提是每次重复都要抽取同一张结构图: + +```text +raw input + -> tokenizer / processor + -> model + -> task-specific head / decoder + -> logits / generated ids / spans + -> loss / metric / postprocess + -> human-readable output +``` + +Chapter 2 `Behind the pipeline` 暂时后移。先完成 Chapter 1/5 task sweep,再回头拆 pipeline,会更容易理解 pipeline 是如何随 task 改变 processor、model class、head 和 postprocess 的。 + +官方页面中对应 `Ready to try your hand...` 的 task labs 一共 8 个: + +1. Text generation / causal language modeling +2. Text classification +3. Token classification +4. Question answering +5. Summarization +6. Translation +7. Automatic speech recognition +8. Image classification + +### 下一步动作 + +- 新增 guide:[`../../guides/04-lesson-lab/05-chapter1-5-task-lab-sweep.md`](../../guides/04-lesson-lab/05-chapter1-5-task-lab-sweep.md)。 +- 下一小步:Token Classification lab。 +- 每个 lab 只追求小步跑通和 shape/输出观察,不追求完整训练效果。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-27-sequence-classification-derivation-review.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-27-sequence-classification-derivation-review.md new file mode 100644 index 0000000..d67aec4 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-27-sequence-classification-derivation-review.md @@ -0,0 +1,127 @@ +# 2026-06-27 Sequence Classification Derivation Review + +### 用户当前复述 + +用户已经能解释前三段链路: + +- Dataset:加载原始数据集,提供 train / test,每条数据包含 `text` 和 `label`;`label` 服务于最终分类目标。 +- Tokenizer:把 dataset 中的 `text` 转为 `input_ids` 和 `attention_mask`,因为模型不能直接处理自然语言字符串。 +- Collator:把多条变长样本通过 padding 填充到一样长,从而可以 batch 训练。 + +### 需要校正的点 + +Tokenizer 不是把文字“向量化”。更准确的链路是: + +```text +text + -> tokenizer + -> tokens + -> token ids + -> model embedding layer + -> embedding vectors +``` + +`input_ids` 是 embedding table 的查表索引;真正的向量表示发生在模型内部的 embedding layer。 + +### 当前卡点 + +用户暂时不知道 4 / 5 要做什么: + +```text +4. 手动跑 model(**batch):观察 outputs.loss 和 outputs.logits.shape。 +5. 手动 argmax(logits) 再查 id2label:理解 pipeline 的 label 如何从模型输出得到。 +``` + +### 下一步动作 + +- 按 guide 的 `Model forward` / `Manual prediction` 小节,把 4 / 5 的 probe 加到 notebook。 +- 只回答三个问题:`batch` 里有什么,`model forward` 输出了什么,`logits` 如何变成最终 `label`。 +- 相关说明已补入 [`../../guides/04-lesson-lab/04-sequence-classification-derivation.md`](../../guides/04-lesson-lab/04-sequence-classification-derivation.md)。 + +### Collator probe 报错 + +用户在手动运行: + +```python +features = [tokenized_imdb["test"][i] for i in range(3)] +batch = data_collator(features=features) +``` + +时遇到: + +```text +ValueError: too many dimensions 'str' +Perhaps your features (`text` in this case) have excessive nesting +``` + +诊断:`tokenized_imdb["test"][i]` 里仍保留原始 `text` 字符串字段。手动调用 `data_collator` 时,它会尝试把 features 中的字段整理成 tensor;`text` 不是模型输入 tensor,因此报错。 + +修复:手动 probe 时只保留 `input_ids`、`attention_mask`、`label`,再交给 `DataCollatorWithPadding`。guide 中的 Collated batch 和 4/5 probe 代码已同步修正。 + +### Model forward / Manual prediction 观察 + +用户按 guide 跑通 4 / 5 probe,观察到: + +```text +batch keys: dict_keys(['input_ids', 'attention_mask', 'labels']) +labels: tensor([0, 0, 0], device='mps:0') +loss: tensor(0.5448, device='mps:0', grad_fn=<NllLossBackward0>) +logits shape: torch.Size([3, 2]) +logits: tensor([[ 0.3006, -0.3274], + [-0.0302, -0.0735], + [ 0.1679, -0.1778]], device='mps:0', grad_fn=<LinearBackward0>) +pred ids: tensor([0, 0, 0], device='mps:0') +pred labels: ['POSITIVE', 'POSITIVE', 'POSITIVE'] +gold labels: ['POSITIVE', 'POSITIVE', 'POSITIVE'] +``` + +当前解释: + +- `batch keys` 表明模型真正吃到的是 `input_ids`、`attention_mask`、`labels`,不再包含原始 `text`。 +- `labels` 是真实类别 id;当前 3 条样本的真实类别 id 都是 `0`。 +- `loss=0.5448` 是模型把 `logits` 和 `labels` 对比后算出的分类损失;`grad_fn=<NllLossBackward0>` 说明这个 loss 仍连接着反向传播计算图。 +- `logits shape=[3, 2]` 表示 batch 有 3 条样本,每条样本有 2 个类别分数。 +- 每行 logits 里第 0 列都大于第 1 列,所以 `argmax` 得到 `pred ids=[0,0,0]`。 +- `pred labels` 和 `gold labels` 都是 `POSITIVE`,说明在当前 `model.config.id2label` 下,这 3 条样本预测正确。 + +需要继续确认:当前输出显示 label id `0` 被映射为 `POSITIVE`。这必须和数据集真实 label 语义保持一致;否则可能出现数字预测对了,但文字标签反了的问题。guide 已补充 `id2label` / dataset label sanity check。 + +### 当前 checkpoint + +用户确认已理解 sequence classification 的 forward / logits / loss / label mapping 链路: + +```text +batch + -> model(**batch) + -> loss + logits + -> argmax(logits) + -> pred ids + -> id2label + -> human-readable labels +``` + +本轮 lesson 不再继续扩展新 API。下一步是闭卷重写最小 sequence classification flow,验证是否能脱离教程按任务契约推导代码。 + +### Closed-book rewrite 验收 + +用户在临时 notebook `sequence_classification_2.ipynb` 中闭卷实现了最小 sequence classification flow: + +```text +load dataset +-> tokenizer +-> map preprocess +-> DataCollatorWithPadding +-> AutoModelForSequenceClassification +-> TrainingArguments / Trainer +-> compute_metrics +-> model forward +-> logits -> argmax -> id2label +``` + +验收结果:通过。notebook 无保存的错误输出,并打印了 batch keys、labels、loss、logits shape、logits、pred ids、pred labels、gold labels。 + +学习 caveat:临时 notebook 使用 `select(range(300))` 构造小数据集,IMDb 前若干条样本可能标签分布偏斜;它适合机制复现,不适合作为效果评估。后续小样本训练应使用 `shuffle(seed=42)` 或构造 label-balanced subset。 + +临时文件已删除,因为主 notebook `sequence_classification.ipynb` 已保留该逻辑。 + +下一步:进入 Chapter 2 `Behind the pipeline`,手写 `pipeline("text-classification")` 的 tokenizer -> model -> postprocess 最小等价流程。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-30-token-classification-lab-recap.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-30-token-classification-lab-recap.md new file mode 100644 index 0000000..20351ab --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-06-30-token-classification-lab-recap.md @@ -0,0 +1,65 @@ +# 2026-06-30 Token Classification Lab 复述与完成证据 + +### 用户当前复述 + +用户已经能按任务契约解释 token classification 的主链路: + +```text +sequence 中每个 word 都需要一个 NER tag +-> WNUT 提供 tokens 和 ner_tags,二者在 word-level 一一对应 +-> tokens 不是模型 tokenizer 之后的 token,而更接近 words +-> tokenizer 会把 word 拆成 subword,并插入 special tokens +-> 因此需要 label alignment +-> special tokens 和非 word-start subword 设置为 -100,不参与 loss +-> 训练后用 pipeline 验证 NER 输出 +``` + +### 需要校正的点 + +1. “输出最后就是一个 `num_labels` 大小的分类器”可以更精确地说成:token classification head 对每个 token hidden state 输出一个 `num_labels` 维 logits 向量,所以整体 `logits.shape == [batch_size, seq_len, num_labels]`。 +2. 这里的 `seq_len` 是 tokenizer、special tokens、padding / truncation 后的模型序列长度,不是原始 `tokens` 的 word 数量。 +3. `-100` 不是直接“告诉 Trainer”,而是作为 labels 进入模型 loss;PyTorch cross entropy 默认用 `ignore_index=-100` 忽略这些位置。Trainer 只是负责把 batch 传给 model。 +4. “非 word-start-token 设置为 -100”是本 lab 采用的对齐策略,不是唯一策略;也可以把同一个 word 的标签复制到所有 subword 上,但当前策略更容易避免重复计算一个原始词的 loss。 + +### 当前结论 + +用户已经抓住本 lab 的关键:token classification 的难点不是训练 API,而是 tokenizer 改变了标注坐标系。 + +### 完成证据 + +Notebook:`demo/hugging-face-course-learning/transformer-work/token_classification.ipynb` + +用户已跑通: + +```text +flaitenberger/wnut_17 +-> tokenizer + label alignment +-> DataCollatorForTokenClassification +-> AutoModelForTokenClassification +-> max_steps=50 小步训练 +-> trainer.save_model("my_awesome_wnut_model") +-> tokenizer.save_pretrained("my_awesome_wnut_model") +-> pipeline("ner", model="my_awesome_wnut_model") +``` + +本地保存目录已包含可加载 artifact: + +```text +config.json +model.safetensors +tokenizer.json +tokenizer_config.json +training_args.bin +``` + +Pipeline 验证句子: + +```text +The Golden State Warriors are an American professional basketball team based in San Francisco. +``` + +观察到模型能走完 NER postprocess,并输出 `B-location` 预测,包括 `golden`、`san`、`francisco`。当前分数较低且预测质量不作为重点;本 lab 的验收目标是跑通 per-token classification 链路,并能解释 label alignment 与 `[batch_size, seq_len, num_labels]`。 + +### 下一步 + +Lab 3/8 Token classification 收口。下一步进入 Lab 4/8 Question answering,观察 start/end logits 和 span extraction。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-03-summarization-lab-recap.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-03-summarization-lab-recap.md new file mode 100644 index 0000000..67f75b4 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-03-summarization-lab-recap.md @@ -0,0 +1,67 @@ +# Summarization Lab Recap + +## 学习现场 + +本 note 记录用户在 Chapter 1/5 Summarization lab 中已经实际跑通和排障过的内容。它是运行证据恢复入口,不替代独立 guide。 + +相关 guide: + +- [`../../guides/04-lesson-lab/08-summarization-derivation.md`](../../guides/04-lesson-lab/08-summarization-derivation.md) +- [`../../guides/04-lesson-lab/09-t5-task-prefix.md`](../../guides/04-lesson-lab/09-t5-task-prefix.md) +- [`../../guides/04-lesson-lab/10-billsum-dataset-loading.md`](../../guides/04-lesson-lab/10-billsum-dataset-loading.md) +- [`../../guides/04-lesson-lab/11-rouge-metric.md`](../../guides/04-lesson-lab/11-rouge-metric.md) +- [`../../guides/04-lesson-lab/13-summarization-compute-metrics-overflow.md`](../../guides/04-lesson-lab/13-summarization-compute-metrics-overflow.md) +- [`../../guides/04-lesson-lab/14-summarization-inference-generate.md`](../../guides/04-lesson-lab/14-summarization-inference-generate.md) + +## 运行证据 + +- Notebook:`demo/hugging-face-course-learning/transformer-work/summarization.ipynb` +- 数据集:`FiscalNote/billsum`,`split="ca_test"`,原始样本数 1237。 +- 训练/评估切分:`train_test_split(test_size=0.2)`,得到约 989 条 train、248 条 eval。 +- 模型:`google-t5/t5-small`,使用 `AutoModelForSeq2SeqLM` / `Seq2SeqTrainer`。 +- 训练输出:`global_step=248`,`epoch=4.0`,`train_loss≈3.02`。 +- 推理加载:`my_awesome_billsum_model/checkpoint-248`。 +- 推理样例输出:模型能对 Inflation Reduction Act 示例生成简短摘要,说明 `tokenizer -> model.generate -> decode` 链路已跑通。 + +## 本次修正点 + +- `load_dataset("billsum")` 在当前环境中会触发短名 URI 解析问题;改用完整 ID `FiscalNote/billsum`。 +- `split="ca_test"` 返回单个 `Dataset`;再用 `train_test_split` 生成训练/评估 `DatasetDict`。 +- notebook 中用 `billsum_raw` 保留原始 `Dataset`,避免反复运行 cell 时把变量覆盖成 `DatasetDict` 后再调用 `.train_test_split(...)`。 +- T5 输入前缀 `summarize: ` 是任务提示,会进入 `input_ids`,不是 label。 +- ROUGE 依赖需要 `rouge-score` / `nltk`;依赖已加入 demo 环境。 +- 当前 Transformers 路径下,完整 eval 的 `predictions` 中会混入 `-100`,需要在 `compute_metrics` 中把 predictions 和 labels 的 `-100` 都替换为 `tokenizer.pad_token_id` 后再 decode。 +- `TrainingArguments(output_dir="my_awesome_billsum_model")` 的根目录不一定是可直接加载模型;本次可加载模型在 `my_awesome_billsum_model/checkpoint-248`。 + +## 机制观察 + +Summarization 和前几个 BERT-style lab 的核心差异: + +```text +classification / NER / QA: + input text -> label / token labels / span positions + +summarization: + source text -> generated target text +``` + +本 lab 已观察到: + +- `labels` 是目标 summary 的 token ids,不是分类 id。 +- `outputs.logits.shape` 的语义是 decoder 每个目标位置上的 vocab 分布。 +- `model.generate(...)` 生成的是 token ids,需要 `tokenizer.decode(...)` 或 `batch_decode(...)` 变回文本。 +- ROUGE 比较的是 decoded prediction 和 decoded reference 的文本重叠,不是 token id。 + +## 下一步 + +进入 Translation lab,重点不再重新解释 encoder-decoder,而是对比: + +```text +summarization: + source document -> shorter same-language summary + +translation: + source language text -> target language text +``` + +观察它和 summarization 在 tokenizer、labels、`generate`、decode/postprocess 上哪些完全同构,哪些来自任务提示、语言方向和指标差异。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-architecture-selection-summary.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-architecture-selection-summary.md new file mode 100644 index 0000000..3276587 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-architecture-selection-summary.md @@ -0,0 +1,39 @@ +# Chapter 1/6 架构选择总结 + +## 场景 + +用户完成 Hugging Face LLM Course Chapter 1/6 `Transformer Architectures` 的核心归纳:从任务形态反推 encoder-only、decoder-only、encoder-decoder 的选择。 + +## 用户总结 + +用户的最终归纳: + +> 理解 encoder,生成 decoder,extract 是 encoder,generate new token 是 decoder,如果 generate 需要理解完整的 input,那要么用 decoder 把“读原文”和“生成摘要”都塞进一个续写过程,要么就分成 encoder + decoder 2 个环节,先理解,后生成。 + +## Agent 校验 + +这个总结已经能覆盖 Chapter 1/6 当前学习目标。更精炼地说: + +```text +encoder-only: + 理解已有输入 + 输出 label / token label / span + +decoder-only: + 基于已有前文继续生成 + 输出新的 token 序列 + +encoder-decoder: + 先理解完整 source + 再生成新的 target +``` + +一个关键进步是用户区分了“能做”和“更适合”:decoder-only 可以通过 prompt 把读原文和生成摘要都塞进续写过程;encoder-decoder 则把 source 理解和 target 生成显式拆成两个环节。这个区别足够说明 Chapter 1/6 的三类架构映射已经通过。但 Chapter 1/6 还没有结束,后续仍需读完 attention mechanisms,再进入 Chapter 2 拆 `pipeline` 如何选择 tokenizer、model 和 postprocess。 + +## 后续提醒 + +Chapter 1/6 架构映射已经完成。后续仍需提醒用户亲自完成: + +- Chapter 1/6 attention mechanisms:理解 full attention 的 O(n^2) 成本,以及 LSH / local attention / axial positional encodings 的取舍。 +- Chapter 2 前后手写 `pipeline("text-classification")` 的 tokenizer -> model -> postprocess 等价流程。 +- Trainer 复盘时补 batch keys / shape / device,并手动运行 `model(**batch)` 观察 loss/logits。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-decoder-only-recap.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-decoder-only-recap.md new file mode 100644 index 0000000..4826357 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-decoder-only-recap.md @@ -0,0 +1,33 @@ +# Chapter 1/6 Decoder-only 复述 + +## 场景 + +用户继续阅读 Hugging Face LLM Course Chapter 1/6 `Transformer Architectures`,当前检查点是:为什么 Causal LM / text generation 更适合映射到 decoder-only。 + +## 用户复述 + +用户的理解: + +> Causal LM / text generation 的核心是续写当前 input,而不是从当前 input 里面摘东西。 + +## Agent 校验 + +这个复述抓住了 decoder-only 的核心:任务目标不是理解完整输入后做抽取或分类,而是基于已有前文继续生成后续 token。因此关键机制是: + +```text +已有前文 +-> causal / masked self-attention,只看当前位置之前的信息 +-> next-token logits +-> 选择或采样下一个 token +-> 把新 token 接回上下文继续生成 +``` + +这也解释了为什么 Causal LM 训练中 `labels` 看起来像 `input_ids`:目标不是复制整段输入,而是在每个位置预测“下一个 token”。 + +## 下一步 + +继续读 Chapter 1/6 的 `Sequence-to-sequence models`。下一次检查问题: + +```text +为什么 summarization 更像 encoder-decoder,而不是单纯 encoder-only 或 decoder-only? +``` diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-encoder-decoder-recap.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-encoder-decoder-recap.md new file mode 100644 index 0000000..489e30e --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-encoder-decoder-recap.md @@ -0,0 +1,56 @@ +# Chapter 1/6 Encoder-decoder 复述 + +## 场景 + +用户继续阅读 Hugging Face LLM Course Chapter 1/6 `Transformer Architectures`,当前检查点是:为什么 summarization 更适合映射到 encoder-decoder,同时辨析 encoder-only / decoder-only 是否也能做。 + +## 用户复述 + +用户的理解: + +> summarization 是先全部理解,然后再基于全部理解进行 text generation。不过其实,我觉得 encoder-only 或 decoder-only 也可以做呀。encoder 本身就是可以通读原文然后提取,而 decoder 可以基于 input 进行 text generation 出 summarization + +## Agent 校验 + +这个复述抓住了 encoder-decoder 的核心,也提出了一个重要边界:架构分类不是能力绝对边界,而是任务接口和归纳偏置。 + +对于 summarization,需要区分两类任务形态: + +```text +Extractive summarization: + 从原文中挑句子 / 片段 + -> encoder-only 可以做,因为它主要是理解和选择 + +Abstractive summarization: + 读完整原文后生成一段新的摘要 + -> encoder-decoder 更自然,因为它把“读输入”和“生成输出”分成两个组件 +``` + +Decoder-only 也可以做 summarization,尤其是现代 LLM:把原文和指令一起放进 prompt,然后继续生成摘要。但它的默认训练目标是 next-token prediction,长输入理解、源文对齐和输出控制都依赖 prompt、上下文长度和训练数据;它能做,不等于这个任务最自然地由 decoder-only 表达。 + +## 机制归纳 + +```text +encoder-only: + 适合理解 / 分类 / 标注 / 抽取 + 可以做 extractive summary + +decoder-only: + 适合续写 / 生成 + 可以通过 prompt 做 abstractive summary + +encoder-decoder: + 适合 source -> target 的转换 + 对 summarization / translation 这类“读完整输入再生成新序列”的任务最贴合 +``` + +## 下一步 + +完成 Chapter 1/6 的架构选择总结: + +```text +看到一个新任务时,先问: +1. 是理解已有输入,还是生成新文本? +2. 输出是标签 / span,还是新 token 序列? +3. 如果是新序列,它是否强依赖完整输入? +``` diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-encoder-only-recap.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-encoder-only-recap.md new file mode 100644 index 0000000..28d7d4b --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/2026-07-07-chapter1-6-encoder-only-recap.md @@ -0,0 +1,30 @@ +# Chapter 1/6 Encoder-only 复述 + +## 场景 + +用户开始阅读 Hugging Face LLM Course Chapter 1/6 `Transformer Architectures`,当前检查点是:为什么 text classification、token classification、extractive QA 更适合映射到 encoder-only。 + +## 用户复述 + +用户的理解: + +> text classification、token classification、extractive QA 都是基于完整的输入文本,在里面去找信息,不管是情感识别、文本分类、实体提取、QA 提取,都是要看到完整的上下文,基于对完整上下文的综合理解,才来做的。 + +## Agent 校验 + +这个复述抓住了 encoder-only 的核心:任务目标不是继续生成新文本,而是在已有输入中做理解、分类、标注或抽取。因此关键机制不是“模型名字叫 BERT”,而是: + +```text +完整输入 +-> 双向上下文理解 +-> task-specific head +-> label / token labels / answer span +``` + +## 下一步 + +继续读 Chapter 1/6 的 `Decoder models`。下一次检查问题: + +```text +为什么 Causal LM / text generation 更像 decoder-only,而不是 encoder-only? +``` diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/README.md new file mode 100644 index 0000000..f2fd4dc --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/README.md @@ -0,0 +1,32 @@ +# 04 Lesson Lab:运行证据索引 + +本 README 只做阶段索引和恢复入口;具体运行证据、用户复述、校正点和机制说明放在同目录独立 note 文件中。 + +## 当前入口 + +- 当前 lab:Chapter 1/8 `Deep dive into Text Generation Inference with LLMs`。 +- 最近完成:Chapter 1/6 架构选择总结,见 [`2026-07-07-chapter1-6-architecture-selection-summary.md`](2026-07-07-chapter1-6-architecture-selection-summary.md)。 +- 当前 guide:Chapter 1/8 LLM inference 见 [`../../guides/04-lesson-lab/16-chapter1-8-llm-inference.md`](../../guides/04-lesson-lab/16-chapter1-8-llm-inference.md);Chapter 1/6 架构与 attention mechanisms 见 [`../../guides/04-lesson-lab/15-chapter1-6-transformer-architectures.md`](../../guides/04-lesson-lab/15-chapter1-6-transformer-architectures.md)。 + +## Notes + +| Date | Note | 用途 | +| --- | --- | --- | +| 2026-06-16 | [`Transformers Pipeline Smoke Test`](2026-06-16-transformers-pipeline-smoke-test.md) | 验证最小 pipeline 运行与静态类型 alias 差异。 | +| 2026-06-22 | [`ELI5 Dataset Inspection`](2026-06-22-eli5-dataset-inspection.md) | 观察 Causal LM 原始数据结构。 | +| 2026-06-22 | [`ELI5 Tokenization Inspection`](2026-06-22-eli5-tokenization-inspection.md) | 观察 tokenizer 输出和长序列 warning。 | +| 2026-06-22 | [`LM Block Inspection`](2026-06-22-lm-block-inspection.md) | 观察 group_texts、block size 和 labels。 | +| 2026-06-23 | [`BPE vs WordPiece`](bpe-vs-wordpiece.md) | 独立 tokenizer 机制笔记。 | +| 2026-06-23 | [`Data Collator / Trainer Warning Inspection`](2026-06-23-data-collator-trainer-warning-inspection.md) | 解释 special token 与 MPS pin_memory warning。 | +| 2026-06-23 | [`MPS Training Speed Inspection`](2026-06-23-mps-training-speed-inspection.md) | 判断训练慢的原因和缩小实验策略。 | +| 2026-06-23 | [`Causal LM Training Completion`](2026-06-23-causal-lm-training-completion.md) | 记录 DistilGPT2 quick training、perplexity 和 Hub 上传。 | +| 2026-06-26 | [`Sequence Classification Lab Completion`](2026-06-26-sequence-classification-lab-completion.md) | 记录 IMDb sequence classification lab 完成与下一步 derivation review。 | +| 2026-06-26 | [`Sequence Classification Pipeline Load Error`](2026-06-26-sequence-classification-pipeline-load-error.md) | 记录本地 pipeline 加载空模型目录的诊断。 | +| 2026-06-27 | [`Sequence Classification Derivation Review`](2026-06-27-sequence-classification-derivation-review.md) | 记录用户复述、forward/logits probe 和闭卷重写验收。 | +| 2026-06-27 | [`Chapter 1/5 Task Lab Sweep 路线调整`](2026-06-27-chapter1-5-task-lab-sweep-route.md) | 记录先完成 8 个 task labs 的路线判断。 | +| 2026-06-30 | [`Token Classification Lab 复述`](2026-06-30-token-classification-lab-recap.md) | 记录用户对 token classification 的复述、校正点和完成证据。 | +| 2026-07-03 | [`Summarization Lab Recap`](2026-07-03-summarization-lab-recap.md) | 记录 BillSum summarization 训练、ROUGE 排障、checkpoint 推理和 seq2seq 观察。 | +| 2026-07-07 | [`Chapter 1/6 Encoder-only 复述`](2026-07-07-chapter1-6-encoder-only-recap.md) | 记录用户对 encoder-only 任务形状的复述与下一步 decoder-only 检查问题。 | +| 2026-07-07 | [`Chapter 1/6 Decoder-only 复述`](2026-07-07-chapter1-6-decoder-only-recap.md) | 记录用户对 decoder-only 续写任务形状的复述与下一步 encoder-decoder 检查问题。 | +| 2026-07-07 | [`Chapter 1/6 Encoder-decoder 复述`](2026-07-07-chapter1-6-encoder-decoder-recap.md) | 记录用户对 summarization 架构选择的复述,以及“能做”和“更适合”的边界。 | +| 2026-07-07 | [`Chapter 1/6 架构选择总结`](2026-07-07-chapter1-6-architecture-selection-summary.md) | 记录用户用任务形态反推 encoder-only、decoder-only、encoder-decoder 的最终归纳。 | diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/bpe-vs-wordpiece.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/bpe-vs-wordpiece.md new file mode 100644 index 0000000..46b49a0 --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/notes/04-lesson-lab/bpe-vs-wordpiece.md @@ -0,0 +1,262 @@ +# BPE 和 WordPiece 的区别 + +## 学习现场 + +用户在复看 Hugging Face LLM Course Chapter 1/5 的 text classification 部分时,先暂停 Trainer 深入,想先把本节实验跑一遍,建立体感。当前问题是: + +```text +BPE 和 WordPiece 的区别是什么? +``` + +## 当前理解 + +在当前课程上下文里,BPE 和 WordPiece 都是 subword tokenizer:它们不是只能按完整单词切,也不是只能按字符切,而是把文本拆成模型词表里的子词片段。 + +最重要的直觉差异: + +```text +BPE 更像频率驱动的压缩规则。 +WordPiece 更像词表驱动的最长匹配,并在训练时更重视片段之间的相对绑定强度。 +``` + +## 训练时和使用时的主宾语 + +这里的“训练时 / 使用时”首先说的是 tokenizer,不是 Transformer 模型: + +```text +tokenizer 训练时: + 主语:BPE / WordPiece 算法 + 宾语:课程语料 / 训练语料 + 目标:学出 vocab、subword、merge rules 或可匹配的 subword 词表 + +tokenizer 使用时: + 主语:已经训练好的 tokenizer + 宾语:新的输入文本 + 目标:把文本切成 tokens,再映射成 token ids + +model 训练时: + 主语:Transformer 模型 + 宾语:token ids / attention_mask / labels + 目标:更新模型参数 + +model 使用时: + 主语:已经训练好的模型 + 宾语:token ids + 目标:输出 logits、分类结果或生成下一个 token +``` + +因此 tokenizer 的输出不是 embedding 本身,而是: + +```text +raw text + -> tokenizer + -> tokens + -> token ids + -> model embedding layer + -> embedding vectors +``` + +也就是说,`input_ids` 只是 embedding table 的查表索引;真正的向量表示是在模型内部的 embedding layer 里产生的。 + +## BPE + +BPE 训练时从更小的单位开始,反复合并最常见的相邻 token pair: + +```text +("u", "g") 出现最多 +-> 合并成 "ug" +-> 继续找下一组最常见 pair +``` + +因此 BPE 的核心问题是: + +```text +哪两个相邻片段一起出现得最多? +``` + +BPE 的频率不是概率,没有分子分母。它更像一个计数: + +```text +pair_freq(pair) = sum(word_freq * pair 在当前 split 中出现的次数) +``` + +例如语料里有: + +```text +("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5) +``` + +初始按字符切开: + +```text +("h" "u" "g", 10) +("p" "u" "g", 5) +("p" "u" "n", 12) +("b" "u" "n", 4) +("h" "u" "g" "s", 5) +``` + +那么: + +```text +("h", "u") = 10 + 5 = 15 +("u", "g") = 10 + 5 + 5 = 20 +("p", "u") = 5 + 12 = 17 +("u", "n") = 12 + 4 = 16 +("g", "s") = 5 +``` + +所以第一条 merge rule 是: + +```text +("u", "g") -> "ug" +``` + +`merge rules` 可以理解为 BPE 训练出来的“相邻 token 合并优先级表”。BPE 使用时不是把 subword 合并成一个“目标词汇”,而是从字符、byte 或已有小 token 开始,按训练好的 merge rules 逐步合并,最终得到一串 token: + +```text +l o w e r +=> lo w e r +=> low e r +=> low er +=> lower +``` + +GPT-2 使用 BPE。放回课程里的 text generation 部分,可以理解为: + +```text +raw text + -> BPE tokenizer + -> token embeddings + position embeddings + -> decoder blocks + -> LM head + -> next-token logits +``` + +## WordPiece + +WordPiece 是 BERT 系模型常见 tokenizer。它的切词结果常用 `##` 表示“这个 token 不是词首”,例如: + +```text +"hugs" -> ["hug", "##s"] +``` + +WordPiece 推理时更像从左到右找词表中最长的合法 subword;训练时不只是看 pair 出现频率,还会考虑 pair 的相对绑定强度。课程中给出的直觉 score 是: + +```text +score = freq(pair) / (freq(first) * freq(second)) +``` + +所以高频 pair 不一定最优先合并。如果某个片段本身到处出现,它和另一个片段一起出现并不一定说明它们是稳定组合。 + +这个 score 的直觉是: + +```text +freq(pair) 是这两个片段挨在一起出现多少次。 +freq(first) 是左片段自己总共出现多少次。 +freq(second) 是右片段自己总共出现多少次。 +``` + +BPE 只问: + +```text +这两个片段挨在一起出现得多不多? +``` + +WordPiece 更像问: + +```text +这两个片段是不是更专属于彼此? +``` + +如果 `first` 和 `second` 各自都到处出现,即使 `pair` 出现次数也不少,score 也会被分母压低。 + +### WordPiece 使用时的贪心最长匹配 + +WordPiece 训练阶段可能会学习 merge,但使用阶段通常只保存最终 vocab,不保存 merge rules。使用时的核心算法是: + +```text +从当前 word 的最左边开始 +-> 找 vocab 里最长的合法 subword +-> 找到后切下来 +-> 剩余部分继续找最长合法 subword,后续片段通常带 ## 前缀 +-> 如果中途找不到合法 subword,整个 word 变成 [UNK] +``` + +例如 vocab 里有: + +```text +hug +##s +b +##u +##gs +``` + +那么: + +```text +"hugs" -> ["hug", "##s"] +"bugs" -> ["b", "##u", "##gs"] +``` + +这里确实是贪心算法,并不保证全局最优。如果 vocab 里有: + +```text +abc +ab +##cd +``` + +输入: + +```text +abcd +``` + +贪心最长匹配会先选: + +```text +abc +``` + +剩下的 `d` 如果找不到 `##d`,那么整个词会失败并变成: + +```text +["[UNK]"] +``` + +但如果当初先选 `ab`,其实可以得到: + +```text +["ab", "##cd"] +``` + +所以 WordPiece 的使用阶段不是全局搜索,也不会回溯。它的工程取舍是:训练阶段构造一个足够好的 subword vocab,使用阶段保持确定、快速、简单的最长匹配。 + +## 和当前 Text Classification 的关系 + +BERT text classification 的链路可以先记成: + +```text +raw text + -> WordPiece tokenizer + -> input_ids / attention_mask / token_type_ids + -> [CLS] final hidden state + -> classification head + -> logits +``` + +这里 tokenizer 的任务是把自然语言变成模型能查 embedding table 的 token ids。分类真正依赖的是 encoder 输出的表示,尤其是 `[CLS]` 位置的 final hidden state。 + +## 当前结论 + +- BPE:训练时按最频繁的相邻 pair 学 merge rules,常见于 GPT-2 这类生成模型 tokenizer。 +- WordPiece:BERT 系常见,使用时按 vocab 做贪心最长匹配,训练时更重视 pair 的相对绑定强度。 +- BPE / WordPiece 都会产出 subword tokens;区别不是“有没有 subword”,而是训练时如何选择新 subword,以及使用时如何把新文本切成 token。 +- 当前阶段先能解释“文本为什么会变成 subword ids、token ids 为什么还不是 embeddings,以及 tokenizer 为什么必须和 model 匹配”。 + +## 来源 + +- Hugging Face LLM Course Chapter 1/5:GPT-2 使用 BPE,BERT 使用 WordPiece。 +- Hugging Face LLM Course Chapter 6:[`Byte-Pair Encoding tokenization`](https://huggingface.co/learn/llm-course/en/chapter6/5) / [`WordPiece tokenization`](https://huggingface.co/learn/llm-course/en/chapter6/6) 机制说明。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/README.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/README.md new file mode 100644 index 0000000..95662eb --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/README.md @@ -0,0 +1,12 @@ +# Reflection + +`reflection/` 是 topic 的滚动知识候选和最终主动回顾区。 + +这里不是 Agent 总结区,也不是从 notes 自动提取知识的地方。`candidate-map.md` 从 01-09 阶段就开始滚动维护,记录学习过程中暴露出的高价值候选;用户需要在完成 topic 后,用自己的语言写出 closeout reflection;Agent 进入总结阶段后,应根据当前 topic 的具体内容重写反思提示,再进行提问、挑战、补外部参照、建议链接和一致性检查。 + +当 topic 主体学习已经完成但你还需要等周末或大块时间回顾时,topic 可以进入 `awaiting-reflection`。这是正常的 closeout pending 状态,不是失败;它不会占用日常 active learning slot,但仍然需要尽快完成。 + +## Files + +- [`candidate-map.md`](candidate-map.md):贯穿 01-09 滚动维护的候选知识表,10-reflection 只做查漏补缺、降噪和状态确认。 +- [`closeout.md`](closeout.md):用户主动回顾总结。进入 `knowledge-base/` 前必须先由用户完成它。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/candidate-map.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/candidate-map.md new file mode 100644 index 0000000..18f2e9c --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/candidate-map.md @@ -0,0 +1,22 @@ +# 知识候选表 + +> 状态只能使用:`候选中`、`总结中`、`已归档`、`已忽略`。 + +## 主题核心 + +| 候选 | 为什么值得看 | 证据 | 状态 | +| --- | --- | --- | --- | +| | | | 候选中 | + +## 底层原理 + +| 候选 | 为什么值得看 | 证据 | 状态 | +| --- | --- | --- | --- | +| | | | 候选中 | + +## 工程模式 + +| 候选 | 为什么值得看 | 证据 | 状态 | +| --- | --- | --- | --- | +| 单仓库多 Python 项目的 IDE 解析边界 | daedalus 作为总仓库会包含多个 topic/demo Python 项目;IDE 解析边界不能只靠“文件夹里有 pyproject/venv”自动推断。直接在根 `.vscode/settings.json` 里绑定某个 demo 的 interpreter,会把根 workspace 误建模为单一 Python project;但只依赖 demo 局部 `.vscode/settings.json`,在用户直接打开 daedalus folder 时又不会生效。更稳的模式是“双路径”:推荐用 multi-root `.code-workspace` 把 daedalus 根和每个 Python demo 都建成独立 workspace folder;同时保留根 `pyrightconfig.json` 作为直接打开仓库根时的静态解析兜底,显式为当前 demo 配置 execution environment 和 `.venv/site-packages`。 | 本 topic 的 `hugging-face-course-learning` demo 中 `.venv` 已安装 `transformers`。第一次直接打开 daedalus 根时,Pylance/BasedPyright 报 `reportMissingImports`;将 Python interpreter 从根 `.vscode/settings.json` 移除后避免了根 workspace 被某个 demo 绑死,但直接打开 daedalus folder 时 demo 局部 `.vscode/settings.json` 不生效,Rust 根配置也需要保留。最终配置分工为:根 `.vscode/settings.json` 只保留 Rust fallback;`daedalus.code-workspace` 作为 multi-root 入口;demo 局部 `.vscode/settings.json` 指向自己的 `.venv`;根 `pyrightconfig.json` 为直接打开根目录时显式添加 demo root 和 `.venv/lib/python3.11/site-packages`。 | 候选中 | +| 动态库 API alias 与静态类型 overload 的差异 | Hugging Face Transformers 这类动态 Python 库运行时会支持 alias 和宽松参数,但类型检查器读取的是 overload / Literal 签名;学习 demo 如果照抄 runtime alias,可能出现“运行成功但编辑器报错”。处理原则不是盲目关闭类型检查,而是优先使用 canonical API 名称,让教学代码同时满足运行时和静态分析。 | `transformers.pipeline("sentiment-analysis")` 能运行成功,因为 `sentiment-analysis` 是 `text-classification` 的 runtime alias;但 Transformers 5.12.0 的 overload 列表只包含 `Literal["text-classification"]`,因此 BasedPyright 报 `reportCallIssue`。将 demo 改为 `pipeline("text-classification")` 后仍输出相同 POSITIVE 结果,并更贴合类型签名。 | 候选中 | diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/closeout.md b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/closeout.md new file mode 100644 index 0000000..022db7b --- /dev/null +++ b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/reflection/closeout.md @@ -0,0 +1,47 @@ +# Closeout Reflection + +> 这是 daedalus 留给你的 topic 收尾回顾。它不是固定问卷,也不是 daedalus 的总结区;它可以粗糙、不完整、有困惑,但一定要保留你自己的判断、变化和疑问。 + +当你准备结束一个 topic 时,先别急着让 daedalus 归档知识。请先在这里写下你真实想明白的东西:这次学习到底解决了什么问题,你的理解发生了什么变化,哪些判断有证据,哪些地方还只是模仿或猜测。 + +整个学习过程中,daedalus 会滚动维护一份知识候选表。进入 closeout 时,你不需要照单全收;你要用 closeout 判断哪些真的成为了你的理解,哪些只是路过,哪些应该忽略。 + +> 完整 topic 的回顾需要高度专注的注意力,你要把几周的源码、demo、争论、失败、设计取舍,压缩成自己的理解。所以推荐你在周末或者有大块连续时间段的时候专注做这件事哦 😄 + +如果这个 topic 已经进入 `awaiting-reflection`,说明主体学习已经完成,daedalus 只是把它从日常推进队列里移出来,等你有足够注意力时再认真收尾。它不是失败,也不是让你无限拖延;它是一张需要被看见、被安排、被完成的 closeout 小债条。 + +你不需要套用固定目录结构。不同 topic 的 closeout reflection 可以有不同标题、不同顺序、不同重点。进入 `10-reflection` 时,daedalus 会先阅读当前 topic 的目标、notes、demo、业务迁移、运行证据和遗留问题,然后在对话里提出贴合当前 topic 的回顾问题。 + +可以大致从这些方向开始想: + +- 你最初想解决的真实问题是什么。 +- 你从一开始的学习目标中真正学到了什么。 +- 这次学习让你改变了哪些看法。 +- 你现在能独立做出什么判断或设计。 +- demo 实现过程中做了哪些关键决策、trade-off 和简化。 +- demo 的核心架构图是什么;如果图还没想清楚,先手绘、人工调整,再让 daedalus 帮你落盘。 +- 学习和实现 demo 的过程中,暴露了哪些基础知识薄弱点,比如操作系统、网络、数据库、编程语言、编译原理、运行时、并发或安全模型。 +- 哪些结论有源码、实验、demo、业务迁移或外部资料支撑。 +- 哪些地方只是为了学习而忠实模仿,不能直接迁移。 +- 哪些经验可以举一反三。 +- 哪些地方仍然模糊、脆弱或需要复习。 +- 下一次遇到相似问题时,你会怎么判断。 +- 你希望 daedalus 重点 challenge 哪些地方。 + +如果你需要画图,不要试图用一张图讲完所有细节。图优先画清楚分层和主方向;关键决策点、异常分支、取舍原因,放在图后的文字里解释。daedalus 会帮你检查图和文字是否互相支撑,而不是让图变成不可读的全量流程。 + +closeout 不要求你把所有底层原理都讲透,但要把重要原理缺口和可迁移能力点出来。后续进入 `knowledge-base` 时,daedalus 会从 guides、notes、demo、测试和外部资料里查漏补缺、去重和降噪。 + +你负责写正文;daedalus 会负责提问、challenge、搜索外部最佳实践、补链接、检查一致性和组织结构。没有你的主动回顾,这个 topic 就不能进入 `knowledge-base/`。 + +你写完并确认后,daedalus 应该主动推进: + +```text +reflection done +-> challenge resolved +-> knowledge-base gate +-> archive confirmed +-> topic completed +``` + +如果某一步已经在对话中完成,daedalus 应该承认完成、同步地图并进入下一步,而不是让你重复提醒。 diff --git a/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/review/.gitkeep b/workspaces/projects/hugging-face-llm-course/topics/lora-feedback-loop/review/.gitkeep new file mode 100644 index 0000000..e69de29