Deploy Token Holdem final validation Space
Browse filesThis view is limited to 50 files because it contains too many changes. Β See raw diff
- .agents/skills/art-asset-generator/SKILL.md +111 -0
- .agents/skills/art-asset-generator/agents/openai.yaml +4 -0
- .agents/skills/cozy-game-polish-qa/SKILL.md +70 -0
- .agents/skills/cozy-game-polish-qa/agents/openai.yaml +4 -0
- .agents/skills/gradio-game-scene-composer/SKILL.md +155 -0
- .agents/skills/gradio-game-scene-composer/agents/openai.yaml +4 -0
- .agents/skills/gradio/SKILL.md +84 -0
- .agents/skills/gradio/references/api-signatures.md +115 -0
- .agents/skills/gradio/references/event-listeners.md +132 -0
- .agents/skills/gradio/references/examples.md +613 -0
- .agents/skills/hf-cli/.hf-skill-manifest.json +4 -0
- .agents/skills/hf-cli/SKILL.md +218 -0
- .agents/skills/hf-gradio/SKILL.md +83 -0
- .agents/skills/hf-mem/SKILL.md +79 -0
- .agents/skills/huggingface-best/SKILL.md +134 -0
- .agents/skills/huggingface-community-evals/SKILL.md +207 -0
- .agents/skills/huggingface-community-evals/examples/.env.example +3 -0
- .agents/skills/huggingface-community-evals/examples/USAGE_EXAMPLES.md +101 -0
- .agents/skills/huggingface-community-evals/scripts/inspect_eval_uv.py +104 -0
- .agents/skills/huggingface-community-evals/scripts/inspect_vllm_uv.py +306 -0
- .agents/skills/huggingface-community-evals/scripts/lighteval_vllm_uv.py +297 -0
- .agents/skills/huggingface-datasets/.hf-skill-manifest.json +4 -0
- .agents/skills/huggingface-datasets/SKILL.md +107 -0
- .agents/skills/huggingface-gradio/SKILL.md +298 -0
- .agents/skills/huggingface-gradio/examples.md +613 -0
- .agents/skills/huggingface-llm-trainer/.hf-skill-manifest.json +4 -0
- .agents/skills/huggingface-llm-trainer/SKILL.md +738 -0
- .agents/skills/huggingface-llm-trainer/references/gguf_conversion.md +296 -0
- .agents/skills/huggingface-llm-trainer/references/hardware_guide.md +283 -0
- .agents/skills/huggingface-llm-trainer/references/hub_saving.md +364 -0
- .agents/skills/huggingface-llm-trainer/references/local_training_macos.md +231 -0
- .agents/skills/huggingface-llm-trainer/references/reliability_principles.md +371 -0
- .agents/skills/huggingface-llm-trainer/references/trackio_guide.md +189 -0
- .agents/skills/huggingface-llm-trainer/references/training_methods.md +150 -0
- .agents/skills/huggingface-llm-trainer/references/training_patterns.md +203 -0
- .agents/skills/huggingface-llm-trainer/references/troubleshooting.md +282 -0
- .agents/skills/huggingface-llm-trainer/references/unsloth.md +313 -0
- .agents/skills/huggingface-llm-trainer/scripts/convert_to_gguf.py +424 -0
- .agents/skills/huggingface-llm-trainer/scripts/dataset_inspector.py +417 -0
- .agents/skills/huggingface-llm-trainer/scripts/estimate_cost.py +150 -0
- .agents/skills/huggingface-llm-trainer/scripts/hf_benchmarks.py +659 -0
- .agents/skills/huggingface-llm-trainer/scripts/train_dpo_example.py +106 -0
- .agents/skills/huggingface-llm-trainer/scripts/train_grpo_example.py +89 -0
- .agents/skills/huggingface-llm-trainer/scripts/train_sft_example.py +122 -0
- .agents/skills/huggingface-llm-trainer/scripts/unsloth_sft_example.py +512 -0
- .agents/skills/huggingface-local-models/SKILL.md +113 -0
- .agents/skills/huggingface-local-models/references/hardware.md +38 -0
- .agents/skills/huggingface-local-models/references/hub-discovery.md +178 -0
- .agents/skills/huggingface-local-models/references/quantization.md +256 -0
- .agents/skills/huggingface-lora-space-builder/SKILL.md +391 -0
.agents/skills/art-asset-generator/SKILL.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: art-asset-generator
|
| 3 |
+
description: Generate final visual image assets for Token Hold'em and save them as real repo files under assets/. Use when Codex is asked to generate actual image files, create tavern background art, create LLM avatar portraits, create poker table assets, create transparent PNG/WebP sprites, use image generation to create assets, save generated assets into the repo, update the asset manifest, integrate generated art through CSS/HTML/token_holdem/render.py, or replace CSS placeholders with real art assets. This skill must produce actual generated files when image generation is available, not just prompts.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Art Asset Generator
|
| 7 |
+
|
| 8 |
+
## Overview
|
| 9 |
+
|
| 10 |
+
Generate, save, optimize, manifest, integrate, and QA production image assets for Token Hold'em. Prefer real repo artifacts over prompt-only output; use prompt-only fallbacks only when Codex image generation is unavailable in the current environment.
|
| 11 |
+
|
| 12 |
+
## Coordination
|
| 13 |
+
|
| 14 |
+
Load companion skills when their responsibility applies:
|
| 15 |
+
|
| 16 |
+
- Use `pixel-tavern-art-director` for visual direction, palette, lighting, composition, negative prompts, and QA criteria.
|
| 17 |
+
- Use `image-asset-pipeline` for manifest shape, naming, asset slots, dimensions, optimization, and UI integration rules.
|
| 18 |
+
- Use `llm-avatar-personification` for LLM avatar concepts that avoid exact company logos, mascots, or wordmarks.
|
| 19 |
+
- Use `poker-table-ui-layout` for seat/table/community-card constraints before making table, overlay, chip, or card assets.
|
| 20 |
+
- Use `gradio-game-scene-composer` when integrating assets into full-page Gradio scene structure or CSS layering.
|
| 21 |
+
|
| 22 |
+
Always inspect current repo state before generating:
|
| 23 |
+
|
| 24 |
+
- `assets/token-holdem/manifest.json`
|
| 25 |
+
- `token_holdem/render.py`
|
| 26 |
+
- `app.py`
|
| 27 |
+
- Any existing `docs/token-holdem-*` prompt, persona, or visual direction files
|
| 28 |
+
- Existing generated assets under `assets/`
|
| 29 |
+
|
| 30 |
+
## Workflow
|
| 31 |
+
|
| 32 |
+
1. Read visual direction, layout constraints, and the asset manifest.
|
| 33 |
+
2. Identify the exact asset slot to generate, including manifest `id`, target use site, and displayed CSS size.
|
| 34 |
+
3. Decide dimensions, format, alpha, filename, and destination path before generation.
|
| 35 |
+
4. Use Codex's available image generation capability to create the actual image file.
|
| 36 |
+
5. Save the generated file under the repo's `assets/` tree, normally `assets/token-holdem/`.
|
| 37 |
+
6. Inspect the generated asset visually. Check art direction, transparency, crop safety, readability, and absence of baked UI text or logos.
|
| 38 |
+
7. Regenerate or refine poor assets instead of accepting unusable output.
|
| 39 |
+
8. Optimize or convert when needed for Hugging Face Spaces. Prefer PNG for alpha-critical art and WebP for full-scene/background assets when quality remains acceptable.
|
| 40 |
+
9. Update `assets/token-holdem/manifest.json` with filename, path, dimensions, format, purpose/role, where it is used, source, prompt summary, and QA notes.
|
| 41 |
+
10. Wire the asset into the Gradio app through CSS, HTML, or helpers in `token_holdem/render.py` where appropriate.
|
| 42 |
+
11. Run checks that prove the app still imports and loads. Prefer `uv run pytest`; for UI-impacting changes, also run `uv run python app.py` and inspect the Gradio URL.
|
| 43 |
+
|
| 44 |
+
## Asset Decisions
|
| 45 |
+
|
| 46 |
+
Use these defaults unless the manifest or layout requires something else:
|
| 47 |
+
|
| 48 |
+
- Tavern backgrounds: `1920x1080` or `2560x1440`, no alpha, full-scene composition, dark quiet center for table/cards/seats.
|
| 49 |
+
- Poker table art: `1536x864` or `2048x1152`, transparent PNG when layered over tavern art, no baked cards, chips, labels, or pot text.
|
| 50 |
+
- LLM avatars: `512x512` transparent PNG or WebP with alpha when supported, consistent camera angle and tavern rim light, no exact company marks.
|
| 51 |
+
- Sprites, chips, candles, props, overlays, seat frames: transparent PNG with tight but safe padding.
|
| 52 |
+
- Card backs: high-resolution rectangular PNG/WebP matching the current CSS card proportions, no ranks, suits, brands, watermarks, or readable words.
|
| 53 |
+
|
| 54 |
+
Use kebab-case filenames with dimensions, for example `tavern-bg-main-1920x1080.webp`, `table-felt-top-1536x864.png`, `avatar-gpt-architect-neutral-512.png`, or `prop-candle-lit-256x512.png`.
|
| 55 |
+
|
| 56 |
+
## Image Generation Requirements
|
| 57 |
+
|
| 58 |
+
The expected output is actual generated image files committed to the repo workspace. Do not stop at writing prompts when image generation is available.
|
| 59 |
+
|
| 60 |
+
When calling image generation, include:
|
| 61 |
+
|
| 62 |
+
- Token Hold'em as the project context.
|
| 63 |
+
- Exact canvas dimensions and aspect ratio.
|
| 64 |
+
- Pixel-art cozy tavern poker style, warm candle/hearth lighting, premium game UI readability.
|
| 65 |
+
- Required alpha/transparent background when appropriate.
|
| 66 |
+
- Composition safe areas and any live UI that must remain readable.
|
| 67 |
+
- Negative constraints: no text unless explicitly required, no logos, no exact company mascots, no watermarks, no baked cards/chips/pot/player labels unless the asset is specifically that object.
|
| 68 |
+
|
| 69 |
+
If image generation is unavailable, explicitly state that limitation and provide fallback output only:
|
| 70 |
+
|
| 71 |
+
- Final generation prompts.
|
| 72 |
+
- Planned filenames and destination paths.
|
| 73 |
+
- Manifest entries marked as pending generation.
|
| 74 |
+
- Integration notes marked blocked until assets exist.
|
| 75 |
+
|
| 76 |
+
## Manifest Update
|
| 77 |
+
|
| 78 |
+
Preserve the existing manifest schema. For generated assets, ensure each relevant entry has:
|
| 79 |
+
|
| 80 |
+
- `file`: repo-root-relative or app-servable path used by the code.
|
| 81 |
+
- `dimensions`: actual pixel dimensions.
|
| 82 |
+
- `format`: actual file format.
|
| 83 |
+
- `alpha`: whether the final file has transparency.
|
| 84 |
+
- `source`: mark as generated, including the generation tool/model if known.
|
| 85 |
+
- `purpose` or `role`: what the file is for.
|
| 86 |
+
- `usedBy` or equivalent notes: CSS selector, HTML/render helper, or file where it is wired.
|
| 87 |
+
- `prompt` or concise prompt summary.
|
| 88 |
+
- `qa`: brief checks performed and any known limitations.
|
| 89 |
+
|
| 90 |
+
If the existing manifest entry is a placeholder, update it in place rather than creating a duplicate ID. Keep IDs stable.
|
| 91 |
+
|
| 92 |
+
## Integration Rules
|
| 93 |
+
|
| 94 |
+
Wire assets only where they improve the actual game UI:
|
| 95 |
+
|
| 96 |
+
- Use CSS background layers for tavern scene art and table surfaces.
|
| 97 |
+
- Use `<img>` or render helper output for semantic or positioned assets only when needed.
|
| 98 |
+
- Keep live gameplay information rendered by code: cards, pot, player names, stacks, action state, buttons, timers, and status labels.
|
| 99 |
+
- Set explicit dimensions, aspect ratios, `object-fit`, `object-position`, and `pointer-events` behavior.
|
| 100 |
+
- Preserve the current 8-seat layout: human seat bottom-center, seats 1-7 around the table, community cards in the fixed center zone, action buttons outside table art.
|
| 101 |
+
- Avoid external asset URLs. All final assets must live under the repo.
|
| 102 |
+
|
| 103 |
+
## QA Output
|
| 104 |
+
|
| 105 |
+
End asset-generation tasks with a concise report:
|
| 106 |
+
|
| 107 |
+
- Files generated and saved.
|
| 108 |
+
- Manifest entries changed.
|
| 109 |
+
- App integration locations changed, such as CSS selectors or `token_holdem/render.py` helpers.
|
| 110 |
+
- Visual QA notes: match to art direction, readability, transparency, crop safety, no prohibited logos/text.
|
| 111 |
+
- Checks run and results.
|
.agents/skills/art-asset-generator/agents/openai.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interface:
|
| 2 |
+
display_name: "Art Asset Generator"
|
| 3 |
+
short_description: "Generate final Token Hold'em art assets"
|
| 4 |
+
default_prompt: "Use $art-asset-generator to generate and integrate a final Token Hold'em image asset."
|
.agents/skills/cozy-game-polish-qa/SKILL.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: cozy-game-polish-qa
|
| 3 |
+
description: Review and improve Token Hold'em final UI polish, UX quality, game feel, animations, hover/focus states, readability, accessibility, responsive behavior, and premium cozy tavern presentation. Use when Codex is asked for UI polish, UX review, game feel, animations, hover states, readability, accessibility, final quality pass, polish punch list, prioritized improvements, mobile checks, or a premium feel review for the Token Hold'em app.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Cozy Game Polish QA
|
| 7 |
+
|
| 8 |
+
## Purpose
|
| 9 |
+
|
| 10 |
+
Use this skill to make Token Hold'em feel like a polished cozy tavern poker game rather than a functional demo. Focus on clarity, charm, responsiveness, readable game state, accessible interactions, and satisfying feedback.
|
| 11 |
+
|
| 12 |
+
## Workflow
|
| 13 |
+
|
| 14 |
+
1. Inspect the running app or relevant UI code before making recommendations. If feasible, run the app and capture desktop and mobile views.
|
| 15 |
+
2. Review the experience as a player, not only as an implementer: evaluate whether the current turn, pot, stacks, bet size, player status, available actions, and result state are immediately legible.
|
| 16 |
+
3. Produce a prioritized polish punch list before editing when the request is review-only. If the user asks to improve the app, implement the highest-impact items directly.
|
| 17 |
+
4. Keep changes coherent with a cozy tavern poker fantasy: warm, tactile, readable, playful, and calm under repeated play.
|
| 18 |
+
5. Verify the result after changes with screenshots or interaction checks when a browser is available.
|
| 19 |
+
|
| 20 |
+
## Review Checklist
|
| 21 |
+
|
| 22 |
+
### Readability and Game State
|
| 23 |
+
|
| 24 |
+
- Ensure the active player, dealer/button, blinds, folded/all-in/sitting-out states, pot, current bet, call amount, and winner/result messages are visible without hunting.
|
| 25 |
+
- Prefer concise table labels over verbose explanatory text.
|
| 26 |
+
- Make numeric values scannable with consistent formatting, spacing, and alignment.
|
| 27 |
+
- Check that cards, chips, player names, and action controls remain readable against textured or illustrated backgrounds.
|
| 28 |
+
- Avoid hiding essential state inside hover-only UI.
|
| 29 |
+
|
| 30 |
+
### Interaction Feel
|
| 31 |
+
|
| 32 |
+
- Add clear hover, active, disabled, selected, focus-visible, and loading states for buttons, cards, seats, and controls.
|
| 33 |
+
- Make primary actions feel deliberate: call/check/bet/raise/fold should have distinct weight and clear affordance.
|
| 34 |
+
- Use transitions for state changes that benefit comprehension, such as chips moving to the pot, active seat changes, revealed cards, winner highlight, and hand reset.
|
| 35 |
+
- Keep animation durations short enough for repeated play, usually 120-300ms for UI feedback and 300-700ms for celebratory or spatial motion.
|
| 36 |
+
- Respect `prefers-reduced-motion`; provide non-motion emphasis such as color, outline, glow, or opacity changes.
|
| 37 |
+
|
| 38 |
+
### Cozy Tavern Premium Feel
|
| 39 |
+
|
| 40 |
+
- Look for tactile material cues: felt, wood, brass, parchment, candlelight, soft shadows, bevels, chip depth, and warm highlights.
|
| 41 |
+
- Use charm in small doses: subtle ambient details, satisfying chip/card motion, clear win celebration, and personable status copy.
|
| 42 |
+
- Avoid clutter, novelty fonts for critical text, overdone glow, muddy contrast, and single-hue palettes.
|
| 43 |
+
- Make the first screen feel like the game is already alive: table centered, players seated, core controls visible, and no empty demo scaffolding.
|
| 44 |
+
|
| 45 |
+
### Accessibility
|
| 46 |
+
|
| 47 |
+
- Verify text contrast for critical controls and state labels.
|
| 48 |
+
- Ensure all interactive elements are keyboard reachable with visible focus.
|
| 49 |
+
- Use semantic buttons for actions and avoid click-only divs.
|
| 50 |
+
- Ensure disabled controls communicate why an action is unavailable through nearby state or labels.
|
| 51 |
+
- Confirm target sizes are comfortable on touch screens, roughly 44px minimum for primary controls.
|
| 52 |
+
|
| 53 |
+
### Mobile and Responsive
|
| 54 |
+
|
| 55 |
+
- Check narrow screens for overlapping cards, clipped buttons, cramped labels, and unreadable chip counts.
|
| 56 |
+
- Prioritize the current player hand, available actions, pot/current bet, and active opponent status on mobile.
|
| 57 |
+
- Let decorative tavern elements yield space before game-critical information does.
|
| 58 |
+
- Use stable dimensions for cards, chips, seats, toolbars, and controls so hover states and changing labels do not shift the layout.
|
| 59 |
+
|
| 60 |
+
## Output Format
|
| 61 |
+
|
| 62 |
+
When reviewing, return:
|
| 63 |
+
|
| 64 |
+
- `Polish punch list`: grouped by visual polish, game-state clarity, interaction feel, accessibility, and responsive behavior.
|
| 65 |
+
- `Prioritized improvements`: order by player impact and implementation risk, with `P0`, `P1`, or `P2`.
|
| 66 |
+
- `Animation recommendations`: include what should move, why it helps, suggested duration/easing, and reduced-motion fallback.
|
| 67 |
+
- `Mobile/responsive checks`: list specific viewports or layout states to test.
|
| 68 |
+
- `Premium feel review`: summarize whether the current build feels like a cozy tavern poker game and name the biggest missing ingredient.
|
| 69 |
+
|
| 70 |
+
When editing code, include a concise final summary of changed files and verification performed.
|
.agents/skills/cozy-game-polish-qa/agents/openai.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interface:
|
| 2 |
+
display_name: "Cozy Game Polish QA"
|
| 3 |
+
short_description: "Review cozy poker UI polish and game feel"
|
| 4 |
+
default_prompt: "Use $cozy-game-polish-qa to run a final polish pass on Token Hold'em."
|
.agents/skills/gradio-game-scene-composer/SKILL.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: gradio-game-scene-composer
|
| 3 |
+
description: Turn Gradio apps into full-page, immersive, game-like scenes using custom HTML, CSS, and layered layout architecture. Use when Codex is asked for full-page game UI, immersive Gradio layout, replacing generic Gradio panels, background scene composition, HUD/sidebar/action bar layout, CSS layering, z-index strategy, responsive game scene design, or Token Hold'em tavern poker scene composition.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Gradio Game Scene Composer
|
| 7 |
+
|
| 8 |
+
## Purpose
|
| 9 |
+
|
| 10 |
+
Use this skill to make a Gradio app feel like an integrated game screen rather than a demo. For Token Hold'em, the tavern environment should be the full-page background, with the poker table, avatars, HUD panels, speech bubbles, sidebars, and action buttons layered above it.
|
| 11 |
+
|
| 12 |
+
Preserve gameplay behavior. Do not change poker engine logic, state transitions, or callback semantics unless the user explicitly asks. Prefer edits to render helpers, CSS, HTML templates, layout wrappers, and component placement.
|
| 13 |
+
|
| 14 |
+
## First Pass
|
| 15 |
+
|
| 16 |
+
1. Inspect `app.py` and rendering/layout helpers before editing. In Token Hold'em, start with `token_holdem/render.py` for HTML/CSS table rendering and only touch `token_holdem/engine.py` if a rendering bug proves engine-adjacent.
|
| 17 |
+
2. Identify which Gradio components are functional controls or outputs, then decide whether each belongs in the game scene, a HUD layer, a side drawer, an action bar, or a hidden plumbing role.
|
| 18 |
+
3. Keep Gradio event wiring intact. Move or restyle components through `elem_id`, `elem_classes`, layout containers, `gr.HTML`, and CSS rather than rewriting callbacks.
|
| 19 |
+
4. Produce or follow the scene plan before large edits: scene layout architecture, DOM layer plan, CSS strategy, breakpoints, component integration, and checklist.
|
| 20 |
+
|
| 21 |
+
## Scene Architecture
|
| 22 |
+
|
| 23 |
+
Use a root scene shell that owns the viewport and isolates game visuals from generic Gradio framing:
|
| 24 |
+
|
| 25 |
+
```html
|
| 26 |
+
<div class="game-scene">
|
| 27 |
+
<div class="scene-bg"></div>
|
| 28 |
+
<main class="scene-stage">
|
| 29 |
+
<section class="table-layer">...</section>
|
| 30 |
+
<section class="avatar-layer">...</section>
|
| 31 |
+
<section class="speech-layer">...</section>
|
| 32 |
+
</main>
|
| 33 |
+
<aside class="hud hud-left">...</aside>
|
| 34 |
+
<aside class="hud hud-right">...</aside>
|
| 35 |
+
<footer class="action-bar">...</footer>
|
| 36 |
+
</div>
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
Map the app's actual DOM to this model. If Gradio must render controls outside the custom HTML block, style its rows/columns as scene overlays with stable IDs/classes.
|
| 40 |
+
|
| 41 |
+
Recommended layer order:
|
| 42 |
+
|
| 43 |
+
1. `scene-bg`: tavern environment, full viewport, non-interactive.
|
| 44 |
+
2. `scene-lighting`: optional vignette/candlelight/material overlays, non-interactive.
|
| 45 |
+
3. `scene-stage`: poker table and player positions.
|
| 46 |
+
4. `table-felt` and card/chip objects.
|
| 47 |
+
5. `avatar-layer`: player portraits and status badges.
|
| 48 |
+
6. `speech-layer`: bubbles, model thoughts, current action callouts.
|
| 49 |
+
7. `hud` panels: hand status, logs, settings, history, model info.
|
| 50 |
+
8. `action-bar`: player controls, bet slider/input, primary actions.
|
| 51 |
+
9. `modal/toast` layer: blocking dialogs and transient alerts.
|
| 52 |
+
|
| 53 |
+
Use explicit z-index tokens instead of scattered numbers:
|
| 54 |
+
|
| 55 |
+
```css
|
| 56 |
+
:root {
|
| 57 |
+
--z-bg: 0;
|
| 58 |
+
--z-stage: 10;
|
| 59 |
+
--z-table: 20;
|
| 60 |
+
--z-avatars: 30;
|
| 61 |
+
--z-speech: 40;
|
| 62 |
+
--z-hud: 50;
|
| 63 |
+
--z-actions: 60;
|
| 64 |
+
--z-modal: 100;
|
| 65 |
+
}
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
## CSS Strategy
|
| 69 |
+
|
| 70 |
+
- Make the app root fill the viewport: set `min-height: 100dvh` on the Gradio container and the scene shell.
|
| 71 |
+
- Hide or neutralize generic demo framing only inside the app scope. Avoid global CSS that breaks Gradio internals, dropdowns, modals, or accessibility.
|
| 72 |
+
- Use CSS Grid for page-level composition: background, stage, left/right HUD, bottom action bar.
|
| 73 |
+
- Use absolute positioning only inside stable scene containers such as the poker table or avatar layer.
|
| 74 |
+
- Use Flexbox for compact HUD panel content, button groups, chip rows, and speech bubble internals.
|
| 75 |
+
- Use stable dimensions with `clamp()`, `aspect-ratio`, `minmax()`, and fixed card/control slots so changing labels do not shift the scene.
|
| 76 |
+
- Keep decorative backgrounds behind readable UI. Add scrims, shadows, or panel surfaces where text crosses detailed art.
|
| 77 |
+
- Prefer class and ID hooks on Gradio components. Avoid brittle selectors based on generated DOM depth unless no better hook exists.
|
| 78 |
+
|
| 79 |
+
Example page grid:
|
| 80 |
+
|
| 81 |
+
```css
|
| 82 |
+
.game-scene {
|
| 83 |
+
min-height: 100dvh;
|
| 84 |
+
display: grid;
|
| 85 |
+
grid-template-columns: minmax(220px, 18vw) 1fr minmax(260px, 22vw);
|
| 86 |
+
grid-template-rows: minmax(0, 1fr) auto;
|
| 87 |
+
grid-template-areas:
|
| 88 |
+
"left stage right"
|
| 89 |
+
"actions actions actions";
|
| 90 |
+
position: relative;
|
| 91 |
+
overflow: hidden;
|
| 92 |
+
}
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
## Responsive Breakpoints
|
| 96 |
+
|
| 97 |
+
Plan at least three states:
|
| 98 |
+
|
| 99 |
+
- Desktop wide: tavern fills the page, table centered, side HUDs visible, action bar fixed along the bottom.
|
| 100 |
+
- Tablet or narrow desktop: collapse one sidebar below or into a drawer; keep the table and action bar dominant.
|
| 101 |
+
- Mobile portrait: prioritize human hand, pot/current turn, action controls, and active opponent. Move logs/history into tabs, accordions, or a drawer.
|
| 102 |
+
|
| 103 |
+
Use responsive rules that preserve game legibility:
|
| 104 |
+
|
| 105 |
+
- Keep primary action controls at comfortable touch size, around 44px minimum.
|
| 106 |
+
- Reduce decorative tavern details before shrinking cards, chip counts, or player state below readability.
|
| 107 |
+
- Check long model names, large stack values, verbose action text, and disabled button labels.
|
| 108 |
+
- Avoid `100vh` alone on mobile; prefer `100dvh` with fallbacks if needed.
|
| 109 |
+
|
| 110 |
+
## Gradio Integration
|
| 111 |
+
|
| 112 |
+
- Use `gr.Blocks(css=...)` or a linked CSS file for the scene system; keep large CSS in helpers or constants if the repo already does.
|
| 113 |
+
- Assign `elem_id` or `elem_classes` to Gradio components that need scene placement.
|
| 114 |
+
- Use `gr.HTML` for complex layered visuals that are output-only, such as the tavern/table scene.
|
| 115 |
+
- Keep interactive controls as real Gradio components when they drive callbacks. Restyle and reposition them instead of replacing them with inert HTML.
|
| 116 |
+
- If custom HTML buttons are unavoidable, bridge them carefully without breaking existing event wiring; otherwise avoid this path.
|
| 117 |
+
- Keep hidden state components and callback dependencies present even if visually hidden. Use accessible hiding patterns where appropriate.
|
| 118 |
+
- Verify Gradio overlays, dropdown menus, and disabled states still render above the scene and remain clickable.
|
| 119 |
+
|
| 120 |
+
## Token Hold'em Constraints
|
| 121 |
+
|
| 122 |
+
- Do not change deterministic poker rules in `token_holdem/engine.py` for visual scene work.
|
| 123 |
+
- Keep exactly 8 seats unless the user explicitly requests a gameplay change.
|
| 124 |
+
- Keep seat 0 as the human bottom-center and seats 1-7 as LLM opponents around the table.
|
| 125 |
+
- Keep community cards in the fixed center table zone.
|
| 126 |
+
- Keep action buttons outside the table art as Gradio controls, visually integrated into the action bar.
|
| 127 |
+
- Preserve callback inputs/outputs in `app.py`; if moving components, keep their variables and event bindings intact.
|
| 128 |
+
|
| 129 |
+
## Implementation Checklist
|
| 130 |
+
|
| 131 |
+
When using this skill, produce or execute this checklist:
|
| 132 |
+
|
| 133 |
+
1. Define the full-screen scene shell and decide which existing Gradio blocks map to each scene layer.
|
| 134 |
+
2. Add stable DOM hooks: IDs/classes for scene root, stage, table, sidebars, HUD panels, logs, speech bubbles, and actions.
|
| 135 |
+
3. Establish z-index tokens and ensure non-interactive visual layers use `pointer-events: none`.
|
| 136 |
+
4. Build the CSS grid/flex structure for desktop, tablet, and mobile breakpoints.
|
| 137 |
+
5. Integrate the poker table and avatars into the scene stage without changing game state logic.
|
| 138 |
+
6. Place Gradio controls into the HUD/action layer while preserving event wiring.
|
| 139 |
+
7. Add readable surfaces, scrims, and contrast rules over the tavern background.
|
| 140 |
+
8. Check overflow, clipping, focus states, disabled states, and dropdown/modal stacking.
|
| 141 |
+
9. Run the app and inspect desktop plus mobile viewport screenshots when feasible.
|
| 142 |
+
10. Run the relevant tests, usually `uv run pytest` for Token Hold'em.
|
| 143 |
+
|
| 144 |
+
## Expected Output
|
| 145 |
+
|
| 146 |
+
For planning requests, return:
|
| 147 |
+
|
| 148 |
+
- `Scene layout architecture`: root shell, regions, and responsibilities.
|
| 149 |
+
- `DOM layer plan`: ordered layer list with z-index and interaction behavior.
|
| 150 |
+
- `CSS grid/flex strategy`: desktop structure and responsive adjustments.
|
| 151 |
+
- `Responsive breakpoints`: concrete viewport behaviors and priorities.
|
| 152 |
+
- `Gradio component integration plan`: which components become scene HTML, HUD, sidebars, action bar, or hidden state.
|
| 153 |
+
- `Implementation checklist`: ordered, testable steps.
|
| 154 |
+
|
| 155 |
+
For implementation requests, make scoped code changes, then summarize changed files and verification performed.
|
.agents/skills/gradio-game-scene-composer/agents/openai.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interface:
|
| 2 |
+
display_name: "Gradio Game Scene Composer"
|
| 3 |
+
short_description: "Compose immersive Gradio game scenes"
|
| 4 |
+
default_prompt: "Use $gradio-game-scene-composer to turn this Gradio app into a full-screen layered game scene."
|
.agents/skills/gradio/SKILL.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: gradio
|
| 3 |
+
description: Build Gradio web UIs and demos in Python. Use when creating, modifying, debugging, or answering questions about Gradio and its capabilties, components, event listeners, or layouts.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Gradio
|
| 7 |
+
|
| 8 |
+
Gradio is a Python library for building interactive web UIs and ML demos. This skill covers the core API, patterns, and examples.
|
| 9 |
+
|
| 10 |
+
## References
|
| 11 |
+
- `references/examples.md` - Illustrative examples showcasing the core Gradio API.
|
| 12 |
+
- `references/api-signatures.md` - API signatures of commonly used components.
|
| 13 |
+
- `references/event-listeners.md` - API signatures of events supported by each component.
|
| 14 |
+
|
| 15 |
+
## Guides
|
| 16 |
+
|
| 17 |
+
Detailed guides on specific topics (read these when relevant):
|
| 18 |
+
|
| 19 |
+
- [Quickstart](https://www.gradio.app/guides/quickstart)
|
| 20 |
+
- [The Interface Class](https://www.gradio.app/guides/the-interface-class)
|
| 21 |
+
- [Blocks and Event Listeners](https://www.gradio.app/guides/blocks-and-event-listeners)
|
| 22 |
+
- [Controlling Layout](https://www.gradio.app/guides/controlling-layout)
|
| 23 |
+
- [More Blocks Features](https://www.gradio.app/guides/more-blocks-features)
|
| 24 |
+
- [Custom CSS and JS](https://www.gradio.app/guides/custom-CSS-and-JS)
|
| 25 |
+
- [Streaming Outputs](https://www.gradio.app/guides/streaming-outputs)
|
| 26 |
+
- [Streaming Inputs](https://www.gradio.app/guides/streaming-inputs)
|
| 27 |
+
- [Sharing Your App](https://www.gradio.app/guides/sharing-your-app)
|
| 28 |
+
- [Custom HTML Components](https://www.gradio.app/guides/custom-HTML-components)
|
| 29 |
+
- [Getting Started with the Python Client](https://www.gradio.app/guides/getting-started-with-the-python-client)
|
| 30 |
+
- [Getting Started with the JS Client](https://www.gradio.app/guides/getting-started-with-the-js-client)
|
| 31 |
+
|
| 32 |
+
## Core Patterns
|
| 33 |
+
|
| 34 |
+
**Interface** (high-level): wraps a function with input/output components.
|
| 35 |
+
|
| 36 |
+
```python
|
| 37 |
+
import gradio as gr
|
| 38 |
+
|
| 39 |
+
def greet(name):
|
| 40 |
+
return f"Hello {name}!"
|
| 41 |
+
|
| 42 |
+
gr.Interface(fn=greet, inputs="text", outputs="text").launch()
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
**Blocks** (low-level): flexible layout with explicit event wiring.
|
| 46 |
+
|
| 47 |
+
```python
|
| 48 |
+
import gradio as gr
|
| 49 |
+
|
| 50 |
+
with gr.Blocks() as demo:
|
| 51 |
+
name = gr.Textbox(label="Name")
|
| 52 |
+
output = gr.Textbox(label="Greeting")
|
| 53 |
+
btn = gr.Button("Greet")
|
| 54 |
+
btn.click(fn=lambda n: f"Hello {n}!", inputs=name, outputs=output)
|
| 55 |
+
|
| 56 |
+
demo.launch()
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
**ChatInterface**: high-level wrapper for chatbot UIs.
|
| 60 |
+
|
| 61 |
+
```python
|
| 62 |
+
import gradio as gr
|
| 63 |
+
|
| 64 |
+
def respond(message, history):
|
| 65 |
+
return f"You said: {message}"
|
| 66 |
+
|
| 67 |
+
gr.ChatInterface(fn=respond).launch()
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
## Custom HTML Components
|
| 71 |
+
|
| 72 |
+
If a task requires significant customization of an existing component or a component that doesn't exist in Gradio, you can create one with `gr.HTML`. It supports `html_template` (with `${}` JS expressions and `{{}}` Handlebars syntax), `css_template` for scoped styles, and `js_on_load` for interactivity β where `props.value` updates the component value and `trigger('event_name')` fires Gradio events. For reuse, subclass `gr.HTML` and define `api_info()` for API/MCP support.
|
| 73 |
+
|
| 74 |
+
See the [full guide](https://www.gradio.app/guides/custom-HTML-components) as well as example in `references/examples.md`
|
| 75 |
+
|
| 76 |
+
## Server Mode
|
| 77 |
+
|
| 78 |
+
Use `gr.Server` instead of gr.Blocks when the users requests any of the following:
|
| 79 |
+
- Completely custom UI (your own HTML, React, Svelte, etc.) powered by Gradio's backend.
|
| 80 |
+
- Full control of FastAPI server (custom GET/POST routes, middleware, dependency injection) alongside Gradio API endpoints
|
| 81 |
+
|
| 82 |
+
See the [full guide](https://www.gradio.app/guides/server-mode) and example in `references/examples.md`.
|
| 83 |
+
|
| 84 |
+
If the user's use case can be handled by Gradio's built-in components or customizable HTML components, prefer not to use `gr.Server`.
|
.agents/skills/gradio/references/api-signatures.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Component API Signatures
|
| 2 |
+
|
| 3 |
+
Quick reference for common Gradio component constructors.
|
| 4 |
+
|
| 5 |
+
## `Textbox`
|
| 6 |
+
|
| 7 |
+
```python
|
| 8 |
+
Textbox(value: str | I18nData | Callable | None = None, type: Literal['text', 'password', 'email'] = "text", lines: int = 1, max_lines: int | None = None, placeholder: str | I18nData | None = None, label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, autofocus: bool = False, autoscroll: bool = True, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", text_align: Literal['left', 'right'] | None = None, rtl: bool = False, buttons: list[Literal['copy'] | Button] | None = None, max_length: int | None = None, submit_btn: str | bool | None = False, stop_btn: str | bool | None = False, html_attributes: InputHTMLAttributes | None = None)
|
| 9 |
+
```
|
| 10 |
+
|
| 11 |
+
Creates a textarea for user to enter string input or display string output.
|
| 12 |
+
|
| 13 |
+
## `Number`
|
| 14 |
+
|
| 15 |
+
```python
|
| 16 |
+
Number(value: float | Callable | None = None, label: str | I18nData | None = None, placeholder: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", buttons: list[Button] | None = None, precision: int | None = None, minimum: float | None = None, maximum: float | None = None, step: float = 1)
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
Creates a numeric field for user to enter numbers as input or display numeric output.
|
| 20 |
+
|
| 21 |
+
## `Slider`
|
| 22 |
+
|
| 23 |
+
```python
|
| 24 |
+
Slider(minimum: float = 0, maximum: float = 100, value: float | Callable | None = None, step: float | None = None, precision: int | None = None, label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", randomize: bool = False, buttons: list[Literal['reset']] | None = None)
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
Creates a slider that ranges from {minimum} to {maximum} with a step size of {step}.
|
| 28 |
+
|
| 29 |
+
## `Checkbox`
|
| 30 |
+
|
| 31 |
+
```python
|
| 32 |
+
Checkbox(value: bool | Callable = False, label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", buttons: list[Button] | None = None)
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
Creates a checkbox that can be set to `True` or `False`. Can be used as an input to pass a boolean value to a function or as an output to display a boolean value.
|
| 36 |
+
|
| 37 |
+
## `Dropdown`
|
| 38 |
+
|
| 39 |
+
```python
|
| 40 |
+
Dropdown(choices: Sequence[str | int | float | tuple[str | I18nData, str | int | float]] | None = None, value: str | int | float | Sequence[str | int | float] | Callable | DefaultValue | None = DefaultValue(), type: Literal['value', 'index'] = "value", multiselect: bool | None = None, allow_custom_value: bool = False, max_choices: int | None = None, filterable: bool = True, label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", buttons: list[Button] | None = None)
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
Creates a dropdown of choices from which a single entry or multiple entries can be selected (as an input component) or displayed (as an output component).
|
| 44 |
+
|
| 45 |
+
## `Radio`
|
| 46 |
+
|
| 47 |
+
```python
|
| 48 |
+
Radio(choices: Sequence[str | int | float | tuple[str | I18nData, str | int | float]] | None = None, value: str | int | float | Callable | None = None, type: Literal['value', 'index'] = "value", label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", rtl: bool = False, buttons: list[Button] | None = None)
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
Creates a set of (string or numeric type) radio buttons of which only one can be selected.
|
| 52 |
+
|
| 53 |
+
## `Image`
|
| 54 |
+
|
| 55 |
+
```python
|
| 56 |
+
Image(value: str | PIL.Image.Image | np.ndarray | Callable | None = None, format: str = "webp", height: int | str | None = None, width: int | str | None = None, image_mode: Literal['1', 'L', 'P', 'RGB', 'RGBA', 'CMYK', 'YCbCr', 'LAB', 'HSV', 'I', 'F'] | None = "RGB", sources: list[Literal['upload', 'webcam', 'clipboard']] | Literal['upload', 'webcam', 'clipboard'] | None = None, type: Literal['numpy', 'pil', 'filepath'] = "numpy", label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, buttons: list[Literal['download', 'share', 'fullscreen'] | Button] | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, streaming: bool = False, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", webcam_options: WebcamOptions | None = None, placeholder: str | None = None, watermark: WatermarkOptions | None = None)
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
Creates an image component that can be used to upload images (as an input) or display images (as an output).
|
| 60 |
+
|
| 61 |
+
## `Audio`
|
| 62 |
+
|
| 63 |
+
```python
|
| 64 |
+
Audio(value: str | Path | tuple[int, np.ndarray] | Callable | None = None, sources: list[Literal['upload', 'microphone']] | Literal['upload', 'microphone'] | None = None, type: Literal['numpy', 'filepath'] = "numpy", label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, streaming: bool = False, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", format: Literal['wav', 'mp3'] | None = None, autoplay: bool = False, editable: bool = True, buttons: list[Literal['download', 'share'] | Button] | None = None, waveform_options: WaveformOptions | dict | None = None, loop: bool = False, recording: bool = False, subtitles: str | Path | list[dict[str, Any]] | None = None, playback_position: float = 0)
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
Creates an audio component that can be used to upload/record audio (as an input) or display audio (as an output).
|
| 68 |
+
|
| 69 |
+
## `Video`
|
| 70 |
+
|
| 71 |
+
```python
|
| 72 |
+
Video(value: str | Path | Callable | None = None, format: str | None = None, sources: list[Literal['upload', 'webcam']] | Literal['upload', 'webcam'] | None = None, height: int | str | None = None, width: int | str | None = None, label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", webcam_options: WebcamOptions | None = None, include_audio: bool | None = None, autoplay: bool = False, buttons: list[Literal['download', 'share'] | Button] | None = None, loop: bool = False, streaming: bool = False, watermark: WatermarkOptions | None = None, subtitles: str | Path | list[dict[str, Any]] | None = None, playback_position: float = 0)
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
Creates a video component that can be used to upload/record videos (as an input) or display videos (as an output). For the video to be playable in the browser it must have a compatible container and codec combination. Allowed combinations are .mp4 with h264 codec, .ogg with theora codec, and .webm with vp9 codec. If the component detects that the output video would not be playable in the browser it will attempt to convert it to a playable mp4 video. If the conversion fails, the original video is returned.
|
| 76 |
+
|
| 77 |
+
## `File`
|
| 78 |
+
|
| 79 |
+
```python
|
| 80 |
+
File(value: str | list[str] | Callable | None = None, file_count: Literal['single', 'multiple', 'directory'] = "single", file_types: list[str] | None = None, type: Literal['filepath', 'binary'] = "filepath", label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, height: int | str | float | None = None, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", allow_reordering: bool = False, buttons: list[Button] | None = None)
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
Creates a file component that allows uploading one or more generic files (when used as an input) or displaying generic files or URLs for download (as output). Demo: zip_files, zip_to_json
|
| 84 |
+
|
| 85 |
+
## `Chatbot`
|
| 86 |
+
|
| 87 |
+
```python
|
| 88 |
+
Chatbot(value: list[MessageDict | Message] | Callable | None = None, label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, autoscroll: bool = True, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", height: int | str | None = 400, resizable: bool = False, max_height: int | str | None = None, min_height: int | str | None = None, editable: Literal['user', 'all'] | None = None, latex_delimiters: list[dict[str, str | bool]] | None = None, rtl: bool = False, buttons: list[Literal['share', 'copy', 'copy_all'] | Button] | None = None, watermark: str | None = None, avatar_images: tuple[str | Path | None, str | Path | None] | None = None, sanitize_html: bool = True, render_markdown: bool = True, feedback_options: list[str] | tuple[str, ...] | None = ('Like', 'Dislike'), feedback_value: Sequence[str | None] | None = None, line_breaks: bool = True, layout: Literal['panel', 'bubble'] | None = None, placeholder: str | None = None, examples: list[ExampleMessage] | None = None, allow_file_downloads: bool = True, group_consecutive_messages: bool = True, allow_tags: list[str] | bool = True, reasoning_tags: list[tuple[str, str]] | None = None, like_user_message: bool = False)
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
Creates a chatbot that displays user-submitted messages and responses. Supports a subset of Markdown including bold, italics, code, tables. Also supports audio/video/image files, which are displayed in the Chatbot, and other kinds of files which are displayed as links. This component is usually used as an output component.
|
| 92 |
+
|
| 93 |
+
## `Button`
|
| 94 |
+
|
| 95 |
+
```python
|
| 96 |
+
Button(value: str | I18nData | Callable = "Run", every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, variant: Literal['primary', 'secondary', 'stop', 'huggingface'] = "secondary", size: Literal['sm', 'md', 'lg'] = "lg", icon: str | Path | None = None, link: str | None = None, link_target: Literal['_self', '_blank', '_parent', '_top'] = "_self", visible: bool | Literal['hidden'] = True, interactive: bool = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", scale: int | None = None, min_width: int | None = None)
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
Creates a button that can be assigned arbitrary .click() events. The value (label) of the button can be used as an input to the function (rarely used) or set via the output of a function.
|
| 100 |
+
|
| 101 |
+
## `Markdown`
|
| 102 |
+
|
| 103 |
+
```python
|
| 104 |
+
Markdown(value: str | I18nData | Callable | None = None, label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, scale: int | None = None, min_width: int | None = None, rtl: bool = False, latex_delimiters: list[dict[str, str | bool]] | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", sanitize_html: bool = True, line_breaks: bool = False, header_links: bool = False, height: int | str | None = None, max_height: int | str | None = None, min_height: int | str | None = None, buttons: list[Literal['copy']] | None = None, container: bool = False, padding: bool = False)
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
Used to render arbitrary Markdown output. Can also render latex enclosed by dollar signs as well as code blocks with syntax highlighting. Supported languages are bash, c, cpp, go, java, javascript, json, php, python, rust, sql, and yaml. As this component does not accept user input, it is rarely used as an input component.
|
| 108 |
+
|
| 109 |
+
## `HTML`
|
| 110 |
+
|
| 111 |
+
```python
|
| 112 |
+
HTML(value: Any | Callable | None = None, label: str | I18nData | None = None, html_template: str = "${value}", css_template: str = "", js_on_load: str | None = "element.addEventListener('click', function() { trigger('click') });", apply_default_css: bool = True, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool = False, scale: int | None = None, min_width: int | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", min_height: int | None = None, max_height: int | None = None, container: bool = False, padding: bool = False, autoscroll: bool = False, buttons: list[Button] | None = None, head: str | None = None, server_functions: list[Callable] | None = None, props: Any)
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
Creates a component with arbitrary HTML. Can include CSS and JavaScript to create highly customized and interactive components.
|
.agents/skills/gradio/references/event-listeners.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Event Listeners
|
| 2 |
+
|
| 3 |
+
Events supported by each component.
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
## Event Listener Signature
|
| 7 |
+
|
| 8 |
+
```python
|
| 9 |
+
component.event_name(
|
| 10 |
+
fn: Callable | None | Literal["decorator"] = "decorator",
|
| 11 |
+
inputs: Component | Sequence[Component] | set[Component] | None = None,
|
| 12 |
+
outputs: Component | Sequence[Component] | set[Component] | None = None,
|
| 13 |
+
api_name: str | None = None,
|
| 14 |
+
api_description: str | None | Literal[False] = None,
|
| 15 |
+
scroll_to_output: bool = False,
|
| 16 |
+
show_progress: Literal["full", "minimal", "hidden"] = "full",
|
| 17 |
+
show_progress_on: Component | Sequence[Component] | None = None,
|
| 18 |
+
queue: bool = True,
|
| 19 |
+
batch: bool = False,
|
| 20 |
+
max_batch_size: int = 4,
|
| 21 |
+
preprocess: bool = True,
|
| 22 |
+
postprocess: bool = True,
|
| 23 |
+
cancels: dict[str, Any] | list[dict[str, Any]] | None = None,
|
| 24 |
+
trigger_mode: Literal["once", "multiple", "always_last"] | None = None,
|
| 25 |
+
js: str | Literal[True] | None = None,
|
| 26 |
+
concurrency_limit: int | None | Literal["default"] = "default",
|
| 27 |
+
concurrency_id: str | None = None,
|
| 28 |
+
api_visibility: Literal["public", "private", "undocumented"] = "public",
|
| 29 |
+
time_limit: int | None = None,
|
| 30 |
+
stream_every: float = 0.5,
|
| 31 |
+
key: int | str | tuple[int | str, ...] | None = None,
|
| 32 |
+
validator: Callable | None = None,
|
| 33 |
+
) -> Dependency
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
## Supported Events by Component
|
| 37 |
+
|
| 38 |
+
- **AnnotatedImage**: change, select
|
| 39 |
+
|
| 40 |
+
- **Audio**: stream, change, clear, play, pause, stop, pause, start_recording, pause_recording, stop_recording, upload, input
|
| 41 |
+
|
| 42 |
+
- **BarPlot**: change, select, double_click
|
| 43 |
+
|
| 44 |
+
- **BrowserState**: change
|
| 45 |
+
|
| 46 |
+
- **Button**: change, click
|
| 47 |
+
|
| 48 |
+
- **Chatbot**: change, select, like, retry, undo, example_select, option_select, clear, copy, edit
|
| 49 |
+
|
| 50 |
+
- **Checkbox**: change, input, select
|
| 51 |
+
|
| 52 |
+
- **CheckboxGroup**: change, input, select
|
| 53 |
+
|
| 54 |
+
- **ClearButton**: change, click
|
| 55 |
+
|
| 56 |
+
- **Code**: change, input, focus, blur
|
| 57 |
+
|
| 58 |
+
- **ColorPicker**: change, input, release, submit, focus, blur
|
| 59 |
+
|
| 60 |
+
- **Dataframe**: change, input, select, edit
|
| 61 |
+
|
| 62 |
+
- **Dataset**: change, click, select
|
| 63 |
+
|
| 64 |
+
- **DateTime**: change, submit
|
| 65 |
+
|
| 66 |
+
- **DeepLinkButton**: change, click
|
| 67 |
+
|
| 68 |
+
- **Dialogue**: change, input, submit
|
| 69 |
+
|
| 70 |
+
- **DownloadButton**: change, click
|
| 71 |
+
|
| 72 |
+
- **Dropdown**: change, input, select, focus, blur, key_up
|
| 73 |
+
|
| 74 |
+
- **DuplicateButton**: change, click
|
| 75 |
+
|
| 76 |
+
- **File**: change, select, clear, upload, delete, download
|
| 77 |
+
|
| 78 |
+
- **FileExplorer**: change, input, select
|
| 79 |
+
|
| 80 |
+
- **Gallery**: select, upload, change, delete, preview_close, preview_open
|
| 81 |
+
|
| 82 |
+
- **HTML**: change, input, click, double_click, submit, stop, edit, clear, play, pause, end, start_recording, pause_recording, stop_recording, focus, blur, upload, release, select, stream, like, example_select, option_select, load, key_up, apply, delete, tick, undo, retry, expand, collapse, download, copy
|
| 83 |
+
|
| 84 |
+
- **HighlightedText**: change, select
|
| 85 |
+
|
| 86 |
+
- **Image**: clear, change, stream, select, upload, input
|
| 87 |
+
|
| 88 |
+
- **ImageEditor**: clear, change, input, select, upload, apply
|
| 89 |
+
|
| 90 |
+
- **ImageSlider**: clear, change, stream, select, upload, input
|
| 91 |
+
|
| 92 |
+
- **JSON**: change
|
| 93 |
+
|
| 94 |
+
- **Label**: change, select
|
| 95 |
+
|
| 96 |
+
- **LinePlot**: change, select, double_click
|
| 97 |
+
|
| 98 |
+
- **LoginButton**: change, click
|
| 99 |
+
|
| 100 |
+
- **Markdown**: change, copy
|
| 101 |
+
|
| 102 |
+
- **Model3D**: change, upload, edit, clear
|
| 103 |
+
|
| 104 |
+
- **MultimodalTextbox**: change, input, select, submit, focus, blur, stop
|
| 105 |
+
|
| 106 |
+
- **Navbar**: change
|
| 107 |
+
|
| 108 |
+
- **Number**: change, input, submit, focus, blur
|
| 109 |
+
|
| 110 |
+
- **ParamViewer**: change, upload
|
| 111 |
+
|
| 112 |
+
- **Plot**: change
|
| 113 |
+
|
| 114 |
+
- **Radio**: select, change, input
|
| 115 |
+
|
| 116 |
+
- **ScatterPlot**: change, select, double_click
|
| 117 |
+
|
| 118 |
+
- **SimpleImage**: clear, change, upload
|
| 119 |
+
|
| 120 |
+
- **Slider**: change, input, release
|
| 121 |
+
|
| 122 |
+
- **State**: change
|
| 123 |
+
|
| 124 |
+
- **Textbox**: change, input, select, submit, focus, blur, stop, copy
|
| 125 |
+
|
| 126 |
+
- **Timer**: change, tick
|
| 127 |
+
|
| 128 |
+
- **UploadButton**: change, click, upload
|
| 129 |
+
|
| 130 |
+
- **Video**: change, clear, start_recording, stop_recording, stop, play, pause, end, upload, input
|
| 131 |
+
|
| 132 |
+
- **WorkflowCanvas**: change
|
.agents/skills/gradio/references/examples.md
ADDED
|
@@ -0,0 +1,613 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Gradio End-to-End Examples
|
| 2 |
+
|
| 3 |
+
Complete working Gradio apps for reference.
|
| 4 |
+
|
| 5 |
+
## Blocks Essay Simple
|
| 6 |
+
|
| 7 |
+
```python
|
| 8 |
+
import gradio as gr
|
| 9 |
+
|
| 10 |
+
def change_textbox(choice):
|
| 11 |
+
if choice == "short":
|
| 12 |
+
return gr.Textbox(lines=2, visible=True)
|
| 13 |
+
elif choice == "long":
|
| 14 |
+
return gr.Textbox(lines=8, visible=True, value="Lorem ipsum dolor sit amet")
|
| 15 |
+
else:
|
| 16 |
+
return gr.Textbox(visible=False)
|
| 17 |
+
|
| 18 |
+
with gr.Blocks() as demo:
|
| 19 |
+
radio = gr.Radio(
|
| 20 |
+
["short", "long", "none"], label="What kind of essay would you like to write?"
|
| 21 |
+
)
|
| 22 |
+
text = gr.Textbox(lines=2, interactive=True, buttons=["copy"])
|
| 23 |
+
radio.change(fn=change_textbox, inputs=radio, outputs=text)
|
| 24 |
+
|
| 25 |
+
demo.launch()
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
## Blocks Flipper
|
| 29 |
+
|
| 30 |
+
```python
|
| 31 |
+
import numpy as np
|
| 32 |
+
import gradio as gr
|
| 33 |
+
|
| 34 |
+
def flip_text(x):
|
| 35 |
+
return x[::-1]
|
| 36 |
+
|
| 37 |
+
def flip_image(x):
|
| 38 |
+
return np.fliplr(x)
|
| 39 |
+
|
| 40 |
+
with gr.Blocks() as demo:
|
| 41 |
+
gr.Markdown("Flip text or image files using this demo.")
|
| 42 |
+
with gr.Tab("Flip Text"):
|
| 43 |
+
text_input = gr.Textbox()
|
| 44 |
+
text_output = gr.Textbox()
|
| 45 |
+
text_button = gr.Button("Flip")
|
| 46 |
+
with gr.Tab("Flip Image"):
|
| 47 |
+
with gr.Row():
|
| 48 |
+
image_input = gr.Image()
|
| 49 |
+
image_output = gr.Image()
|
| 50 |
+
image_button = gr.Button("Flip")
|
| 51 |
+
|
| 52 |
+
with gr.Accordion("Open for More!", open=False):
|
| 53 |
+
gr.Markdown("Look at me...")
|
| 54 |
+
temp_slider = gr.Slider(
|
| 55 |
+
0, 1,
|
| 56 |
+
value=0.1,
|
| 57 |
+
step=0.1,
|
| 58 |
+
interactive=True,
|
| 59 |
+
label="Slide me",
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
text_button.click(flip_text, inputs=text_input, outputs=text_output)
|
| 63 |
+
image_button.click(flip_image, inputs=image_input, outputs=image_output)
|
| 64 |
+
|
| 65 |
+
demo.launch()
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
## Blocks Form
|
| 69 |
+
|
| 70 |
+
```python
|
| 71 |
+
import gradio as gr
|
| 72 |
+
|
| 73 |
+
with gr.Blocks() as demo:
|
| 74 |
+
name_box = gr.Textbox(label="Name")
|
| 75 |
+
age_box = gr.Number(label="Age", minimum=0, maximum=100)
|
| 76 |
+
symptoms_box = gr.CheckboxGroup(["Cough", "Fever", "Runny Nose"])
|
| 77 |
+
submit_btn = gr.Button("Submit")
|
| 78 |
+
|
| 79 |
+
with gr.Column(visible=False) as output_col:
|
| 80 |
+
diagnosis_box = gr.Textbox(label="Diagnosis")
|
| 81 |
+
patient_summary_box = gr.Textbox(label="Patient Summary")
|
| 82 |
+
|
| 83 |
+
def submit(name, age, symptoms):
|
| 84 |
+
return {
|
| 85 |
+
submit_btn: gr.Button(visible=False),
|
| 86 |
+
output_col: gr.Column(visible=True),
|
| 87 |
+
diagnosis_box: "covid" if "Cough" in symptoms else "flu",
|
| 88 |
+
patient_summary_box: f"{name}, {age} y/o",
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
submit_btn.click(
|
| 92 |
+
submit,
|
| 93 |
+
[name_box, age_box, symptoms_box],
|
| 94 |
+
[submit_btn, diagnosis_box, patient_summary_box, output_col],
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
demo.launch()
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
## Blocks Hello
|
| 101 |
+
|
| 102 |
+
```python
|
| 103 |
+
import gradio as gr
|
| 104 |
+
|
| 105 |
+
def welcome(name):
|
| 106 |
+
return f"Welcome to Gradio, {name}!"
|
| 107 |
+
|
| 108 |
+
with gr.Blocks() as demo:
|
| 109 |
+
gr.Markdown(
|
| 110 |
+
"""
|
| 111 |
+
# Hello World!
|
| 112 |
+
Start typing below to see the output.
|
| 113 |
+
""")
|
| 114 |
+
inp = gr.Textbox(placeholder="What is your name?")
|
| 115 |
+
out = gr.Textbox()
|
| 116 |
+
inp.change(welcome, inp, out)
|
| 117 |
+
|
| 118 |
+
demo.launch()
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
## Blocks Layout
|
| 122 |
+
|
| 123 |
+
```python
|
| 124 |
+
import gradio as gr
|
| 125 |
+
|
| 126 |
+
demo = gr.Blocks()
|
| 127 |
+
|
| 128 |
+
with demo:
|
| 129 |
+
with gr.Row():
|
| 130 |
+
gr.Image(interactive=True, scale=2)
|
| 131 |
+
gr.Image()
|
| 132 |
+
with gr.Row():
|
| 133 |
+
gr.Textbox(label="Text")
|
| 134 |
+
gr.Number(label="Count", scale=2)
|
| 135 |
+
gr.Radio(choices=["One", "Two"])
|
| 136 |
+
with gr.Row():
|
| 137 |
+
gr.Button("500", scale=0, min_width=500)
|
| 138 |
+
gr.Button("A", scale=0)
|
| 139 |
+
gr.Button("grow")
|
| 140 |
+
with gr.Row():
|
| 141 |
+
gr.Textbox()
|
| 142 |
+
gr.Textbox()
|
| 143 |
+
gr.Button()
|
| 144 |
+
with gr.Row():
|
| 145 |
+
with gr.Row():
|
| 146 |
+
with gr.Column():
|
| 147 |
+
gr.Textbox(label="Text")
|
| 148 |
+
gr.Number(label="Count")
|
| 149 |
+
gr.Radio(choices=["One", "Two"])
|
| 150 |
+
gr.Image()
|
| 151 |
+
with gr.Column():
|
| 152 |
+
gr.Image(interactive=True)
|
| 153 |
+
gr.Image()
|
| 154 |
+
gr.Image()
|
| 155 |
+
gr.Textbox(label="Text")
|
| 156 |
+
gr.Number(label="Count")
|
| 157 |
+
gr.Radio(choices=["One", "Two"])
|
| 158 |
+
|
| 159 |
+
demo.launch()
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
## Calculator
|
| 163 |
+
|
| 164 |
+
```python
|
| 165 |
+
import gradio as gr
|
| 166 |
+
|
| 167 |
+
def calculator(num1, operation, num2):
|
| 168 |
+
if operation == "add":
|
| 169 |
+
return num1 + num2
|
| 170 |
+
elif operation == "subtract":
|
| 171 |
+
return num1 - num2
|
| 172 |
+
elif operation == "multiply":
|
| 173 |
+
return num1 * num2
|
| 174 |
+
elif operation == "divide":
|
| 175 |
+
if num2 == 0:
|
| 176 |
+
raise gr.Error("Cannot divide by zero!")
|
| 177 |
+
return num1 / num2
|
| 178 |
+
|
| 179 |
+
demo = gr.Interface(
|
| 180 |
+
calculator,
|
| 181 |
+
[
|
| 182 |
+
"number",
|
| 183 |
+
gr.Radio(["add", "subtract", "multiply", "divide"]),
|
| 184 |
+
"number"
|
| 185 |
+
],
|
| 186 |
+
"number",
|
| 187 |
+
examples=[
|
| 188 |
+
[45, "add", 3],
|
| 189 |
+
[3.14, "divide", 2],
|
| 190 |
+
[144, "multiply", 2.5],
|
| 191 |
+
[0, "subtract", 1.2],
|
| 192 |
+
],
|
| 193 |
+
title="Toy Calculator",
|
| 194 |
+
description="Here's a sample toy calculator.",
|
| 195 |
+
api_name="predict"
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
demo.launch()
|
| 199 |
+
```
|
| 200 |
+
|
| 201 |
+
## Chatbot Simple
|
| 202 |
+
|
| 203 |
+
```python
|
| 204 |
+
import gradio as gr
|
| 205 |
+
import random
|
| 206 |
+
import time
|
| 207 |
+
|
| 208 |
+
with gr.Blocks() as demo:
|
| 209 |
+
chatbot = gr.Chatbot()
|
| 210 |
+
msg = gr.Textbox()
|
| 211 |
+
clear = gr.ClearButton([msg, chatbot])
|
| 212 |
+
|
| 213 |
+
def respond(message, chat_history):
|
| 214 |
+
bot_message = random.choice(["How are you?", "Today is a great day", "I'm very hungry"])
|
| 215 |
+
chat_history.append({"role": "user", "content": message})
|
| 216 |
+
chat_history.append({"role": "assistant", "content": bot_message})
|
| 217 |
+
time.sleep(2)
|
| 218 |
+
return "", chat_history
|
| 219 |
+
|
| 220 |
+
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
| 221 |
+
|
| 222 |
+
demo.launch()
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
## Chatbot Streaming
|
| 226 |
+
|
| 227 |
+
```python
|
| 228 |
+
import gradio as gr
|
| 229 |
+
import random
|
| 230 |
+
import time
|
| 231 |
+
|
| 232 |
+
with gr.Blocks() as demo:
|
| 233 |
+
chatbot = gr.Chatbot()
|
| 234 |
+
msg = gr.Textbox()
|
| 235 |
+
clear = gr.Button("Clear")
|
| 236 |
+
|
| 237 |
+
def user(user_message, history: list):
|
| 238 |
+
return "", history + [{"role": "user", "content": user_message}]
|
| 239 |
+
|
| 240 |
+
def bot(history: list):
|
| 241 |
+
bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"])
|
| 242 |
+
history.append({"role": "assistant", "content": ""})
|
| 243 |
+
for character in bot_message:
|
| 244 |
+
history[-1]['content'] += character
|
| 245 |
+
time.sleep(0.05)
|
| 246 |
+
yield history
|
| 247 |
+
|
| 248 |
+
msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
|
| 249 |
+
bot, chatbot, chatbot
|
| 250 |
+
)
|
| 251 |
+
clear.click(lambda: None, None, chatbot, queue=False)
|
| 252 |
+
|
| 253 |
+
demo.launch()
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
## Custom Css
|
| 257 |
+
|
| 258 |
+
```python
|
| 259 |
+
import gradio as gr
|
| 260 |
+
|
| 261 |
+
with gr.Blocks() as demo:
|
| 262 |
+
with gr.Column(elem_classes="cool-col"):
|
| 263 |
+
gr.Markdown("### Gradio Demo with Custom CSS", elem_classes="darktest")
|
| 264 |
+
gr.Markdown(
|
| 265 |
+
elem_classes="markdown",
|
| 266 |
+
value="Resize the browser window to see the CSS media query in action.",
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
if __name__ == "__main__":
|
| 270 |
+
demo.launch(css_paths=["demo/custom_css/custom_css.css"])
|
| 271 |
+
```
|
| 272 |
+
|
| 273 |
+
## Fake Diffusion
|
| 274 |
+
|
| 275 |
+
```python
|
| 276 |
+
import gradio as gr
|
| 277 |
+
import numpy as np
|
| 278 |
+
import time
|
| 279 |
+
|
| 280 |
+
def fake_diffusion(steps):
|
| 281 |
+
rng = np.random.default_rng()
|
| 282 |
+
for i in range(steps):
|
| 283 |
+
time.sleep(1)
|
| 284 |
+
image = rng.random(size=(600, 600, 3))
|
| 285 |
+
yield image
|
| 286 |
+
image = np.ones((1000,1000,3), np.uint8)
|
| 287 |
+
image[:] = [255, 124, 0]
|
| 288 |
+
yield image
|
| 289 |
+
|
| 290 |
+
demo = gr.Interface(fake_diffusion,
|
| 291 |
+
inputs=gr.Slider(1, 10, 3, step=1),
|
| 292 |
+
outputs="image",
|
| 293 |
+
api_name="predict")
|
| 294 |
+
|
| 295 |
+
demo.launch()
|
| 296 |
+
```
|
| 297 |
+
|
| 298 |
+
## Hello World
|
| 299 |
+
|
| 300 |
+
```python
|
| 301 |
+
import gradio as gr
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def greet(name):
|
| 305 |
+
return "Hello " + name + "!"
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
demo = gr.Interface(fn=greet, inputs="textbox", outputs="textbox", api_name="predict")
|
| 309 |
+
|
| 310 |
+
demo.launch()
|
| 311 |
+
```
|
| 312 |
+
|
| 313 |
+
## Image Editor
|
| 314 |
+
|
| 315 |
+
```python
|
| 316 |
+
import gradio as gr
|
| 317 |
+
import time
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
def sleep(im):
|
| 321 |
+
time.sleep(5)
|
| 322 |
+
return [im["background"], im["layers"][0], im["layers"][1], im["composite"]]
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
def predict(im):
|
| 326 |
+
return im["composite"]
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
with gr.Blocks() as demo:
|
| 330 |
+
with gr.Row():
|
| 331 |
+
im = gr.ImageEditor(
|
| 332 |
+
type="numpy",
|
| 333 |
+
)
|
| 334 |
+
im_preview = gr.Image()
|
| 335 |
+
n_upload = gr.Number(0, label="Number of upload events", step=1)
|
| 336 |
+
n_change = gr.Number(0, label="Number of change events", step=1)
|
| 337 |
+
n_input = gr.Number(0, label="Number of input events", step=1)
|
| 338 |
+
|
| 339 |
+
im.upload(lambda x: x + 1, outputs=n_upload, inputs=n_upload)
|
| 340 |
+
im.change(lambda x: x + 1, outputs=n_change, inputs=n_change)
|
| 341 |
+
im.input(lambda x: x + 1, outputs=n_input, inputs=n_input)
|
| 342 |
+
im.change(predict, outputs=im_preview, inputs=im, show_progress="hidden")
|
| 343 |
+
|
| 344 |
+
demo.launch()
|
| 345 |
+
```
|
| 346 |
+
|
| 347 |
+
## On Listener Decorator
|
| 348 |
+
|
| 349 |
+
```python
|
| 350 |
+
import gradio as gr
|
| 351 |
+
|
| 352 |
+
with gr.Blocks() as demo:
|
| 353 |
+
name = gr.Textbox(label="Name")
|
| 354 |
+
output = gr.Textbox(label="Output Box")
|
| 355 |
+
greet_btn = gr.Button("Greet")
|
| 356 |
+
|
| 357 |
+
@gr.on(triggers=[name.submit, greet_btn.click], inputs=name, outputs=output)
|
| 358 |
+
def greet(name):
|
| 359 |
+
return "Hello " + name + "!"
|
| 360 |
+
|
| 361 |
+
demo.launch()
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
## Render Merge
|
| 365 |
+
|
| 366 |
+
```python
|
| 367 |
+
import gradio as gr
|
| 368 |
+
import time
|
| 369 |
+
|
| 370 |
+
with gr.Blocks() as demo:
|
| 371 |
+
text_count = gr.Slider(1, 5, value=1, step=1, label="Textbox Count")
|
| 372 |
+
|
| 373 |
+
@gr.render(inputs=text_count)
|
| 374 |
+
def render_count(count):
|
| 375 |
+
boxes = []
|
| 376 |
+
for i in range(count):
|
| 377 |
+
box = gr.Textbox(label=f"Box {i}")
|
| 378 |
+
boxes.append(box)
|
| 379 |
+
|
| 380 |
+
def merge(*args):
|
| 381 |
+
time.sleep(0.2) # simulate a delay
|
| 382 |
+
return " ".join(args)
|
| 383 |
+
|
| 384 |
+
merge_btn.click(merge, boxes, output)
|
| 385 |
+
|
| 386 |
+
def clear():
|
| 387 |
+
time.sleep(0.2) # simulate a delay
|
| 388 |
+
return [" "] * count
|
| 389 |
+
|
| 390 |
+
clear_btn.click(clear, None, boxes)
|
| 391 |
+
|
| 392 |
+
def countup():
|
| 393 |
+
time.sleep(0.2) # simulate a delay
|
| 394 |
+
return list(range(count))
|
| 395 |
+
|
| 396 |
+
count_btn.click(countup, None, boxes, queue=False)
|
| 397 |
+
|
| 398 |
+
with gr.Row():
|
| 399 |
+
merge_btn = gr.Button("Merge")
|
| 400 |
+
clear_btn = gr.Button("Clear")
|
| 401 |
+
count_btn = gr.Button("Count")
|
| 402 |
+
|
| 403 |
+
output = gr.Textbox()
|
| 404 |
+
|
| 405 |
+
demo.launch()
|
| 406 |
+
```
|
| 407 |
+
|
| 408 |
+
## Reverse Audio 2
|
| 409 |
+
|
| 410 |
+
```python
|
| 411 |
+
import gradio as gr
|
| 412 |
+
import numpy as np
|
| 413 |
+
|
| 414 |
+
def reverse_audio(audio):
|
| 415 |
+
sr, data = audio
|
| 416 |
+
return (sr, np.flipud(data))
|
| 417 |
+
|
| 418 |
+
demo = gr.Interface(fn=reverse_audio,
|
| 419 |
+
inputs="microphone",
|
| 420 |
+
outputs="audio", api_name="predict")
|
| 421 |
+
|
| 422 |
+
demo.launch()
|
| 423 |
+
```
|
| 424 |
+
|
| 425 |
+
## Sepia Filter
|
| 426 |
+
|
| 427 |
+
```python
|
| 428 |
+
import numpy as np
|
| 429 |
+
import gradio as gr
|
| 430 |
+
|
| 431 |
+
def sepia(input_img):
|
| 432 |
+
sepia_filter = np.array([
|
| 433 |
+
[0.393, 0.769, 0.189],
|
| 434 |
+
[0.349, 0.686, 0.168],
|
| 435 |
+
[0.272, 0.534, 0.131]
|
| 436 |
+
])
|
| 437 |
+
sepia_img = input_img.dot(sepia_filter.T)
|
| 438 |
+
sepia_img /= sepia_img.max()
|
| 439 |
+
return sepia_img
|
| 440 |
+
|
| 441 |
+
demo = gr.Interface(sepia, gr.Image(), "image", api_name="predict")
|
| 442 |
+
demo.launch()
|
| 443 |
+
```
|
| 444 |
+
|
| 445 |
+
## Sort Records
|
| 446 |
+
|
| 447 |
+
```python
|
| 448 |
+
import gradio as gr
|
| 449 |
+
|
| 450 |
+
def sort_records(records):
|
| 451 |
+
return records.sort("Quantity")
|
| 452 |
+
|
| 453 |
+
demo = gr.Interface(
|
| 454 |
+
sort_records,
|
| 455 |
+
gr.Dataframe(
|
| 456 |
+
headers=["Item", "Quantity"],
|
| 457 |
+
datatype=["str", "number"],
|
| 458 |
+
row_count=3,
|
| 459 |
+
column_count=2,
|
| 460 |
+
column_limits=(2, 2),
|
| 461 |
+
type="polars"
|
| 462 |
+
),
|
| 463 |
+
"dataframe",
|
| 464 |
+
description="Sort by Quantity"
|
| 465 |
+
)
|
| 466 |
+
|
| 467 |
+
demo.launch()
|
| 468 |
+
```
|
| 469 |
+
|
| 470 |
+
## Streaming Simple
|
| 471 |
+
|
| 472 |
+
```python
|
| 473 |
+
import gradio as gr
|
| 474 |
+
|
| 475 |
+
with gr.Blocks() as demo:
|
| 476 |
+
with gr.Row():
|
| 477 |
+
with gr.Column():
|
| 478 |
+
input_img = gr.Image(label="Input", sources="webcam")
|
| 479 |
+
with gr.Column():
|
| 480 |
+
output_img = gr.Image(label="Output")
|
| 481 |
+
input_img.stream(lambda s: s, input_img, output_img, time_limit=15, stream_every=0.1, concurrency_limit=30)
|
| 482 |
+
|
| 483 |
+
if __name__ == "__main__":
|
| 484 |
+
|
| 485 |
+
demo.launch()
|
| 486 |
+
```
|
| 487 |
+
|
| 488 |
+
## Tabbed Interface Lite
|
| 489 |
+
|
| 490 |
+
```python
|
| 491 |
+
import gradio as gr
|
| 492 |
+
|
| 493 |
+
hello_world = gr.Interface(lambda name: "Hello " + name, "text", "text", api_name="predict")
|
| 494 |
+
bye_world = gr.Interface(lambda name: "Bye " + name, "text", "text", api_name="predict")
|
| 495 |
+
chat = gr.ChatInterface(lambda *args: "Hello " + args[0], api_name="chat")
|
| 496 |
+
|
| 497 |
+
demo = gr.TabbedInterface([hello_world, bye_world, chat], ["Hello World", "Bye World", "Chat"])
|
| 498 |
+
|
| 499 |
+
demo.launch()
|
| 500 |
+
```
|
| 501 |
+
|
| 502 |
+
## Tax Calculator
|
| 503 |
+
|
| 504 |
+
```python
|
| 505 |
+
import gradio as gr
|
| 506 |
+
|
| 507 |
+
def tax_calculator(income, marital_status, assets):
|
| 508 |
+
tax_brackets = [(10, 0), (25, 8), (60, 12), (120, 20), (250, 30)]
|
| 509 |
+
total_deductible = sum(cost for cost, deductible in zip(assets["Cost"], assets["Deductible"]) if deductible)
|
| 510 |
+
taxable_income = income - total_deductible
|
| 511 |
+
|
| 512 |
+
total_tax = 0
|
| 513 |
+
for bracket, rate in tax_brackets:
|
| 514 |
+
if taxable_income > bracket:
|
| 515 |
+
total_tax += (taxable_income - bracket) * rate / 100
|
| 516 |
+
|
| 517 |
+
if marital_status == "Married":
|
| 518 |
+
total_tax *= 0.75
|
| 519 |
+
elif marital_status == "Divorced":
|
| 520 |
+
total_tax *= 0.8
|
| 521 |
+
|
| 522 |
+
return round(total_tax)
|
| 523 |
+
|
| 524 |
+
demo = gr.Interface(
|
| 525 |
+
tax_calculator,
|
| 526 |
+
[
|
| 527 |
+
"number",
|
| 528 |
+
gr.Radio(["Single", "Married", "Divorced"]),
|
| 529 |
+
gr.Dataframe(
|
| 530 |
+
headers=["Item", "Cost", "Deductible"],
|
| 531 |
+
datatype=["str", "number", "bool"],
|
| 532 |
+
label="Assets Purchased this Year",
|
| 533 |
+
),
|
| 534 |
+
],
|
| 535 |
+
gr.Number(label="Tax due"),
|
| 536 |
+
examples=[
|
| 537 |
+
[10000, "Married", [["Suit", 5000, True], ["Laptop (for work)", 800, False], ["Car", 1800, True]]],
|
| 538 |
+
[80000, "Single", [["Suit", 800, True], ["Watch", 1800, True], ["Food", 800, True]]],
|
| 539 |
+
],
|
| 540 |
+
live=True,
|
| 541 |
+
api_name="predict"
|
| 542 |
+
)
|
| 543 |
+
|
| 544 |
+
demo.launch()
|
| 545 |
+
```
|
| 546 |
+
|
| 547 |
+
## Timer Simple
|
| 548 |
+
|
| 549 |
+
```python
|
| 550 |
+
import gradio as gr
|
| 551 |
+
import random
|
| 552 |
+
import time
|
| 553 |
+
|
| 554 |
+
with gr.Blocks() as demo:
|
| 555 |
+
timer = gr.Timer(1)
|
| 556 |
+
timestamp = gr.Number(label="Time")
|
| 557 |
+
timer.tick(lambda: round(time.time()), outputs=timestamp, api_name="timestamp")
|
| 558 |
+
|
| 559 |
+
number = gr.Number(lambda: random.randint(1, 10), every=timer, label="Random Number")
|
| 560 |
+
with gr.Row():
|
| 561 |
+
gr.Button("Start").click(lambda: gr.Timer(active=True), None, timer)
|
| 562 |
+
gr.Button("Stop").click(lambda: gr.Timer(active=False), None, timer)
|
| 563 |
+
gr.Button("Go Fast").click(lambda: 0.2, None, timer)
|
| 564 |
+
|
| 565 |
+
if __name__ == "__main__":
|
| 566 |
+
demo.launch()
|
| 567 |
+
```
|
| 568 |
+
|
| 569 |
+
## Variable Outputs
|
| 570 |
+
|
| 571 |
+
```python
|
| 572 |
+
import gradio as gr
|
| 573 |
+
|
| 574 |
+
max_textboxes = 10
|
| 575 |
+
|
| 576 |
+
def variable_outputs(k):
|
| 577 |
+
k = int(k)
|
| 578 |
+
return [gr.Textbox(visible=True)]*k + [gr.Textbox(visible=False)]*(max_textboxes-k)
|
| 579 |
+
|
| 580 |
+
with gr.Blocks() as demo:
|
| 581 |
+
s = gr.Slider(1, max_textboxes, value=max_textboxes, step=1, label="How many textboxes to show:")
|
| 582 |
+
textboxes = []
|
| 583 |
+
for i in range(max_textboxes):
|
| 584 |
+
t = gr.Textbox(f"Textbox {i}")
|
| 585 |
+
textboxes.append(t)
|
| 586 |
+
|
| 587 |
+
s.change(variable_outputs, s, textboxes)
|
| 588 |
+
|
| 589 |
+
if __name__ == "__main__":
|
| 590 |
+
demo.launch()
|
| 591 |
+
```
|
| 592 |
+
|
| 593 |
+
## Video Identity
|
| 594 |
+
|
| 595 |
+
```python
|
| 596 |
+
import gradio as gr
|
| 597 |
+
from gradio.media import get_video
|
| 598 |
+
|
| 599 |
+
def video_identity(video):
|
| 600 |
+
return video
|
| 601 |
+
|
| 602 |
+
# get_video() returns file paths to sample media included with Gradio
|
| 603 |
+
demo = gr.Interface(video_identity,
|
| 604 |
+
gr.Video(),
|
| 605 |
+
"playable_video",
|
| 606 |
+
examples=[
|
| 607 |
+
get_video("world.mp4")
|
| 608 |
+
],
|
| 609 |
+
cache_examples=True,
|
| 610 |
+
api_name="predict",)
|
| 611 |
+
|
| 612 |
+
demo.launch()
|
| 613 |
+
```
|
.agents/skills/hf-cli/.hf-skill-manifest.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"installed_revision": "7bf59b7f85b79c74207b10d5e425934514e8b089",
|
| 3 |
+
"schema_version": 1
|
| 4 |
+
}
|
.agents/skills/hf-cli/SKILL.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: hf-cli
|
| 3 |
+
description: "Hugging Face Hub CLI (`hf`) for downloading, uploading, and managing models, datasets, spaces, buckets, repos, papers, jobs, and more on the Hugging Face Hub. Use when: handling authentication; managing local cache; managing Hugging Face Buckets; running or scheduling jobs on Hugging Face infrastructure; managing Hugging Face repos; discussions and pull requests; browsing models, datasets and spaces; reading, searching, or browsing academic papers; managing collections; querying datasets; configuring spaces; setting up webhooks; or deploying and managing HF Inference Endpoints. Make sure to use this skill whenever the user mentions 'hf', 'huggingface', 'Hugging Face', 'huggingface-cli', or 'hugging face cli', or wants to do anything related to the Hugging Face ecosystem and to AI and ML in general. Also use for cloud storage needs like training checkpoints, data pipelines, or agent traces. Use even if the user doesn't explicitly ask for a CLI command. Replaces the deprecated `huggingface-cli`."
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
Install: `curl -LsSf https://hf.co/cli/install.sh | bash -s`.
|
| 7 |
+
|
| 8 |
+
The Hugging Face Hub CLI tool `hf` is available. IMPORTANT: The `hf` command replaces the deprecated `huggingface-cli` command.
|
| 9 |
+
|
| 10 |
+
Use `hf --help` to view available functions. Note that auth commands are now all under `hf auth` e.g. `hf auth whoami`.
|
| 11 |
+
|
| 12 |
+
Generated with `huggingface_hub v1.19.0`. Run `hf skills add --force` to regenerate.
|
| 13 |
+
|
| 14 |
+
## Commands
|
| 15 |
+
|
| 16 |
+
- `hf cp SRC` β Copy files between local paths, repositories, and buckets. `[--format [auto|human|agent|json|quiet]]`
|
| 17 |
+
- `hf download REPO_ID` β Download files from the Hub. `[--type [model|dataset|space] --revision TEXT --include TEXT --exclude TEXT --cache-dir TEXT --local-dir TEXT --force-download --dry-run --max-workers INTEGER --format [auto|human|agent|json|quiet]]`
|
| 18 |
+
- `hf env` β Print information about the environment. `[--format [auto|human|agent|json|quiet]]`
|
| 19 |
+
- `hf sync` β Sync files between local directory and a bucket. `[--delete --ignore-times --ignore-sizes --plan TEXT --apply TEXT --dry-run --include TEXT --exclude TEXT --filter-from TEXT --existing --ignore-existing --verbose --format [auto|human|agent|json|quiet]]`
|
| 20 |
+
- `hf update` β Update the `hf` CLI to the latest version. `[--format [auto|human|agent|json|quiet]]`
|
| 21 |
+
- `hf upload REPO_ID` β Upload a file or a folder to the Hub. Recommended for single-commit uploads. `[--type [model|dataset|space] --revision TEXT --private --include TEXT --exclude TEXT --delete TEXT --commit-message TEXT --commit-description TEXT --create-pr --every FLOAT --format [auto|human|agent|json|quiet]]`
|
| 22 |
+
- `hf upload-large-folder REPO_ID LOCAL_PATH` β Upload a large folder to the Hub. Recommended for resumable uploads. `[--type [model|dataset|space] --revision TEXT --private --include TEXT --exclude TEXT --num-workers INTEGER --no-report --no-bars --format [auto|human|agent|json|quiet]]`
|
| 23 |
+
- `hf version` β Print information about the hf version. `[--format [auto|human|agent|json|quiet]]`
|
| 24 |
+
|
| 25 |
+
### `hf auth` β Manage authentication (login, logout, etc.).
|
| 26 |
+
|
| 27 |
+
- `hf auth list` β List all stored access tokens. `[--format [auto|human|agent|json|quiet]]`
|
| 28 |
+
- `hf auth login` β Login using a token from huggingface.co/settings/tokens. `[--add-to-git-credential --force --format [auto|human|agent|json|quiet]]`
|
| 29 |
+
- `hf auth logout` β Logout from a specific token. `[--token-name TEXT --format [auto|human|agent|json|quiet]]`
|
| 30 |
+
- `hf auth switch` β Switch between access tokens. `[--token-name TEXT --add-to-git-credential --format [auto|human|agent|json|quiet]]`
|
| 31 |
+
- `hf auth token` β Print the current access token to stdout. `[--format [auto|human|agent|json|quiet]]`
|
| 32 |
+
- `hf auth whoami` β Find out which huggingface.co account you are logged in as. `[--format [auto|human|agent|json|quiet]]`
|
| 33 |
+
|
| 34 |
+
### `hf buckets` β Commands to interact with buckets.
|
| 35 |
+
|
| 36 |
+
- `hf buckets cp SRC` β Copy files between local paths, repositories, and buckets. `[--format [auto|human|agent|json|quiet]]`
|
| 37 |
+
- `hf buckets create BUCKET_ID` β Create a new bucket. `[--private --region [us|eu] --exist-ok --format [auto|human|agent|json|quiet]]`
|
| 38 |
+
- `hf buckets delete BUCKET_ID` β Delete a bucket. `[--yes --missing-ok --format [auto|human|agent|json|quiet]]`
|
| 39 |
+
- `hf buckets info BUCKET_ID` β Get info about a bucket. `[--format [auto|human|agent|json|quiet]]`
|
| 40 |
+
- `hf buckets list` β List buckets or files in a bucket. `[--human-readable --tree --recursive --search TEXT --format [auto|human|agent|json|quiet]]`
|
| 41 |
+
- `hf buckets move FROM_ID TO_ID` β Move (rename) a bucket to a new name or namespace. `[--format [auto|human|agent|json|quiet]]`
|
| 42 |
+
- `hf buckets remove ARGUMENT` β Remove files from a bucket. `[--recursive --yes --dry-run --include TEXT --exclude TEXT --format [auto|human|agent|json|quiet]]`
|
| 43 |
+
- `hf buckets sync` β Sync files between local directory and a bucket. `[--delete --ignore-times --ignore-sizes --plan TEXT --apply TEXT --dry-run --include TEXT --exclude TEXT --filter-from TEXT --existing --ignore-existing --verbose --format [auto|human|agent|json|quiet]]`
|
| 44 |
+
|
| 45 |
+
### `hf cache` β Manage local cache directory.
|
| 46 |
+
|
| 47 |
+
- `hf cache list` β List cached repositories or revisions. `[--cache-dir TEXT --revisions --filter TEXT --sort [accessed|accessed:asc|accessed:desc|modified|modified:asc|modified:desc|name|name:asc|name:desc|size|size:asc|size:desc] --limit INTEGER --format [auto|human|agent|json|quiet]]`
|
| 48 |
+
- `hf cache prune` β Remove detached revisions from the cache. `[--cache-dir TEXT --yes --dry-run --format [auto|human|agent|json|quiet]]`
|
| 49 |
+
- `hf cache rm TARGETS` β Remove cached repositories or revisions. `[--cache-dir TEXT --yes --dry-run --format [auto|human|agent|json|quiet]]`
|
| 50 |
+
- `hf cache verify REPO_ID` β Verify checksums for a single repo revision from cache or a local directory. `[--type [model|dataset|space] --revision TEXT --cache-dir TEXT --local-dir TEXT --fail-on-missing-files --fail-on-extra-files --format [auto|human|agent|json|quiet]]`
|
| 51 |
+
|
| 52 |
+
### `hf collections` β Interact with collections on the Hub.
|
| 53 |
+
|
| 54 |
+
- `hf collections add-item COLLECTION_SLUG ITEM_ID ITEM_TYPE` β Add an item to a collection. `[--note TEXT --exists-ok --format [auto|human|agent|json|quiet]]`
|
| 55 |
+
- `hf collections create TITLE` β Create a new collection on the Hub. `[--namespace TEXT --description TEXT --private --exists-ok --format [auto|human|agent|json|quiet]]`
|
| 56 |
+
- `hf collections delete COLLECTION_SLUG` β Delete a collection from the Hub. `[--missing-ok --format [auto|human|agent|json|quiet]]`
|
| 57 |
+
- `hf collections delete-item COLLECTION_SLUG ITEM_OBJECT_ID` β Delete an item from a collection. `[--missing-ok --format [auto|human|agent|json|quiet]]`
|
| 58 |
+
- `hf collections info COLLECTION_SLUG` β Get info about a collection on the Hub. `[--format [auto|human|agent|json|quiet]]`
|
| 59 |
+
- `hf collections list` β List collections on the Hub. `[--owner TEXT --item TEXT --sort [lastModified|trending|upvotes] --limit INTEGER --format [auto|human|agent|json|quiet]]`
|
| 60 |
+
- `hf collections update COLLECTION_SLUG` β Update a collection's metadata on the Hub. `[--title TEXT --description TEXT --position INTEGER --private --theme TEXT --format [auto|human|agent|json|quiet]]`
|
| 61 |
+
- `hf collections update-item COLLECTION_SLUG ITEM_OBJECT_ID` β Update an item in a collection. `[--note TEXT --position INTEGER --format [auto|human|agent|json|quiet]]`
|
| 62 |
+
|
| 63 |
+
### `hf datasets` β Interact with datasets on the Hub.
|
| 64 |
+
|
| 65 |
+
- `hf datasets card DATASET_ID` β Get the dataset card (README) for a dataset on the Hub. `[--metadata --text --format [auto|human|agent|json|quiet]]`
|
| 66 |
+
- `hf datasets info DATASET_ID` β Get info about a dataset on the Hub. `[--revision TEXT --expand TEXT --format [auto|human|agent|json|quiet]]`
|
| 67 |
+
- `hf datasets leaderboard DATASET_ID` β List model scores from a dataset leaderboard. This command helps find the best models for a task or compare models by benchmark scores. Use 'hf datasets ls --filter benchmark:official' to list available leaderboards. `[--limit INTEGER --format [auto|human|agent|json|quiet]]`
|
| 68 |
+
- `hf datasets list` β List datasets on the Hub, or files in a dataset repo. `[--search TEXT --author TEXT --filter TEXT --sort [created_at|downloads|last_modified|likes|trending_score] --limit INTEGER --expand TEXT --human-readable --tree --recursive --revision TEXT --format [auto|human|agent|json|quiet]]`
|
| 69 |
+
- `hf datasets parquet DATASET_ID` β List parquet file URLs available for a dataset. `[--subset TEXT --split TEXT --format [auto|human|agent|json|quiet]]`
|
| 70 |
+
- `hf datasets sql SQL` β Execute a raw SQL query with DuckDB against dataset parquet URLs. `[--format [auto|human|agent|json|quiet]]`
|
| 71 |
+
|
| 72 |
+
### `hf discussions` β Manage discussions and pull requests on the Hub.
|
| 73 |
+
|
| 74 |
+
- `hf discussions close REPO_ID NUM` β Close a discussion or pull request. `[--comment TEXT --yes --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 75 |
+
- `hf discussions comment REPO_ID NUM` β Comment on a discussion or pull request. `[--body TEXT --body-file PATH --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 76 |
+
- `hf discussions create REPO_ID --title TEXT` β Create a new discussion or pull request on a repo. `[--body TEXT --body-file PATH --pull-request --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 77 |
+
- `hf discussions diff REPO_ID NUM` β Show the diff of a pull request. `[--type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 78 |
+
- `hf discussions info REPO_ID NUM` β Get info about a discussion or pull request. `[--type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 79 |
+
- `hf discussions list REPO_ID` β List discussions and pull requests on a repo. `[--status [open|closed|merged|draft|all] --kind [all|discussion|pull_request] --author TEXT --limit INTEGER --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 80 |
+
- `hf discussions merge REPO_ID NUM` β Merge a pull request. `[--comment TEXT --yes --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 81 |
+
- `hf discussions rename REPO_ID NUM NEW_TITLE` β Rename a discussion or pull request. `[--type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 82 |
+
- `hf discussions reopen REPO_ID NUM` β Reopen a closed discussion or pull request. `[--comment TEXT --yes --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 83 |
+
|
| 84 |
+
### `hf endpoints` β Manage Hugging Face Inference Endpoints.
|
| 85 |
+
|
| 86 |
+
- `hf endpoints catalog deploy --repo TEXT` β Deploy an Inference Endpoint from the Model Catalog. `[--name TEXT --accelerator TEXT --namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 87 |
+
- `hf endpoints catalog list` β List available Catalog models. `[--format [auto|human|agent|json|quiet]]`
|
| 88 |
+
- `hf endpoints delete NAME` β Delete an Inference Endpoint permanently. `[--namespace TEXT --yes --format [auto|human|agent|json|quiet]]`
|
| 89 |
+
- `hf endpoints deploy NAME --repo TEXT --framework TEXT --accelerator TEXT --instance-size TEXT --instance-type TEXT --region TEXT --vendor TEXT` β Deploy an Inference Endpoint from a Hub repository. `[--namespace TEXT --task TEXT --min-replica INTEGER --max-replica INTEGER --scale-to-zero-timeout INTEGER --scaling-metric [pendingRequests|hardwareUsage] --scaling-threshold FLOAT --format [auto|human|agent|json|quiet]]`
|
| 90 |
+
- `hf endpoints describe NAME` β Get information about an existing endpoint. `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 91 |
+
- `hf endpoints list` β Lists all Inference Endpoints for the given namespace. `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 92 |
+
- `hf endpoints pause NAME` β Pause an Inference Endpoint. `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 93 |
+
- `hf endpoints resume NAME` β Resume an Inference Endpoint. `[--namespace TEXT --fail-if-already-running --format [auto|human|agent|json|quiet]]`
|
| 94 |
+
- `hf endpoints scale-to-zero NAME` β Scale an Inference Endpoint to zero. `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 95 |
+
- `hf endpoints update NAME` β Update an existing endpoint. `[--namespace TEXT --repo TEXT --accelerator TEXT --instance-size TEXT --instance-type TEXT --framework TEXT --revision TEXT --task TEXT --min-replica INTEGER --max-replica INTEGER --scale-to-zero-timeout INTEGER --scaling-metric [pendingRequests|hardwareUsage] --scaling-threshold FLOAT --format [auto|human|agent|json|quiet]]`
|
| 96 |
+
|
| 97 |
+
### `hf extensions` β Manage hf CLI extensions.
|
| 98 |
+
|
| 99 |
+
- `hf extensions exec NAME` β Execute an installed extension.
|
| 100 |
+
- `hf extensions install REPO_ID` β Install an extension from a public GitHub repository. `[--force --format [auto|human|agent|json|quiet]]`
|
| 101 |
+
- `hf extensions list` β List installed extension commands. `[--format [auto|human|agent|json|quiet]]`
|
| 102 |
+
- `hf extensions remove NAME` β Remove an installed extension. `[--format [auto|human|agent|json|quiet]]`
|
| 103 |
+
- `hf extensions search` β Search extensions available on GitHub (tagged with 'hf-extension' topic). `[--format [auto|human|agent|json|quiet]]`
|
| 104 |
+
|
| 105 |
+
### `hf jobs` β Run and manage Jobs on the Hub.
|
| 106 |
+
|
| 107 |
+
- `hf jobs cancel JOB_ID` β Cancel a Job `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 108 |
+
- `hf jobs hardware` β List available hardware options for Jobs `[--format [auto|human|agent|json|quiet]]`
|
| 109 |
+
- `hf jobs inspect JOB_IDS` β Display detailed information on one or more Jobs `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 110 |
+
- `hf jobs labels JOB_ID` β Update labels on a Job. Replaces all existing labels. `[--label TEXT --clear --namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 111 |
+
- `hf jobs logs JOB_ID` β Fetch the logs of a Job. `[--follow --tail INTEGER --namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 112 |
+
- `hf jobs ps` β List Jobs. `[--all --namespace TEXT --filter TEXT --format [auto|human|agent|json|quiet]]`
|
| 113 |
+
- `hf jobs run IMAGE COMMAND` β Run a Job. `[--env TEXT --secrets TEXT --label TEXT --volume TEXT --env-file TEXT --secrets-file TEXT --flavor [cpu-basic|cpu-upgrade|cpu-performance|cpu-xl|t4-small|t4-medium|l4x1|l4x4|l40sx1|l40sx4|l40sx8|a10g-small|a10g-large|a10g-largex2|a10g-largex4|a100-large|a100x4|a100x8|h200|h200x2|h200x4|h200x8|rtx-pro-6000|rtx-pro-6000x2|rtx-pro-6000x4|rtx-pro-6000x8] --timeout TEXT --detach --expose INTEGER --namespace TEXT]`
|
| 114 |
+
- `hf jobs scheduled delete SCHEDULED_JOB_ID` β Delete a scheduled Job. `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 115 |
+
- `hf jobs scheduled inspect SCHEDULED_JOB_IDS` β Display detailed information on one or more scheduled Jobs `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 116 |
+
- `hf jobs scheduled labels SCHEDULED_JOB_ID` β Update labels on a scheduled Job. Replaces all existing labels. `[--label TEXT --clear --namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 117 |
+
- `hf jobs scheduled ps` β List scheduled Jobs `[--all --namespace TEXT --filter TEXT --format [auto|human|agent|json|quiet]]`
|
| 118 |
+
- `hf jobs scheduled resume SCHEDULED_JOB_ID` β Resume (unpause) a scheduled Job. `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 119 |
+
- `hf jobs scheduled run SCHEDULE IMAGE COMMAND` β Schedule a Job. `[--suspend --concurrency --env TEXT --secrets TEXT --label TEXT --volume TEXT --env-file TEXT --secrets-file TEXT --flavor [cpu-basic|cpu-upgrade|cpu-performance|cpu-xl|t4-small|t4-medium|l4x1|l4x4|l40sx1|l40sx4|l40sx8|a10g-small|a10g-large|a10g-largex2|a10g-largex4|a100-large|a100x4|a100x8|h200|h200x2|h200x4|h200x8|rtx-pro-6000|rtx-pro-6000x2|rtx-pro-6000x4|rtx-pro-6000x8] --timeout TEXT --expose INTEGER --namespace TEXT]`
|
| 120 |
+
- `hf jobs scheduled suspend SCHEDULED_JOB_ID` β Suspend (pause) a scheduled Job. `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 121 |
+
- `hf jobs scheduled uv run SCHEDULE SCRIPT` β Run a UV script (local file or URL) on HF infrastructure `[--suspend --concurrency --image TEXT --flavor [cpu-basic|cpu-upgrade|cpu-performance|cpu-xl|t4-small|t4-medium|l4x1|l4x4|l40sx1|l40sx4|l40sx8|a10g-small|a10g-large|a10g-largex2|a10g-largex4|a100-large|a100x4|a100x8|h200|h200x2|h200x4|h200x8|rtx-pro-6000|rtx-pro-6000x2|rtx-pro-6000x4|rtx-pro-6000x8] --env TEXT --secrets TEXT --label TEXT --volume TEXT --env-file TEXT --secrets-file TEXT --timeout TEXT --expose INTEGER --namespace TEXT --with TEXT --python TEXT]`
|
| 122 |
+
- `hf jobs stats` β Fetch the resource usage statistics and metrics of Jobs `[--namespace TEXT --format [auto|human|agent|json|quiet]]`
|
| 123 |
+
- `hf jobs uv run SCRIPT` β Run a UV script (local file or URL) on HF infrastructure `[--image TEXT --flavor [cpu-basic|cpu-upgrade|cpu-performance|cpu-xl|t4-small|t4-medium|l4x1|l4x4|l40sx1|l40sx4|l40sx8|a10g-small|a10g-large|a10g-largex2|a10g-largex4|a100-large|a100x4|a100x8|h200|h200x2|h200x4|h200x8|rtx-pro-6000|rtx-pro-6000x2|rtx-pro-6000x4|rtx-pro-6000x8] --env TEXT --secrets TEXT --label TEXT --volume TEXT --env-file TEXT --secrets-file TEXT --timeout TEXT --detach --expose INTEGER --namespace TEXT --with TEXT --python TEXT]`
|
| 124 |
+
|
| 125 |
+
### `hf models` β Interact with models on the Hub.
|
| 126 |
+
|
| 127 |
+
- `hf models card MODEL_ID` β Get the model card (README) for a model on the Hub. `[--metadata --text --format [auto|human|agent|json|quiet]]`
|
| 128 |
+
- `hf models info MODEL_ID` β Get info about a model on the Hub. `[--revision TEXT --expand TEXT --format [auto|human|agent|json|quiet]]`
|
| 129 |
+
- `hf models list` β List models on the Hub, or files in a model repo. `[--search TEXT --author TEXT --filter TEXT --num-parameters TEXT --sort [created_at|downloads|last_modified|likes|trending_score] --limit INTEGER --expand TEXT --human-readable --tree --recursive --revision TEXT --format [auto|human|agent|json|quiet]]`
|
| 130 |
+
|
| 131 |
+
### `hf papers` β Interact with papers on the Hub.
|
| 132 |
+
|
| 133 |
+
- `hf papers info PAPER_ID` β Get info about a paper on the Hub. `[--format [auto|human|agent|json|quiet]]`
|
| 134 |
+
- `hf papers list` β List daily papers on the Hub. `[--date TEXT --week TEXT --month TEXT --submitter TEXT --sort [publishedAt|trending] --limit INTEGER --format [auto|human|agent|json|quiet]]`
|
| 135 |
+
- `hf papers read PAPER_ID` β Read a paper as markdown. `[--format [auto|human|agent|json|quiet]]`
|
| 136 |
+
- `hf papers search QUERY` β Search papers on the Hub. `[--limit INTEGER --format [auto|human|agent|json|quiet]]`
|
| 137 |
+
|
| 138 |
+
### `hf repos` β Manage repos on the Hub.
|
| 139 |
+
|
| 140 |
+
- `hf repos branch create REPO_ID BRANCH` β Create a new branch for a repo on the Hub. `[--revision TEXT --type [model|dataset|space] --exist-ok --format [auto|human|agent|json|quiet]]`
|
| 141 |
+
- `hf repos branch delete REPO_ID BRANCH` β Delete a branch from a repo on the Hub. `[--type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 142 |
+
- `hf repos cp SRC` β Copy files between local paths, repositories, and buckets. `[--format [auto|human|agent|json|quiet]]`
|
| 143 |
+
- `hf repos create REPO_ID` β Create a new repo on the Hub. `[--type [model|dataset|space] --space-sdk TEXT --private --public --protected --exist-ok --resource-group-id TEXT --region [us|eu] --flavor [cpu-basic|cpu-upgrade|zero-a10g|t4-small|t4-medium|l4x1|l4x4|l40sx1|l40sx4|l40sx8|a10g-small|a10g-large|a10g-largex2|a10g-largex4|a100-large|a100x4|a100x8] --storage [small|medium|large] --sleep-time INTEGER --secrets TEXT --secrets-file TEXT --env TEXT --env-file TEXT --volume TEXT --format [auto|human|agent|json|quiet]]`
|
| 144 |
+
- `hf repos delete REPO_ID` β Delete a repo from the Hub. This is an irreversible operation. `[--type [model|dataset|space] --missing-ok --yes --format [auto|human|agent|json|quiet]]`
|
| 145 |
+
- `hf repos delete-files REPO_ID PATTERNS` β Delete files from a repo on the Hub. `[--type [model|dataset|space] --revision TEXT --commit-message TEXT --commit-description TEXT --create-pr --format [auto|human|agent|json|quiet]]`
|
| 146 |
+
- `hf repos duplicate FROM_ID` β Duplicate a repo on the Hub (model, dataset, or Space). `[--type [model|dataset|space] --private --public --protected --exist-ok --flavor [cpu-basic|cpu-upgrade|zero-a10g|t4-small|t4-medium|l4x1|l4x4|l40sx1|l40sx4|l40sx8|a10g-small|a10g-large|a10g-largex2|a10g-largex4|a100-large|a100x4|a100x8] --storage [small|medium|large] --sleep-time INTEGER --secrets TEXT --secrets-file TEXT --env TEXT --env-file TEXT --volume TEXT --format [auto|human|agent|json|quiet]]`
|
| 147 |
+
- `hf repos list` β List all repos (models, datasets, spaces, buckets) with storage info. `[--namespace TEXT --type [model|dataset|space|bucket] --search TEXT --limit INTEGER --explore --format [auto|human|agent|json|quiet]]`
|
| 148 |
+
- `hf repos move FROM_ID TO_ID` β Move a repository from a namespace to another namespace. `[--type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 149 |
+
- `hf repos settings REPO_ID` β Update the settings of a repository. `[--gated [auto|manual|false] --private --public --protected --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 150 |
+
- `hf repos tag create REPO_ID TAG` β Create a tag for a repo. `[--message TEXT --revision TEXT --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 151 |
+
- `hf repos tag delete REPO_ID TAG` β Delete a tag for a repo. `[--yes --type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 152 |
+
- `hf repos tag list REPO_ID` β List tags for a repo. `[--type [model|dataset|space] --format [auto|human|agent|json|quiet]]`
|
| 153 |
+
|
| 154 |
+
### `hf skills` β Manage skills for AI assistants.
|
| 155 |
+
|
| 156 |
+
- `hf skills add` β Download a Hugging Face skill and install it for an AI assistant. `[--claude --global --dest PATH --force --format [auto|human|agent|json|quiet]]`
|
| 157 |
+
- `hf skills list` β List available skills from the Hugging Face marketplace. `[--format [auto|human|agent|json|quiet]]`
|
| 158 |
+
- `hf skills preview` β Print the generated `hf-cli` SKILL.md to stdout. `[--format [auto|human|agent|json|quiet]]`
|
| 159 |
+
- `hf skills update` β Update installed Hugging Face marketplace skills. `[--claude --global --dest PATH --format [auto|human|agent|json|quiet]]`
|
| 160 |
+
|
| 161 |
+
### `hf spaces` β Interact with spaces on the Hub.
|
| 162 |
+
|
| 163 |
+
- `hf spaces card SPACE_ID` β Get the Space card (README) for a Space on the Hub. `[--metadata --text --format [auto|human|agent|json|quiet]]`
|
| 164 |
+
- `hf spaces dev-mode SPACE_ID` β Enable or disable dev mode on a Space. `[--stop --format [auto|human|agent|json|quiet]]`
|
| 165 |
+
- `hf spaces hardware` β List available hardware options for Spaces. `[--format [auto|human|agent|json|quiet]]`
|
| 166 |
+
- `hf spaces hot-reload SPACE_ID` β Hot-reload any Python file of a Space without a full rebuild + restart. `[--local-file PATH --skip-checks --skip-summary --format [auto|human|agent|json|quiet]]`
|
| 167 |
+
- `hf spaces info SPACE_ID` β Get info about a space on the Hub. `[--revision TEXT --expand TEXT --format [auto|human|agent|json|quiet]]`
|
| 168 |
+
- `hf spaces list` β List spaces on the Hub, or files in a space repo. `[--search TEXT --author TEXT --filter TEXT --sort [created_at|last_modified|likes|trending_score] --limit INTEGER --expand TEXT --human-readable --tree --recursive --revision TEXT --format [auto|human|agent|json|quiet]]`
|
| 169 |
+
- `hf spaces logs SPACE_ID` β Fetch the run or build logs of a Space. `[--build --follow --tail INTEGER --format [auto|human|agent|json|quiet]]`
|
| 170 |
+
- `hf spaces pause SPACE_ID` β Pause a Space. `[--format [auto|human|agent|json|quiet]]`
|
| 171 |
+
- `hf spaces restart SPACE_ID` β Restart a Space. `[--factory-reboot --format [auto|human|agent|json|quiet]]`
|
| 172 |
+
- `hf spaces search QUERY` β Search spaces on the Hub using semantic search. `[--filter TEXT --sdk TEXT --include-non-running --description --limit INTEGER --format [auto|human|agent|json|quiet]]`
|
| 173 |
+
- `hf spaces secrets add SPACE_ID` β Add or update secrets for a Space. `[--secrets TEXT --secrets-file TEXT --format [auto|human|agent|json|quiet]]`
|
| 174 |
+
- `hf spaces secrets delete SPACE_ID KEY` β Remove a secret from a Space. `[--yes --format [auto|human|agent|json|quiet]]`
|
| 175 |
+
- `hf spaces secrets list SPACE_ID` β List secrets for a Space. Secret values are write-only and not returned. `[--format [auto|human|agent|json|quiet]]`
|
| 176 |
+
- `hf spaces settings SPACE_ID` β Update the settings of a Space. `[--sleep-time INTEGER --hardware [cpu-basic|cpu-upgrade|zero-a10g|t4-small|t4-medium|l4x1|l4x4|l40sx1|l40sx4|l40sx8|a10g-small|a10g-large|a10g-largex2|a10g-largex4|a100-large|a100x4|a100x8] --format [auto|human|agent|json|quiet]]`
|
| 177 |
+
- `hf spaces ssh SPACE_ID` β SSH into a Space's Dev Mode container. `[--identity-file PATH --dry-run --auto --format [auto|human|agent|json|quiet]]`
|
| 178 |
+
- `hf spaces variables add SPACE_ID` β Add or update environment variables for a Space. `[--env TEXT --env-file TEXT --format [auto|human|agent|json|quiet]]`
|
| 179 |
+
- `hf spaces variables delete SPACE_ID KEY` β Remove an environment variable from a Space. `[--yes --format [auto|human|agent|json|quiet]]`
|
| 180 |
+
- `hf spaces variables list SPACE_ID` β List environment variables for a Space. `[--format [auto|human|agent|json|quiet]]`
|
| 181 |
+
- `hf spaces volumes delete SPACE_ID` β Remove all volumes from a Space. `[--yes --format [auto|human|agent|json|quiet]]`
|
| 182 |
+
- `hf spaces volumes list SPACE_ID` β List volumes mounted in a Space. `[--format [auto|human|agent|json|quiet]]`
|
| 183 |
+
- `hf spaces volumes set SPACE_ID` β Set (replace) volumes for a Space. `[--volume TEXT --format [auto|human|agent|json|quiet]]`
|
| 184 |
+
|
| 185 |
+
### `hf webhooks` β Manage webhooks on the Hub.
|
| 186 |
+
|
| 187 |
+
- `hf webhooks create --watch TEXT` β Create a new webhook. `[--url TEXT --job-id TEXT --domain [repo|discussions] --secret TEXT --format [auto|human|agent|json|quiet]]`
|
| 188 |
+
- `hf webhooks delete WEBHOOK_ID` β Delete a webhook permanently. `[--yes --format [auto|human|agent|json|quiet]]`
|
| 189 |
+
- `hf webhooks disable WEBHOOK_ID` β Disable an active webhook. `[--format [auto|human|agent|json|quiet]]`
|
| 190 |
+
- `hf webhooks enable WEBHOOK_ID` β Enable a disabled webhook. `[--format [auto|human|agent|json|quiet]]`
|
| 191 |
+
- `hf webhooks info WEBHOOK_ID` β Show full details for a single webhook. `[--format [auto|human|agent|json|quiet]]`
|
| 192 |
+
- `hf webhooks list` β List all webhooks for the current user. `[--format [auto|human|agent|json|quiet]]`
|
| 193 |
+
- `hf webhooks update WEBHOOK_ID` β Update an existing webhook. Only provided options are changed. `[--url TEXT --watch TEXT --domain [repo|discussions] --secret TEXT --format [auto|human|agent|json|quiet]]`
|
| 194 |
+
|
| 195 |
+
## Common options
|
| 196 |
+
|
| 197 |
+
- `--format` β Output format: `--format json` (or `--json`) or `--format table` (default).
|
| 198 |
+
- `-q / --quiet` β Quiet output (one ID per line).
|
| 199 |
+
- `--revision` β Git revision id which can be a branch name, a tag, or a commit hash.
|
| 200 |
+
- `--token` β Use a User Access Token. Prefer setting `HF_TOKEN` env var instead of passing `--token`.
|
| 201 |
+
- `--type` β The type of repository (model, dataset, or space).
|
| 202 |
+
|
| 203 |
+
## Mounting repos as local filesystems
|
| 204 |
+
|
| 205 |
+
To mount Hub repositories or buckets as local filesystems β no download, no copy, no waiting β use `hf-mount`. Files are fetched on demand. GitHub: https://github.com/huggingface/hf-mount
|
| 206 |
+
|
| 207 |
+
Install: `curl -fsSL https://raw.githubusercontent.com/huggingface/hf-mount/main/install.sh | sh`
|
| 208 |
+
|
| 209 |
+
Some command examples:
|
| 210 |
+
- `hf-mount start repo openai-community/gpt2 /tmp/gpt2` β mount a repo (read-only)
|
| 211 |
+
- `hf-mount start --hf-token $HF_TOKEN bucket myuser/my-bucket /tmp/data` β mount a bucket (read-write)
|
| 212 |
+
- `hf-mount status` / `hf-mount stop /tmp/data` β list or unmount
|
| 213 |
+
|
| 214 |
+
## Tips
|
| 215 |
+
|
| 216 |
+
- Use `hf <command> --help` for full options, descriptions, usage, and real-world examples
|
| 217 |
+
- Authenticate with `HF_TOKEN` env var (recommended) or with `--token`
|
| 218 |
+
- Update the CLI with `hf update` (uses the correct command for the detected install method)
|
.agents/skills/hf-gradio/SKILL.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: hf-gradio
|
| 3 |
+
description: Use Gradio applications via API. Use when the user asks for to generate a prediction from a Gradio app on Hugging Face spaces or public URL. For example, "Generate an image using black-forest-labs/FLUX.2-dev".
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
## hf-gradio CLI Skill
|
| 7 |
+
|
| 8 |
+
The `hf-gradio` CLI `gradio` CLI includes `info` and `predict` commands for interacting with Gradio apps programmatically.
|
| 9 |
+
|
| 10 |
+
## Step 1 - Verify installation
|
| 11 |
+
|
| 12 |
+
Verify that either `hf-gradio` or `gradio` are installed in the current virtual environment.
|
| 13 |
+
|
| 14 |
+
If the `hf` CLI app is installed. The `hf-gradio` extension can be installed via
|
| 15 |
+
|
| 16 |
+
```bash
|
| 17 |
+
hf extensions install gradio-app/hf-gradio
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
### Step 2 - Use `info` to discover endpoints and payload format
|
| 21 |
+
|
| 22 |
+
```bash
|
| 23 |
+
gradio info <space_id_or_url>
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
```bash
|
| 27 |
+
hf-gradio info <space_id_or_url>
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
```bash
|
| 31 |
+
hf gradio info <space_id_or_url>
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
Returns a JSON payload describing all endpoints, their parameters (with types and defaults), and return values.
|
| 35 |
+
|
| 36 |
+
```bash
|
| 37 |
+
gradio info gradio/calculator
|
| 38 |
+
# {
|
| 39 |
+
# "/predict": {
|
| 40 |
+
# "parameters": [
|
| 41 |
+
# {"name": "num1", "required": true, "default": null, "type": {"type": "number"}},
|
| 42 |
+
# {"name": "operation", "required": true, "default": null, "type": {"enum": ["add", "subtract", "multiply", "divide"], "type": "string"}},
|
| 43 |
+
# {"name": "num2", "required": true, "default": null, "type": {"type": "number"}}
|
| 44 |
+
# ],
|
| 45 |
+
# "returns": [{"name": "output", "type": {"type": "number"}}],
|
| 46 |
+
# "description": ""
|
| 47 |
+
# }
|
| 48 |
+
# }
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
File-type parameters show `"type": "filepath"` with instructions to include `"meta": {"_type": "gradio.FileData"}` β this signals the file will be uploaded to the remote server.
|
| 52 |
+
|
| 53 |
+
## Step 3 - Use `predict` to generate the prediction
|
| 54 |
+
|
| 55 |
+
```bash
|
| 56 |
+
gradio predict <space_id_or_url> <endpoint> <json_payload>
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
```bash
|
| 60 |
+
hf-gradio predict <space_id_or_url> <endpoint> <json_payload>
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
```bash
|
| 64 |
+
hf gradio predict <space_id_or_url> <endpoint> <json_payload>
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
Returns a JSON object with named output keys.
|
| 68 |
+
|
| 69 |
+
```bash
|
| 70 |
+
# Simple numeric prediction
|
| 71 |
+
gradio predict gradio/calculator /predict '{"num1": 5, "operation": "multiply", "num2": 3}'
|
| 72 |
+
# {"output": 15}
|
| 73 |
+
|
| 74 |
+
# Image generation
|
| 75 |
+
gradio predict black-forest-labs/FLUX.2-dev /infer '{"prompt": "A majestic dragon"}'
|
| 76 |
+
# {"Result": "/tmp/gradio/.../image.webp", "Seed": 1117868604}
|
| 77 |
+
|
| 78 |
+
# File upload (must include meta key)
|
| 79 |
+
gradio predict gradio/image_mod /predict '{"image": {"path": "/path/to/image.png", "meta": {"_type": "gradio.FileData"}}}'
|
| 80 |
+
# {"output": "/tmp/gradio/.../output.png"}
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
Both commands accept `--token` for accessing private Spaces.
|
.agents/skills/hf-mem/SKILL.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: hf-mem
|
| 3 |
+
description: Hugging Face CLI to estimate the required memory to load Safetensors or GGUF model weights for inference from the Hugging Face Hub
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
`hf_mem` estimates the required memory for inference, including model weights and an optional KV cache, for Safetensors and GGUF for models on the Hugging Face Hub using HTTP Range requests i.e., without downloading or loading any weights locally.
|
| 7 |
+
|
| 8 |
+
## When to use?
|
| 9 |
+
|
| 10 |
+
- User asks how much VRAM or memory a model needs to run
|
| 11 |
+
- User wants to know if a model fits on their GPU or a given instance
|
| 12 |
+
- User references a Hugging Face model ID or URL and asks about inference requirements
|
| 13 |
+
|
| 14 |
+
## What are the requirements?
|
| 15 |
+
|
| 16 |
+
- `uv` installed (for `uvx`)
|
| 17 |
+
- `HF_TOKEN` env var or `--hf-token` flag (for gated or private models only)
|
| 18 |
+
|
| 19 |
+
## How to run?
|
| 20 |
+
|
| 21 |
+
Run with `--model-id` pointing to the Hugging Face Hub repository which will check that it either contains Safetensors (via `model.safetensors`, `model.safetensors.index.json` if sharded, or `model_index.json` for Diffusers) or GGUF model weights within.
|
| 22 |
+
|
| 23 |
+
```bash
|
| 24 |
+
uvx hf-mem --model-id <model-id> --json-output
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
If the repository contains GGUF model weights in multiple precisions / quantizations, the estimations will be on a per-file basis, whereas for inference you won't load all of those but rather only a single precision. This being said, for GGUF you might as well need to provide `--gguf-file` to target the specific file (or path if sharded) you want to run.
|
| 28 |
+
|
| 29 |
+
```bash
|
| 30 |
+
uvx hf-mem --model-id <model-id> --gguf-file <file-or-path> --json-output
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
Additionally, `hf-mem` comes with an `--experimental` flag that will also calculate the KV cache memory requirements too, useful for large-language models, meaning it applies to LLMs (`...ForCausalLM`), VLMs (`...ForConditionalGeneration`), and GGUF models.
|
| 34 |
+
|
| 35 |
+
As per the context window, it will be read from the default or overridden with `--max-model-len` a la vLLM. And, same goes for the KV cache precision, which will default to the model precision unless manually set via `--kv-cache-dtype` a la vLLM too.
|
| 36 |
+
|
| 37 |
+
For Safetensors use as:
|
| 38 |
+
|
| 39 |
+
```bash
|
| 40 |
+
uvx hf-mem --model-id <model-id> --experimental [--max-model-len N] [--batch-size N] [--kv-cache-dtype auto|bfloat16|fp8|fp8_ds_mla|fp8_e4m3|fp8_e5m2|fp8_inc] --json-output
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
And, for GGUF use as:
|
| 44 |
+
|
| 45 |
+
```bash
|
| 46 |
+
uvx hf-mem --model-id <model-id> --gguf-file <file-or-path> --experimental [--max-model-len N] [--batch-size N] [--kv-cache-dtype auto|F32|F16|Q4_0|Q4_1|Q5_0|Q5_1|Q8_0|Q8_1|Q2_K|Q3_K|Q4_K|Q5_K|Q6_K|Q8_K|IQ2_XXS|IQ2_XS|IQ3_XXS|IQ1_S|IQ4_NL|IQ3_S|IQ2_S|IQ4_XS|I8|I16|I32|I64|F64|IQ1_M|BF16|TQ1_0|TQ2_0|MXFP4] --json-output
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
## Examples
|
| 50 |
+
|
| 51 |
+
For Transformers with Safetensors weights:
|
| 52 |
+
|
| 53 |
+
```bash
|
| 54 |
+
uvx hf-mem --model-id MiniMaxAI/MiniMax-M2 --json-output
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
For Diffusers with Safetensors weights:
|
| 58 |
+
|
| 59 |
+
```bash
|
| 60 |
+
uvx hf-mem --model-id Qwen/Qwen-Image --json-output
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
For Sentence Transformers with Safetensors weights:
|
| 64 |
+
|
| 65 |
+
```bash
|
| 66 |
+
uvx hf-mem --model-id google/embeddinggemma-300m --json-output
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
With `--experimental` to include the KV cache estimation for LLMs and VLMs:
|
| 70 |
+
|
| 71 |
+
```bash
|
| 72 |
+
uvx hf-mem --model-id mistralai/Mistral-7B-v0.1 --experimental --json-output
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
And, for LLMs or VLMs with GGUF weights:
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
uvx hf-mem --model-id unsloth/Qwen3.5-397B-A17B-GGUF --gguf-file Q4_K_M --experimental --json-output
|
| 79 |
+
```
|
.agents/skills/huggingface-best/SKILL.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: huggingface-best
|
| 3 |
+
description: >
|
| 4 |
+
Use when the user asks about finding the best, top, or recommended model for a task,
|
| 5 |
+
wants to know what AI model to use, or wants to compare models by benchmark scores.
|
| 6 |
+
Triggers on: "best model for X", "what model should I use for", "top models for [task]",
|
| 7 |
+
"which model runs on my laptop/machine/device", "recommend a model for", "what LLM should
|
| 8 |
+
I use for", "compare models for", "what's state of the art for", or any question about
|
| 9 |
+
choosing an AI model for a specific use case. Always use this skill when the user wants
|
| 10 |
+
model recommendations or comparisons, even if they don't explicitly mention HuggingFace
|
| 11 |
+
or benchmarks.
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# HuggingFace Best Model Finder
|
| 15 |
+
|
| 16 |
+
Finds the best models for a task by querying official HF benchmark leaderboards, enriching
|
| 17 |
+
results with model size data, filtering for what fits on the user's device, and returning a
|
| 18 |
+
comparison table with benchmark scores.
|
| 19 |
+
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
## Step 1: Parse the request
|
| 23 |
+
|
| 24 |
+
Extract from the user's message:
|
| 25 |
+
- **Task**: what they want the model to do (coding, math/reasoning, chat, OCR, RAG/retrieval, speech recognition, image classification, multimodal, agents, etc.)
|
| 26 |
+
- **Device**: hardware constraints (MacBook M-series 8/16/32/64GB unified memory, RTX GPU with VRAM amount, CPU-only, cloud/no constraint, etc.)
|
| 27 |
+
|
| 28 |
+
If device is not mentioned, skip filtering entirely and return the highest-performing models regardless of size. If the task is genuinely ambiguous, ask one clarifying question.
|
| 29 |
+
|
| 30 |
+
### Device β max parameter budget
|
| 31 |
+
|
| 32 |
+
When a device is specified, extract its available memory (unified RAM for Apple Silicon, VRAM for discrete GPUs) and apply:
|
| 33 |
+
|
| 34 |
+
- **fp16 max params (B)** β memory (GB) Γ· 2
|
| 35 |
+
- **Q4 max params (B)** β memory (GB) Γ 2
|
| 36 |
+
|
| 37 |
+
Examples: 16GB β 8B fp16 / 32B Q4 β 24GB VRAM β 12B fp16 / 48B Q4 β 8GB β 4B fp16 / 16B Q4
|
| 38 |
+
|
| 39 |
+
---
|
| 40 |
+
|
| 41 |
+
## Step 2: Find relevant benchmark datasets
|
| 42 |
+
|
| 43 |
+
Fetch the full list of official HF benchmarks:
|
| 44 |
+
|
| 45 |
+
```bash
|
| 46 |
+
curl -s -H "Authorization: Bearer $(cat ~/.cache/huggingface/token)" \
|
| 47 |
+
"https://huggingface.co/api/datasets?filter=benchmark:official&limit=500" | jq '[.[] | {id, tags, description}]'
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
Read the returned list and select the datasets most relevant to the user's task β match on dataset id, tags, and description. Use your judgment; don't limit yourself to 2-3. Aim for comprehensive coverage: if 5 benchmarks clearly cover the task, use all 5.
|
| 51 |
+
|
| 52 |
+
---
|
| 53 |
+
|
| 54 |
+
## Step 3: Fetch top models from leaderboards
|
| 55 |
+
|
| 56 |
+
For each selected benchmark dataset:
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
curl -s -H "Authorization: Bearer $(cat ~/.cache/huggingface/token)" \
|
| 60 |
+
"https://huggingface.co/api/datasets/<namespace>/<repo>/leaderboard" | jq '[.[:15] | .[] | {rank, modelId, value, verified}]'
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
Collect model IDs and scores across all benchmarks. If a leaderboard returns an error (404, 401, etc.), skip it and note it in the output.
|
| 64 |
+
|
| 65 |
+
---
|
| 66 |
+
|
| 67 |
+
## Step 4: Enrich with model metadata
|
| 68 |
+
|
| 69 |
+
For the top 10-15 candidate model IDs, get model infos.
|
| 70 |
+
|
| 71 |
+
```bash
|
| 72 |
+
# REST API
|
| 73 |
+
curl -s -H "Authorization: Bearer $(cat ~/.cache/huggingface/token)" \
|
| 74 |
+
"https://huggingface.co/api/models/org/model1" | jq '{safetensors, tags, cardData}'
|
| 75 |
+
|
| 76 |
+
# CLI (hf-cli)
|
| 77 |
+
hf models info org/model1 --json | jq '{safetensors, tags, cardData}'
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
Extract from each response:
|
| 81 |
+
- **Parameters**: `safetensors.total` β convert to B (e.g., 7_241_748_480 β "7.2B")
|
| 82 |
+
- **License**: from model card tags (look for `license:apache-2.0`, `license:mit`, etc.)
|
| 83 |
+
- If `safetensors` is absent, parse size from the model name (look for "7b", "8b", "13b", "70b", "72b", etc.)
|
| 84 |
+
|
| 85 |
+
---
|
| 86 |
+
|
| 87 |
+
## Step 5: Filter and rank
|
| 88 |
+
|
| 89 |
+
**If a device was specified:**
|
| 90 |
+
1. Remove models exceeding the fp16 parameter budget for the device
|
| 91 |
+
2. Flag models that fit only with Q4 quantization (multiply budget by ~4 for Q4 capacity)
|
| 92 |
+
3. If a highly-ranked model is slightly over budget, keep it with a "needs Q4" note β don't silently drop it
|
| 93 |
+
|
| 94 |
+
**If no device was mentioned:** skip all size filtering β just rank by benchmark score.
|
| 95 |
+
|
| 96 |
+
Then: rank by benchmark score (descending), keep top 5-8 models.
|
| 97 |
+
|
| 98 |
+
Include proprietary models (GPT-4, Claude, Gemini) if they appear on leaderboards, but flag them as "API only / not self-hostable". If the user explicitly asked for local/open models only, exclude them.
|
| 99 |
+
|
| 100 |
+
---
|
| 101 |
+
|
| 102 |
+
## Step 6: Output
|
| 103 |
+
|
| 104 |
+
### Comparison table
|
| 105 |
+
|
| 106 |
+
```markdown
|
| 107 |
+
| # | Model | Params | [Benchmark 1] | [Benchmark 2] | License | On device |
|
| 108 |
+
|---|-------|--------|--------------|--------------|---------|-----------|
|
| 109 |
+
| β1 | [org/name](https://huggingface.co/org/name) | 7B | 85.2% | β | Apache 2.0 | Yes (fp16) |
|
| 110 |
+
| 2 | [org/name](https://huggingface.co/org/name) | 13B | 83.1% | 71.5% | MIT | Q4 only |
|
| 111 |
+
| 3 | [org/name](https://huggingface.co/org/name) | 70B | 90.0% | 81.0% | Llama | Too large |
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
- Link model names to `https://huggingface.co/<model_id>`
|
| 115 |
+
- Use `β` for benchmarks where the model wasn't evaluated
|
| 116 |
+
- Star the top recommended pick with β
|
| 117 |
+
- "On device" values: `Yes (fp16)`, `Q4 only`, `Too large`, `API only`
|
| 118 |
+
|
| 119 |
+
### Follow-up
|
| 120 |
+
|
| 121 |
+
After presenting the table, ask the user: "Would you like to run **[top recommended model]**?"
|
| 122 |
+
|
| 123 |
+
If they say yes, ask whether they'd prefer to:
|
| 124 |
+
- **Run locally** β ask about their device if not already known, then give appropriate setup instructions
|
| 125 |
+
- **Run on HF Jobs** β point them to the HF Jobs guide: https://huggingface.co/docs/huggingface_hub/en/guides/jobs
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
## Error handling
|
| 130 |
+
|
| 131 |
+
- **Leaderboard not found**: skip, note "leaderboard unavailable" in output
|
| 132 |
+
- **Model missing from hub_repo_details**: fall back to parsing size from model name
|
| 133 |
+
- **No benchmarks found for task**: use the curated fallback table above, or try `hub_repo_search` with `filters=["<task>"]` sorted by `trendingScore`
|
| 134 |
+
- **All leaderboards fail**: fall back to `hub_repo_search` for popular models tagged with the task, note that results are by popularity rather than benchmark score
|
.agents/skills/huggingface-community-evals/SKILL.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: huggingface-community-evals
|
| 3 |
+
description: Run evaluations for Hugging Face Hub models using inspect-ai and lighteval on local hardware. Use for backend selection, local GPU evals, and choosing between vLLM / Transformers / accelerate. Not for HF Jobs orchestration, model-card PRs, .eval_results publication, or community-evals automation.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Overview
|
| 7 |
+
|
| 8 |
+
This skill is for **running evaluations against models on the Hugging Face Hub on local hardware**.
|
| 9 |
+
|
| 10 |
+
It covers:
|
| 11 |
+
- `inspect-ai` with local inference
|
| 12 |
+
- `lighteval` with local inference
|
| 13 |
+
- choosing between `vllm`, Hugging Face Transformers, and `accelerate`
|
| 14 |
+
- smoke tests, task selection, and backend fallback strategy
|
| 15 |
+
|
| 16 |
+
It does **not** cover:
|
| 17 |
+
- Hugging Face Jobs orchestration
|
| 18 |
+
- model-card or `model-index` edits
|
| 19 |
+
- README table extraction
|
| 20 |
+
- Artificial Analysis imports
|
| 21 |
+
- `.eval_results` generation or publishing
|
| 22 |
+
- PR creation or community-evals automation
|
| 23 |
+
|
| 24 |
+
If the user wants to **run the same eval remotely on Hugging Face Jobs**, hand off to the `hugging-face-jobs` skill and pass it one of the local scripts in this skill.
|
| 25 |
+
|
| 26 |
+
If the user wants to **publish results into the community evals workflow**, stop after generating the evaluation run and hand off that publishing step to `~/code/community-evals`.
|
| 27 |
+
|
| 28 |
+
> All paths below are relative to the directory containing this `SKILL.md`.
|
| 29 |
+
|
| 30 |
+
# When To Use Which Script
|
| 31 |
+
|
| 32 |
+
| Use case | Script |
|
| 33 |
+
|---|---|
|
| 34 |
+
| Local `inspect-ai` eval on a Hub model via inference providers | `scripts/inspect_eval_uv.py` |
|
| 35 |
+
| Local GPU eval with `inspect-ai` using `vllm` or Transformers | `scripts/inspect_vllm_uv.py` |
|
| 36 |
+
| Local GPU eval with `lighteval` using `vllm` or `accelerate` | `scripts/lighteval_vllm_uv.py` |
|
| 37 |
+
| Extra command patterns | `examples/USAGE_EXAMPLES.md` |
|
| 38 |
+
|
| 39 |
+
# Prerequisites
|
| 40 |
+
|
| 41 |
+
- Prefer `uv run` for local execution.
|
| 42 |
+
- Set `HF_TOKEN` for gated/private models.
|
| 43 |
+
- For local GPU runs, verify GPU access before starting:
|
| 44 |
+
|
| 45 |
+
```bash
|
| 46 |
+
uv --version
|
| 47 |
+
printenv HF_TOKEN >/dev/null
|
| 48 |
+
nvidia-smi
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
If `nvidia-smi` is unavailable, either:
|
| 52 |
+
- use `scripts/inspect_eval_uv.py` for lighter provider-backed evaluation, or
|
| 53 |
+
- hand off to the `hugging-face-jobs` skill if the user wants remote compute.
|
| 54 |
+
|
| 55 |
+
# Core Workflow
|
| 56 |
+
|
| 57 |
+
1. Choose the evaluation framework.
|
| 58 |
+
- Use `inspect-ai` when you want explicit task control and inspect-native flows.
|
| 59 |
+
- Use `lighteval` when the benchmark is naturally expressed as a lighteval task string, especially leaderboard-style tasks.
|
| 60 |
+
2. Choose the inference backend.
|
| 61 |
+
- Prefer `vllm` for throughput on supported architectures.
|
| 62 |
+
- Use Hugging Face Transformers (`--backend hf`) or `accelerate` as compatibility fallbacks.
|
| 63 |
+
3. Start with a smoke test.
|
| 64 |
+
- `inspect-ai`: add `--limit 10` or similar.
|
| 65 |
+
- `lighteval`: add `--max-samples 10`.
|
| 66 |
+
4. Scale up only after the smoke test passes.
|
| 67 |
+
5. If the user wants remote execution, hand off to `hugging-face-jobs` with the same script + args.
|
| 68 |
+
|
| 69 |
+
# Quick Start
|
| 70 |
+
|
| 71 |
+
## Option A: inspect-ai with local inference providers path
|
| 72 |
+
|
| 73 |
+
Best when the model is already supported by Hugging Face Inference Providers and you want the lowest local setup overhead.
|
| 74 |
+
|
| 75 |
+
```bash
|
| 76 |
+
uv run scripts/inspect_eval_uv.py \
|
| 77 |
+
--model meta-llama/Llama-3.2-1B \
|
| 78 |
+
--task mmlu \
|
| 79 |
+
--limit 20
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
Use this path when:
|
| 83 |
+
- you want a quick local smoke test
|
| 84 |
+
- you do not need direct GPU control
|
| 85 |
+
- the task already exists in `inspect-evals`
|
| 86 |
+
|
| 87 |
+
## Option B: inspect-ai on Local GPU
|
| 88 |
+
|
| 89 |
+
Best when you need to load the Hub model directly, use `vllm`, or fall back to Transformers for unsupported architectures.
|
| 90 |
+
|
| 91 |
+
Local GPU:
|
| 92 |
+
|
| 93 |
+
```bash
|
| 94 |
+
uv run scripts/inspect_vllm_uv.py \
|
| 95 |
+
--model meta-llama/Llama-3.2-1B \
|
| 96 |
+
--task gsm8k \
|
| 97 |
+
--limit 20
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
Transformers fallback:
|
| 101 |
+
|
| 102 |
+
```bash
|
| 103 |
+
uv run scripts/inspect_vllm_uv.py \
|
| 104 |
+
--model microsoft/phi-2 \
|
| 105 |
+
--task mmlu \
|
| 106 |
+
--backend hf \
|
| 107 |
+
--trust-remote-code \
|
| 108 |
+
--limit 20
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
## Option C: lighteval on Local GPU
|
| 112 |
+
|
| 113 |
+
Best when the task is naturally expressed as a `lighteval` task string, especially Open LLM Leaderboard style benchmarks.
|
| 114 |
+
|
| 115 |
+
Local GPU:
|
| 116 |
+
|
| 117 |
+
```bash
|
| 118 |
+
uv run scripts/lighteval_vllm_uv.py \
|
| 119 |
+
--model meta-llama/Llama-3.2-3B-Instruct \
|
| 120 |
+
--tasks "leaderboard|mmlu|5,leaderboard|gsm8k|5" \
|
| 121 |
+
--max-samples 20 \
|
| 122 |
+
--use-chat-template
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
`accelerate` fallback:
|
| 126 |
+
|
| 127 |
+
```bash
|
| 128 |
+
uv run scripts/lighteval_vllm_uv.py \
|
| 129 |
+
--model microsoft/phi-2 \
|
| 130 |
+
--tasks "leaderboard|mmlu|5" \
|
| 131 |
+
--backend accelerate \
|
| 132 |
+
--trust-remote-code \
|
| 133 |
+
--max-samples 20
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
# Remote Execution Boundary
|
| 137 |
+
|
| 138 |
+
This skill intentionally stops at **local execution and backend selection**.
|
| 139 |
+
|
| 140 |
+
If the user wants to:
|
| 141 |
+
- run these scripts on Hugging Face Jobs
|
| 142 |
+
- pick remote hardware
|
| 143 |
+
- pass secrets to remote jobs
|
| 144 |
+
- schedule recurring runs
|
| 145 |
+
- inspect / cancel / monitor jobs
|
| 146 |
+
|
| 147 |
+
then switch to the **`hugging-face-jobs`** skill and pass it one of these scripts plus the chosen arguments.
|
| 148 |
+
|
| 149 |
+
# Task Selection
|
| 150 |
+
|
| 151 |
+
`inspect-ai` examples:
|
| 152 |
+
- `mmlu`
|
| 153 |
+
- `gsm8k`
|
| 154 |
+
- `hellaswag`
|
| 155 |
+
- `arc_challenge`
|
| 156 |
+
- `truthfulqa`
|
| 157 |
+
- `winogrande`
|
| 158 |
+
- `humaneval`
|
| 159 |
+
|
| 160 |
+
`lighteval` task strings use `suite|task|num_fewshot`:
|
| 161 |
+
- `leaderboard|mmlu|5`
|
| 162 |
+
- `leaderboard|gsm8k|5`
|
| 163 |
+
- `leaderboard|arc_challenge|25`
|
| 164 |
+
- `lighteval|hellaswag|0`
|
| 165 |
+
|
| 166 |
+
Multiple `lighteval` tasks can be comma-separated in `--tasks`.
|
| 167 |
+
|
| 168 |
+
# Backend Selection
|
| 169 |
+
|
| 170 |
+
- Prefer `inspect_vllm_uv.py --backend vllm` for fast GPU inference on supported architectures.
|
| 171 |
+
- Use `inspect_vllm_uv.py --backend hf` when `vllm` does not support the model.
|
| 172 |
+
- Prefer `lighteval_vllm_uv.py --backend vllm` for throughput on supported models.
|
| 173 |
+
- Use `lighteval_vllm_uv.py --backend accelerate` as the compatibility fallback.
|
| 174 |
+
- Use `inspect_eval_uv.py` when Inference Providers already cover the model and you do not need direct GPU control.
|
| 175 |
+
|
| 176 |
+
# Hardware Guidance
|
| 177 |
+
|
| 178 |
+
| Model size | Suggested local hardware |
|
| 179 |
+
|---|---|
|
| 180 |
+
| `< 3B` | consumer GPU / Apple Silicon / small dev GPU |
|
| 181 |
+
| `3B - 13B` | stronger local GPU |
|
| 182 |
+
| `13B+` | high-memory local GPU or hand off to `hugging-face-jobs` |
|
| 183 |
+
|
| 184 |
+
For smoke tests, prefer cheaper local runs plus `--limit` or `--max-samples`.
|
| 185 |
+
|
| 186 |
+
# Troubleshooting
|
| 187 |
+
|
| 188 |
+
- CUDA or vLLM OOM:
|
| 189 |
+
- reduce `--batch-size`
|
| 190 |
+
- reduce `--gpu-memory-utilization`
|
| 191 |
+
- switch to a smaller model for the smoke test
|
| 192 |
+
- if necessary, hand off to `hugging-face-jobs`
|
| 193 |
+
- Model unsupported by `vllm`:
|
| 194 |
+
- switch to `--backend hf` for `inspect-ai`
|
| 195 |
+
- switch to `--backend accelerate` for `lighteval`
|
| 196 |
+
- Gated/private repo access fails:
|
| 197 |
+
- verify `HF_TOKEN`
|
| 198 |
+
- Custom model code required:
|
| 199 |
+
- add `--trust-remote-code`
|
| 200 |
+
|
| 201 |
+
# Examples
|
| 202 |
+
|
| 203 |
+
See:
|
| 204 |
+
- `examples/USAGE_EXAMPLES.md` for local command patterns
|
| 205 |
+
- `scripts/inspect_eval_uv.py`
|
| 206 |
+
- `scripts/inspect_vllm_uv.py`
|
| 207 |
+
- `scripts/lighteval_vllm_uv.py`
|
.agents/skills/huggingface-community-evals/examples/.env.example
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face Token (required for gated/private models)
|
| 2 |
+
# Get your token at: https://huggingface.co/settings/tokens
|
| 3 |
+
HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
.agents/skills/huggingface-community-evals/examples/USAGE_EXAMPLES.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Usage Examples
|
| 2 |
+
|
| 3 |
+
This document provides practical examples for **running evaluations locally** against Hugging Face Hub models.
|
| 4 |
+
|
| 5 |
+
## What this skill covers
|
| 6 |
+
|
| 7 |
+
- `inspect-ai` local runs
|
| 8 |
+
- `inspect-ai` with `vllm` or Transformers backends
|
| 9 |
+
- `lighteval` local runs with `vllm` or `accelerate`
|
| 10 |
+
- smoke tests and backend fallback patterns
|
| 11 |
+
|
| 12 |
+
## What this skill does NOT cover
|
| 13 |
+
|
| 14 |
+
- `model-index`
|
| 15 |
+
- `.eval_results`
|
| 16 |
+
- community eval publication workflows
|
| 17 |
+
- model-card PR creation
|
| 18 |
+
- Hugging Face Jobs orchestration
|
| 19 |
+
|
| 20 |
+
If you want to run these same scripts remotely, use the `hugging-face-jobs` skill and pass one of the scripts in `scripts/`.
|
| 21 |
+
|
| 22 |
+
## Setup
|
| 23 |
+
|
| 24 |
+
```bash
|
| 25 |
+
cd skills/hugging-face-evaluation
|
| 26 |
+
export HF_TOKEN=hf_xxx
|
| 27 |
+
uv --version
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
For local GPU runs:
|
| 31 |
+
|
| 32 |
+
```bash
|
| 33 |
+
nvidia-smi
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
## inspect-ai examples
|
| 37 |
+
|
| 38 |
+
### Quick smoke test
|
| 39 |
+
|
| 40 |
+
```bash
|
| 41 |
+
uv run scripts/inspect_eval_uv.py \
|
| 42 |
+
--model meta-llama/Llama-3.2-1B \
|
| 43 |
+
--task mmlu \
|
| 44 |
+
--limit 10
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
### Local GPU with vLLM
|
| 48 |
+
|
| 49 |
+
```bash
|
| 50 |
+
uv run scripts/inspect_vllm_uv.py \
|
| 51 |
+
--model meta-llama/Llama-3.2-8B-Instruct \
|
| 52 |
+
--task gsm8k \
|
| 53 |
+
--limit 20
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
### Transformers fallback
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
uv run scripts/inspect_vllm_uv.py \
|
| 60 |
+
--model microsoft/phi-2 \
|
| 61 |
+
--task mmlu \
|
| 62 |
+
--backend hf \
|
| 63 |
+
--trust-remote-code \
|
| 64 |
+
--limit 20
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
## lighteval examples
|
| 68 |
+
|
| 69 |
+
### Single task
|
| 70 |
+
|
| 71 |
+
```bash
|
| 72 |
+
uv run scripts/lighteval_vllm_uv.py \
|
| 73 |
+
--model meta-llama/Llama-3.2-3B-Instruct \
|
| 74 |
+
--tasks "leaderboard|mmlu|5" \
|
| 75 |
+
--max-samples 20
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
### Multiple tasks
|
| 79 |
+
|
| 80 |
+
```bash
|
| 81 |
+
uv run scripts/lighteval_vllm_uv.py \
|
| 82 |
+
--model meta-llama/Llama-3.2-3B-Instruct \
|
| 83 |
+
--tasks "leaderboard|mmlu|5,leaderboard|gsm8k|5" \
|
| 84 |
+
--max-samples 20 \
|
| 85 |
+
--use-chat-template
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
### accelerate fallback
|
| 89 |
+
|
| 90 |
+
```bash
|
| 91 |
+
uv run scripts/lighteval_vllm_uv.py \
|
| 92 |
+
--model microsoft/phi-2 \
|
| 93 |
+
--tasks "leaderboard|mmlu|5" \
|
| 94 |
+
--backend accelerate \
|
| 95 |
+
--trust-remote-code \
|
| 96 |
+
--max-samples 20
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
## Hand-off to Hugging Face Jobs
|
| 100 |
+
|
| 101 |
+
When local hardware is not enough, switch to the `hugging-face-jobs` skill and run one of these scripts remotely. Keep the script path and args; move the orchestration there.
|
.agents/skills/huggingface-community-evals/scripts/inspect_eval_uv.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# /// script
|
| 2 |
+
# requires-python = ">=3.10"
|
| 3 |
+
# dependencies = [
|
| 4 |
+
# "inspect-ai>=0.3.0",
|
| 5 |
+
# "inspect-evals",
|
| 6 |
+
# "openai",
|
| 7 |
+
# ]
|
| 8 |
+
# ///
|
| 9 |
+
|
| 10 |
+
"""
|
| 11 |
+
Entry point script for running inspect-ai evaluations against Hugging Face inference providers.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import argparse
|
| 17 |
+
import os
|
| 18 |
+
import subprocess
|
| 19 |
+
import sys
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
from typing import Optional
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _inspect_evals_tasks_root() -> Optional[Path]:
|
| 25 |
+
"""Return the installed inspect_evals package path if available."""
|
| 26 |
+
try:
|
| 27 |
+
import inspect_evals
|
| 28 |
+
|
| 29 |
+
return Path(inspect_evals.__file__).parent
|
| 30 |
+
except Exception:
|
| 31 |
+
return None
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _normalize_task(task: str) -> str:
|
| 35 |
+
"""Allow lighteval-style `suite|task|shots` strings by keeping the task name."""
|
| 36 |
+
if "|" in task:
|
| 37 |
+
parts = task.split("|")
|
| 38 |
+
if len(parts) >= 2 and parts[1]:
|
| 39 |
+
return parts[1]
|
| 40 |
+
return task
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def main() -> None:
|
| 44 |
+
parser = argparse.ArgumentParser(description="Inspect-ai job runner")
|
| 45 |
+
parser.add_argument("--model", required=True, help="Model ID on Hugging Face Hub")
|
| 46 |
+
parser.add_argument("--task", required=True, help="inspect-ai task to execute")
|
| 47 |
+
parser.add_argument("--limit", type=int, default=None, help="Limit number of samples to evaluate")
|
| 48 |
+
parser.add_argument(
|
| 49 |
+
"--tasks-root",
|
| 50 |
+
default=None,
|
| 51 |
+
help="Optional path to inspect task files. Defaults to the installed inspect_evals package.",
|
| 52 |
+
)
|
| 53 |
+
parser.add_argument(
|
| 54 |
+
"--sandbox",
|
| 55 |
+
default="local",
|
| 56 |
+
help="Sandbox backend to use (default: local for HF jobs without Docker).",
|
| 57 |
+
)
|
| 58 |
+
args = parser.parse_args()
|
| 59 |
+
|
| 60 |
+
# Ensure downstream libraries can read the token passed as a secret
|
| 61 |
+
hf_token = os.getenv("HF_TOKEN")
|
| 62 |
+
if hf_token:
|
| 63 |
+
os.environ.setdefault("HUGGING_FACE_HUB_TOKEN", hf_token)
|
| 64 |
+
os.environ.setdefault("HF_HUB_TOKEN", hf_token)
|
| 65 |
+
|
| 66 |
+
task = _normalize_task(args.task)
|
| 67 |
+
tasks_root = Path(args.tasks_root) if args.tasks_root else _inspect_evals_tasks_root()
|
| 68 |
+
if tasks_root and not tasks_root.exists():
|
| 69 |
+
tasks_root = None
|
| 70 |
+
|
| 71 |
+
cmd = [
|
| 72 |
+
"inspect",
|
| 73 |
+
"eval",
|
| 74 |
+
task,
|
| 75 |
+
"--model",
|
| 76 |
+
f"hf-inference-providers/{args.model}",
|
| 77 |
+
"--log-level",
|
| 78 |
+
"info",
|
| 79 |
+
# Reduce batch size to avoid OOM errors (default is 32)
|
| 80 |
+
"--max-connections",
|
| 81 |
+
"1",
|
| 82 |
+
# Set a small positive temperature (HF doesn't allow temperature=0)
|
| 83 |
+
"--temperature",
|
| 84 |
+
"0.001",
|
| 85 |
+
]
|
| 86 |
+
|
| 87 |
+
if args.sandbox:
|
| 88 |
+
cmd.extend(["--sandbox", args.sandbox])
|
| 89 |
+
|
| 90 |
+
if args.limit:
|
| 91 |
+
cmd.extend(["--limit", str(args.limit)])
|
| 92 |
+
|
| 93 |
+
try:
|
| 94 |
+
subprocess.run(cmd, check=True, cwd=tasks_root)
|
| 95 |
+
print("Evaluation complete.")
|
| 96 |
+
except subprocess.CalledProcessError as exc:
|
| 97 |
+
location = f" (cwd={tasks_root})" if tasks_root else ""
|
| 98 |
+
print(f"Evaluation failed with exit code {exc.returncode}{location}", file=sys.stderr)
|
| 99 |
+
raise
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
if __name__ == "__main__":
|
| 103 |
+
main()
|
| 104 |
+
|
.agents/skills/huggingface-community-evals/scripts/inspect_vllm_uv.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# /// script
|
| 2 |
+
# requires-python = ">=3.10"
|
| 3 |
+
# dependencies = [
|
| 4 |
+
# "inspect-ai>=0.3.0",
|
| 5 |
+
# "inspect-evals",
|
| 6 |
+
# "vllm>=0.4.0",
|
| 7 |
+
# "torch>=2.0.0",
|
| 8 |
+
# "transformers>=4.40.0",
|
| 9 |
+
# ]
|
| 10 |
+
# ///
|
| 11 |
+
|
| 12 |
+
"""
|
| 13 |
+
Entry point script for running inspect-ai evaluations with vLLM or HuggingFace Transformers backend.
|
| 14 |
+
|
| 15 |
+
This script runs evaluations on custom HuggingFace models using local GPU inference,
|
| 16 |
+
separate from inference provider scripts (which use external APIs).
|
| 17 |
+
|
| 18 |
+
Usage (standalone):
|
| 19 |
+
uv run scripts/inspect_vllm_uv.py --model "meta-llama/Llama-3.2-1B" --task "mmlu"
|
| 20 |
+
|
| 21 |
+
Model backends:
|
| 22 |
+
- vllm: Fast inference with vLLM (recommended for large models)
|
| 23 |
+
- hf: HuggingFace Transformers backend (broader model compatibility)
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
from __future__ import annotations
|
| 27 |
+
|
| 28 |
+
import argparse
|
| 29 |
+
import os
|
| 30 |
+
import subprocess
|
| 31 |
+
import sys
|
| 32 |
+
from typing import Optional
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def setup_environment() -> None:
|
| 36 |
+
"""Configure environment variables for HuggingFace authentication."""
|
| 37 |
+
hf_token = os.getenv("HF_TOKEN")
|
| 38 |
+
if hf_token:
|
| 39 |
+
os.environ.setdefault("HUGGING_FACE_HUB_TOKEN", hf_token)
|
| 40 |
+
os.environ.setdefault("HF_HUB_TOKEN", hf_token)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def run_inspect_vllm(
|
| 44 |
+
model_id: str,
|
| 45 |
+
task: str,
|
| 46 |
+
limit: Optional[int] = None,
|
| 47 |
+
max_connections: int = 4,
|
| 48 |
+
temperature: float = 0.0,
|
| 49 |
+
tensor_parallel_size: int = 1,
|
| 50 |
+
gpu_memory_utilization: float = 0.8,
|
| 51 |
+
dtype: str = "auto",
|
| 52 |
+
trust_remote_code: bool = False,
|
| 53 |
+
log_level: str = "info",
|
| 54 |
+
) -> None:
|
| 55 |
+
"""
|
| 56 |
+
Run inspect-ai evaluation with vLLM backend.
|
| 57 |
+
|
| 58 |
+
Args:
|
| 59 |
+
model_id: HuggingFace model ID
|
| 60 |
+
task: inspect-ai task to execute (e.g., "mmlu", "gsm8k")
|
| 61 |
+
limit: Limit number of samples to evaluate
|
| 62 |
+
max_connections: Maximum concurrent connections
|
| 63 |
+
temperature: Sampling temperature
|
| 64 |
+
tensor_parallel_size: Number of GPUs for tensor parallelism
|
| 65 |
+
gpu_memory_utilization: GPU memory fraction
|
| 66 |
+
dtype: Data type (auto, float16, bfloat16)
|
| 67 |
+
trust_remote_code: Allow remote code execution
|
| 68 |
+
log_level: Logging level
|
| 69 |
+
"""
|
| 70 |
+
setup_environment()
|
| 71 |
+
|
| 72 |
+
model_spec = f"vllm/{model_id}"
|
| 73 |
+
cmd = [
|
| 74 |
+
"inspect",
|
| 75 |
+
"eval",
|
| 76 |
+
task,
|
| 77 |
+
"--model",
|
| 78 |
+
model_spec,
|
| 79 |
+
"--log-level",
|
| 80 |
+
log_level,
|
| 81 |
+
"--max-connections",
|
| 82 |
+
str(max_connections),
|
| 83 |
+
]
|
| 84 |
+
|
| 85 |
+
# vLLM supports temperature=0 unlike HF inference providers
|
| 86 |
+
cmd.extend(["--temperature", str(temperature)])
|
| 87 |
+
|
| 88 |
+
# Older inspect-ai CLI versions do not support --model-args; rely on defaults
|
| 89 |
+
# and let vLLM choose sensible settings for small models.
|
| 90 |
+
if tensor_parallel_size != 1:
|
| 91 |
+
cmd.extend(["--tensor-parallel-size", str(tensor_parallel_size)])
|
| 92 |
+
if gpu_memory_utilization != 0.8:
|
| 93 |
+
cmd.extend(["--gpu-memory-utilization", str(gpu_memory_utilization)])
|
| 94 |
+
if dtype != "auto":
|
| 95 |
+
cmd.extend(["--dtype", dtype])
|
| 96 |
+
if trust_remote_code:
|
| 97 |
+
cmd.append("--trust-remote-code")
|
| 98 |
+
|
| 99 |
+
if limit:
|
| 100 |
+
cmd.extend(["--limit", str(limit)])
|
| 101 |
+
|
| 102 |
+
print(f"Running: {' '.join(cmd)}")
|
| 103 |
+
|
| 104 |
+
try:
|
| 105 |
+
subprocess.run(cmd, check=True)
|
| 106 |
+
print("Evaluation complete.")
|
| 107 |
+
except subprocess.CalledProcessError as exc:
|
| 108 |
+
print(f"Evaluation failed with exit code {exc.returncode}", file=sys.stderr)
|
| 109 |
+
sys.exit(exc.returncode)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def run_inspect_hf(
|
| 113 |
+
model_id: str,
|
| 114 |
+
task: str,
|
| 115 |
+
limit: Optional[int] = None,
|
| 116 |
+
max_connections: int = 1,
|
| 117 |
+
temperature: float = 0.001,
|
| 118 |
+
device: str = "auto",
|
| 119 |
+
dtype: str = "auto",
|
| 120 |
+
trust_remote_code: bool = False,
|
| 121 |
+
log_level: str = "info",
|
| 122 |
+
) -> None:
|
| 123 |
+
"""
|
| 124 |
+
Run inspect-ai evaluation with HuggingFace Transformers backend.
|
| 125 |
+
|
| 126 |
+
Use this when vLLM doesn't support the model architecture.
|
| 127 |
+
|
| 128 |
+
Args:
|
| 129 |
+
model_id: HuggingFace model ID
|
| 130 |
+
task: inspect-ai task to execute
|
| 131 |
+
limit: Limit number of samples
|
| 132 |
+
max_connections: Maximum concurrent connections (keep low for memory)
|
| 133 |
+
temperature: Sampling temperature
|
| 134 |
+
device: Device to use (auto, cuda, cpu)
|
| 135 |
+
dtype: Data type
|
| 136 |
+
trust_remote_code: Allow remote code execution
|
| 137 |
+
log_level: Logging level
|
| 138 |
+
"""
|
| 139 |
+
setup_environment()
|
| 140 |
+
|
| 141 |
+
model_spec = f"hf/{model_id}"
|
| 142 |
+
|
| 143 |
+
cmd = [
|
| 144 |
+
"inspect",
|
| 145 |
+
"eval",
|
| 146 |
+
task,
|
| 147 |
+
"--model",
|
| 148 |
+
model_spec,
|
| 149 |
+
"--log-level",
|
| 150 |
+
log_level,
|
| 151 |
+
"--max-connections",
|
| 152 |
+
str(max_connections),
|
| 153 |
+
"--temperature",
|
| 154 |
+
str(temperature),
|
| 155 |
+
]
|
| 156 |
+
|
| 157 |
+
if device != "auto":
|
| 158 |
+
cmd.extend(["--device", device])
|
| 159 |
+
if dtype != "auto":
|
| 160 |
+
cmd.extend(["--dtype", dtype])
|
| 161 |
+
if trust_remote_code:
|
| 162 |
+
cmd.append("--trust-remote-code")
|
| 163 |
+
|
| 164 |
+
if limit:
|
| 165 |
+
cmd.extend(["--limit", str(limit)])
|
| 166 |
+
|
| 167 |
+
print(f"Running: {' '.join(cmd)}")
|
| 168 |
+
|
| 169 |
+
try:
|
| 170 |
+
subprocess.run(cmd, check=True)
|
| 171 |
+
print("Evaluation complete.")
|
| 172 |
+
except subprocess.CalledProcessError as exc:
|
| 173 |
+
print(f"Evaluation failed with exit code {exc.returncode}", file=sys.stderr)
|
| 174 |
+
sys.exit(exc.returncode)
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def main() -> None:
|
| 178 |
+
parser = argparse.ArgumentParser(
|
| 179 |
+
description="Run inspect-ai evaluations with vLLM or HuggingFace Transformers on custom models",
|
| 180 |
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
| 181 |
+
epilog="""
|
| 182 |
+
Examples:
|
| 183 |
+
# Run MMLU with vLLM backend
|
| 184 |
+
uv run scripts/inspect_vllm_uv.py --model meta-llama/Llama-3.2-1B --task mmlu
|
| 185 |
+
|
| 186 |
+
# Run with HuggingFace Transformers backend
|
| 187 |
+
uv run scripts/inspect_vllm_uv.py --model meta-llama/Llama-3.2-1B --task mmlu --backend hf
|
| 188 |
+
|
| 189 |
+
# Run with limited samples for testing
|
| 190 |
+
uv run scripts/inspect_vllm_uv.py --model meta-llama/Llama-3.2-1B --task mmlu --limit 10
|
| 191 |
+
|
| 192 |
+
# Run on multiple GPUs with tensor parallelism
|
| 193 |
+
uv run scripts/inspect_vllm_uv.py --model meta-llama/Llama-3.2-70B --task mmlu --tensor-parallel-size 4
|
| 194 |
+
|
| 195 |
+
Available tasks (from inspect-evals):
|
| 196 |
+
- mmlu: Massive Multitask Language Understanding
|
| 197 |
+
- gsm8k: Grade School Math
|
| 198 |
+
- hellaswag: Common sense reasoning
|
| 199 |
+
- arc_challenge: AI2 Reasoning Challenge
|
| 200 |
+
- truthfulqa: TruthfulQA benchmark
|
| 201 |
+
- winogrande: Winograd Schema Challenge
|
| 202 |
+
- humaneval: Code generation (HumanEval)
|
| 203 |
+
|
| 204 |
+
""",
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
parser.add_argument(
|
| 208 |
+
"--model",
|
| 209 |
+
required=True,
|
| 210 |
+
help="HuggingFace model ID (e.g., meta-llama/Llama-3.2-1B)",
|
| 211 |
+
)
|
| 212 |
+
parser.add_argument(
|
| 213 |
+
"--task",
|
| 214 |
+
required=True,
|
| 215 |
+
help="inspect-ai task to execute (e.g., mmlu, gsm8k)",
|
| 216 |
+
)
|
| 217 |
+
parser.add_argument(
|
| 218 |
+
"--backend",
|
| 219 |
+
choices=["vllm", "hf"],
|
| 220 |
+
default="vllm",
|
| 221 |
+
help="Model backend (default: vllm)",
|
| 222 |
+
)
|
| 223 |
+
parser.add_argument(
|
| 224 |
+
"--limit",
|
| 225 |
+
type=int,
|
| 226 |
+
default=None,
|
| 227 |
+
help="Limit number of samples to evaluate",
|
| 228 |
+
)
|
| 229 |
+
parser.add_argument(
|
| 230 |
+
"--max-connections",
|
| 231 |
+
type=int,
|
| 232 |
+
default=None,
|
| 233 |
+
help="Maximum concurrent connections (default: 4 for vllm, 1 for hf)",
|
| 234 |
+
)
|
| 235 |
+
parser.add_argument(
|
| 236 |
+
"--temperature",
|
| 237 |
+
type=float,
|
| 238 |
+
default=None,
|
| 239 |
+
help="Sampling temperature (default: 0.0 for vllm, 0.001 for hf)",
|
| 240 |
+
)
|
| 241 |
+
parser.add_argument(
|
| 242 |
+
"--tensor-parallel-size",
|
| 243 |
+
type=int,
|
| 244 |
+
default=1,
|
| 245 |
+
help="Number of GPUs for tensor parallelism (vLLM only, default: 1)",
|
| 246 |
+
)
|
| 247 |
+
parser.add_argument(
|
| 248 |
+
"--gpu-memory-utilization",
|
| 249 |
+
type=float,
|
| 250 |
+
default=0.8,
|
| 251 |
+
help="GPU memory fraction to use (vLLM only, default: 0.8)",
|
| 252 |
+
)
|
| 253 |
+
parser.add_argument(
|
| 254 |
+
"--dtype",
|
| 255 |
+
default="auto",
|
| 256 |
+
choices=["auto", "float16", "bfloat16", "float32"],
|
| 257 |
+
help="Data type for model weights (default: auto)",
|
| 258 |
+
)
|
| 259 |
+
parser.add_argument(
|
| 260 |
+
"--device",
|
| 261 |
+
default="auto",
|
| 262 |
+
help="Device for HF backend (auto, cuda, cpu)",
|
| 263 |
+
)
|
| 264 |
+
parser.add_argument(
|
| 265 |
+
"--trust-remote-code",
|
| 266 |
+
action="store_true",
|
| 267 |
+
help="Allow executing remote code from model repository",
|
| 268 |
+
)
|
| 269 |
+
parser.add_argument(
|
| 270 |
+
"--log-level",
|
| 271 |
+
default="info",
|
| 272 |
+
choices=["debug", "info", "warning", "error"],
|
| 273 |
+
help="Logging level (default: info)",
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
args = parser.parse_args()
|
| 277 |
+
|
| 278 |
+
if args.backend == "vllm":
|
| 279 |
+
run_inspect_vllm(
|
| 280 |
+
model_id=args.model,
|
| 281 |
+
task=args.task,
|
| 282 |
+
limit=args.limit,
|
| 283 |
+
max_connections=args.max_connections or 4,
|
| 284 |
+
temperature=args.temperature if args.temperature is not None else 0.0,
|
| 285 |
+
tensor_parallel_size=args.tensor_parallel_size,
|
| 286 |
+
gpu_memory_utilization=args.gpu_memory_utilization,
|
| 287 |
+
dtype=args.dtype,
|
| 288 |
+
trust_remote_code=args.trust_remote_code,
|
| 289 |
+
log_level=args.log_level,
|
| 290 |
+
)
|
| 291 |
+
else:
|
| 292 |
+
run_inspect_hf(
|
| 293 |
+
model_id=args.model,
|
| 294 |
+
task=args.task,
|
| 295 |
+
limit=args.limit,
|
| 296 |
+
max_connections=args.max_connections or 1,
|
| 297 |
+
temperature=args.temperature if args.temperature is not None else 0.001,
|
| 298 |
+
device=args.device,
|
| 299 |
+
dtype=args.dtype,
|
| 300 |
+
trust_remote_code=args.trust_remote_code,
|
| 301 |
+
log_level=args.log_level,
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
if __name__ == "__main__":
|
| 306 |
+
main()
|
.agents/skills/huggingface-community-evals/scripts/lighteval_vllm_uv.py
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# /// script
|
| 2 |
+
# requires-python = ">=3.10"
|
| 3 |
+
# dependencies = [
|
| 4 |
+
# "lighteval[accelerate,vllm]>=0.6.0",
|
| 5 |
+
# "torch>=2.0.0",
|
| 6 |
+
# "transformers>=4.40.0",
|
| 7 |
+
# "accelerate>=0.30.0",
|
| 8 |
+
# "vllm>=0.4.0",
|
| 9 |
+
# ]
|
| 10 |
+
# ///
|
| 11 |
+
|
| 12 |
+
"""
|
| 13 |
+
Entry point script for running lighteval evaluations with local GPU backends.
|
| 14 |
+
|
| 15 |
+
This script runs evaluations using vLLM or accelerate on custom HuggingFace models.
|
| 16 |
+
It is separate from inference provider scripts and evaluates models directly on local hardware.
|
| 17 |
+
|
| 18 |
+
Usage (standalone):
|
| 19 |
+
uv run scripts/lighteval_vllm_uv.py --model "meta-llama/Llama-3.2-1B" --tasks "leaderboard|mmlu|5"
|
| 20 |
+
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
from __future__ import annotations
|
| 24 |
+
|
| 25 |
+
import argparse
|
| 26 |
+
import os
|
| 27 |
+
import subprocess
|
| 28 |
+
import sys
|
| 29 |
+
from typing import Optional
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def setup_environment() -> None:
|
| 33 |
+
"""Configure environment variables for HuggingFace authentication."""
|
| 34 |
+
hf_token = os.getenv("HF_TOKEN")
|
| 35 |
+
if hf_token:
|
| 36 |
+
os.environ.setdefault("HUGGING_FACE_HUB_TOKEN", hf_token)
|
| 37 |
+
os.environ.setdefault("HF_HUB_TOKEN", hf_token)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def run_lighteval_vllm(
|
| 41 |
+
model_id: str,
|
| 42 |
+
tasks: str,
|
| 43 |
+
output_dir: Optional[str] = None,
|
| 44 |
+
max_samples: Optional[int] = None,
|
| 45 |
+
batch_size: int = 1,
|
| 46 |
+
tensor_parallel_size: int = 1,
|
| 47 |
+
gpu_memory_utilization: float = 0.8,
|
| 48 |
+
dtype: str = "auto",
|
| 49 |
+
trust_remote_code: bool = False,
|
| 50 |
+
use_chat_template: bool = False,
|
| 51 |
+
system_prompt: Optional[str] = None,
|
| 52 |
+
) -> None:
|
| 53 |
+
"""
|
| 54 |
+
Run lighteval with vLLM backend for efficient GPU inference.
|
| 55 |
+
|
| 56 |
+
Args:
|
| 57 |
+
model_id: HuggingFace model ID (e.g., "meta-llama/Llama-3.2-1B")
|
| 58 |
+
tasks: Task specification (e.g., "leaderboard|mmlu|5" or "lighteval|hellaswag|0")
|
| 59 |
+
output_dir: Directory for evaluation results
|
| 60 |
+
max_samples: Limit number of samples per task
|
| 61 |
+
batch_size: Batch size for evaluation
|
| 62 |
+
tensor_parallel_size: Number of GPUs for tensor parallelism
|
| 63 |
+
gpu_memory_utilization: GPU memory fraction to use (0.0-1.0)
|
| 64 |
+
dtype: Data type for model weights (auto, float16, bfloat16)
|
| 65 |
+
trust_remote_code: Allow executing remote code from model repo
|
| 66 |
+
use_chat_template: Apply chat template for conversational models
|
| 67 |
+
system_prompt: System prompt for chat models
|
| 68 |
+
"""
|
| 69 |
+
setup_environment()
|
| 70 |
+
|
| 71 |
+
# Build lighteval vllm command
|
| 72 |
+
cmd = [
|
| 73 |
+
"lighteval",
|
| 74 |
+
"vllm",
|
| 75 |
+
model_id,
|
| 76 |
+
tasks,
|
| 77 |
+
"--batch-size", str(batch_size),
|
| 78 |
+
"--tensor-parallel-size", str(tensor_parallel_size),
|
| 79 |
+
"--gpu-memory-utilization", str(gpu_memory_utilization),
|
| 80 |
+
"--dtype", dtype,
|
| 81 |
+
]
|
| 82 |
+
|
| 83 |
+
if output_dir:
|
| 84 |
+
cmd.extend(["--output-dir", output_dir])
|
| 85 |
+
|
| 86 |
+
if max_samples:
|
| 87 |
+
cmd.extend(["--max-samples", str(max_samples)])
|
| 88 |
+
|
| 89 |
+
if trust_remote_code:
|
| 90 |
+
cmd.append("--trust-remote-code")
|
| 91 |
+
|
| 92 |
+
if use_chat_template:
|
| 93 |
+
cmd.append("--use-chat-template")
|
| 94 |
+
|
| 95 |
+
if system_prompt:
|
| 96 |
+
cmd.extend(["--system-prompt", system_prompt])
|
| 97 |
+
|
| 98 |
+
print(f"Running: {' '.join(cmd)}")
|
| 99 |
+
|
| 100 |
+
try:
|
| 101 |
+
subprocess.run(cmd, check=True)
|
| 102 |
+
print("Evaluation complete.")
|
| 103 |
+
except subprocess.CalledProcessError as exc:
|
| 104 |
+
print(f"Evaluation failed with exit code {exc.returncode}", file=sys.stderr)
|
| 105 |
+
sys.exit(exc.returncode)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def run_lighteval_accelerate(
|
| 109 |
+
model_id: str,
|
| 110 |
+
tasks: str,
|
| 111 |
+
output_dir: Optional[str] = None,
|
| 112 |
+
max_samples: Optional[int] = None,
|
| 113 |
+
batch_size: int = 1,
|
| 114 |
+
dtype: str = "bfloat16",
|
| 115 |
+
trust_remote_code: bool = False,
|
| 116 |
+
use_chat_template: bool = False,
|
| 117 |
+
system_prompt: Optional[str] = None,
|
| 118 |
+
) -> None:
|
| 119 |
+
"""
|
| 120 |
+
Run lighteval with accelerate backend for multi-GPU distributed inference.
|
| 121 |
+
|
| 122 |
+
Use this backend when vLLM is not available or for models not supported by vLLM.
|
| 123 |
+
|
| 124 |
+
Args:
|
| 125 |
+
model_id: HuggingFace model ID
|
| 126 |
+
tasks: Task specification
|
| 127 |
+
output_dir: Directory for evaluation results
|
| 128 |
+
max_samples: Limit number of samples per task
|
| 129 |
+
batch_size: Batch size for evaluation
|
| 130 |
+
dtype: Data type for model weights
|
| 131 |
+
trust_remote_code: Allow executing remote code
|
| 132 |
+
use_chat_template: Apply chat template
|
| 133 |
+
system_prompt: System prompt for chat models
|
| 134 |
+
"""
|
| 135 |
+
setup_environment()
|
| 136 |
+
|
| 137 |
+
# Build lighteval accelerate command
|
| 138 |
+
cmd = [
|
| 139 |
+
"lighteval",
|
| 140 |
+
"accelerate",
|
| 141 |
+
model_id,
|
| 142 |
+
tasks,
|
| 143 |
+
"--batch-size", str(batch_size),
|
| 144 |
+
"--dtype", dtype,
|
| 145 |
+
]
|
| 146 |
+
|
| 147 |
+
if output_dir:
|
| 148 |
+
cmd.extend(["--output-dir", output_dir])
|
| 149 |
+
|
| 150 |
+
if max_samples:
|
| 151 |
+
cmd.extend(["--max-samples", str(max_samples)])
|
| 152 |
+
|
| 153 |
+
if trust_remote_code:
|
| 154 |
+
cmd.append("--trust-remote-code")
|
| 155 |
+
|
| 156 |
+
if use_chat_template:
|
| 157 |
+
cmd.append("--use-chat-template")
|
| 158 |
+
|
| 159 |
+
if system_prompt:
|
| 160 |
+
cmd.extend(["--system-prompt", system_prompt])
|
| 161 |
+
|
| 162 |
+
print(f"Running: {' '.join(cmd)}")
|
| 163 |
+
|
| 164 |
+
try:
|
| 165 |
+
subprocess.run(cmd, check=True)
|
| 166 |
+
print("Evaluation complete.")
|
| 167 |
+
except subprocess.CalledProcessError as exc:
|
| 168 |
+
print(f"Evaluation failed with exit code {exc.returncode}", file=sys.stderr)
|
| 169 |
+
sys.exit(exc.returncode)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def main() -> None:
|
| 173 |
+
parser = argparse.ArgumentParser(
|
| 174 |
+
description="Run lighteval evaluations with vLLM or accelerate backend on custom HuggingFace models",
|
| 175 |
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
| 176 |
+
epilog="""
|
| 177 |
+
Examples:
|
| 178 |
+
# Run MMLU evaluation with vLLM
|
| 179 |
+
uv run scripts/lighteval_vllm_uv.py --model meta-llama/Llama-3.2-1B --tasks "leaderboard|mmlu|5"
|
| 180 |
+
|
| 181 |
+
# Run with accelerate backend instead of vLLM
|
| 182 |
+
uv run scripts/lighteval_vllm_uv.py --model meta-llama/Llama-3.2-1B --tasks "leaderboard|mmlu|5" --backend accelerate
|
| 183 |
+
|
| 184 |
+
# Run with chat template for instruction-tuned models
|
| 185 |
+
uv run scripts/lighteval_vllm_uv.py --model meta-llama/Llama-3.2-1B-Instruct --tasks "leaderboard|mmlu|5" --use-chat-template
|
| 186 |
+
|
| 187 |
+
# Run with limited samples for testing
|
| 188 |
+
uv run scripts/lighteval_vllm_uv.py --model meta-llama/Llama-3.2-1B --tasks "leaderboard|mmlu|5" --max-samples 10
|
| 189 |
+
|
| 190 |
+
Task format:
|
| 191 |
+
Tasks use the format: "suite|task|num_fewshot"
|
| 192 |
+
- leaderboard|mmlu|5 (MMLU with 5-shot)
|
| 193 |
+
- lighteval|hellaswag|0 (HellaSwag zero-shot)
|
| 194 |
+
- leaderboard|gsm8k|5 (GSM8K with 5-shot)
|
| 195 |
+
- Multiple tasks: "leaderboard|mmlu|5,leaderboard|gsm8k|5"
|
| 196 |
+
""",
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
parser.add_argument(
|
| 200 |
+
"--model",
|
| 201 |
+
required=True,
|
| 202 |
+
help="HuggingFace model ID (e.g., meta-llama/Llama-3.2-1B)",
|
| 203 |
+
)
|
| 204 |
+
parser.add_argument(
|
| 205 |
+
"--tasks",
|
| 206 |
+
required=True,
|
| 207 |
+
help="Task specification (e.g., 'leaderboard|mmlu|5')",
|
| 208 |
+
)
|
| 209 |
+
parser.add_argument(
|
| 210 |
+
"--backend",
|
| 211 |
+
choices=["vllm", "accelerate"],
|
| 212 |
+
default="vllm",
|
| 213 |
+
help="Inference backend to use (default: vllm)",
|
| 214 |
+
)
|
| 215 |
+
parser.add_argument(
|
| 216 |
+
"--output-dir",
|
| 217 |
+
default=None,
|
| 218 |
+
help="Directory for evaluation results",
|
| 219 |
+
)
|
| 220 |
+
parser.add_argument(
|
| 221 |
+
"--max-samples",
|
| 222 |
+
type=int,
|
| 223 |
+
default=None,
|
| 224 |
+
help="Limit number of samples per task (useful for testing)",
|
| 225 |
+
)
|
| 226 |
+
parser.add_argument(
|
| 227 |
+
"--batch-size",
|
| 228 |
+
type=int,
|
| 229 |
+
default=1,
|
| 230 |
+
help="Batch size for evaluation (default: 1)",
|
| 231 |
+
)
|
| 232 |
+
parser.add_argument(
|
| 233 |
+
"--tensor-parallel-size",
|
| 234 |
+
type=int,
|
| 235 |
+
default=1,
|
| 236 |
+
help="Number of GPUs for tensor parallelism (vLLM only, default: 1)",
|
| 237 |
+
)
|
| 238 |
+
parser.add_argument(
|
| 239 |
+
"--gpu-memory-utilization",
|
| 240 |
+
type=float,
|
| 241 |
+
default=0.8,
|
| 242 |
+
help="GPU memory fraction to use (vLLM only, default: 0.8)",
|
| 243 |
+
)
|
| 244 |
+
parser.add_argument(
|
| 245 |
+
"--dtype",
|
| 246 |
+
default="auto",
|
| 247 |
+
choices=["auto", "float16", "bfloat16", "float32"],
|
| 248 |
+
help="Data type for model weights (default: auto)",
|
| 249 |
+
)
|
| 250 |
+
parser.add_argument(
|
| 251 |
+
"--trust-remote-code",
|
| 252 |
+
action="store_true",
|
| 253 |
+
help="Allow executing remote code from model repository",
|
| 254 |
+
)
|
| 255 |
+
parser.add_argument(
|
| 256 |
+
"--use-chat-template",
|
| 257 |
+
action="store_true",
|
| 258 |
+
help="Apply chat template for instruction-tuned/chat models",
|
| 259 |
+
)
|
| 260 |
+
parser.add_argument(
|
| 261 |
+
"--system-prompt",
|
| 262 |
+
default=None,
|
| 263 |
+
help="System prompt for chat models",
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
args = parser.parse_args()
|
| 267 |
+
|
| 268 |
+
if args.backend == "vllm":
|
| 269 |
+
run_lighteval_vllm(
|
| 270 |
+
model_id=args.model,
|
| 271 |
+
tasks=args.tasks,
|
| 272 |
+
output_dir=args.output_dir,
|
| 273 |
+
max_samples=args.max_samples,
|
| 274 |
+
batch_size=args.batch_size,
|
| 275 |
+
tensor_parallel_size=args.tensor_parallel_size,
|
| 276 |
+
gpu_memory_utilization=args.gpu_memory_utilization,
|
| 277 |
+
dtype=args.dtype,
|
| 278 |
+
trust_remote_code=args.trust_remote_code,
|
| 279 |
+
use_chat_template=args.use_chat_template,
|
| 280 |
+
system_prompt=args.system_prompt,
|
| 281 |
+
)
|
| 282 |
+
else:
|
| 283 |
+
run_lighteval_accelerate(
|
| 284 |
+
model_id=args.model,
|
| 285 |
+
tasks=args.tasks,
|
| 286 |
+
output_dir=args.output_dir,
|
| 287 |
+
max_samples=args.max_samples,
|
| 288 |
+
batch_size=args.batch_size,
|
| 289 |
+
dtype=args.dtype if args.dtype != "auto" else "bfloat16",
|
| 290 |
+
trust_remote_code=args.trust_remote_code,
|
| 291 |
+
use_chat_template=args.use_chat_template,
|
| 292 |
+
system_prompt=args.system_prompt,
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
if __name__ == "__main__":
|
| 297 |
+
main()
|
.agents/skills/huggingface-datasets/.hf-skill-manifest.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"installed_revision": "b3df145a3a5a3e64ac075d781c625896d9d2bfcd",
|
| 3 |
+
"schema_version": 1
|
| 4 |
+
}
|
.agents/skills/huggingface-datasets/SKILL.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: huggingface-datasets
|
| 3 |
+
description: Use this skill for Hugging Face Dataset Viewer API workflows that fetch subset/split metadata, paginate rows, search text, apply filters, download parquet URLs, and read size or statistics.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Hugging Face Dataset Viewer
|
| 7 |
+
|
| 8 |
+
Use this skill to execute read-only Dataset Viewer API calls for dataset exploration and extraction.
|
| 9 |
+
|
| 10 |
+
## Core workflow
|
| 11 |
+
|
| 12 |
+
1. Optionally validate dataset availability with `/is-valid`.
|
| 13 |
+
2. Resolve `config` + `split` with `/splits`.
|
| 14 |
+
3. Preview with `/first-rows`.
|
| 15 |
+
4. Paginate content with `/rows` using `offset` and `length` (max 100).
|
| 16 |
+
5. Use `/search` for text matching and `/filter` for row predicates.
|
| 17 |
+
6. Retrieve parquet links via `/parquet` and totals/metadata via `/size` and `/statistics`.
|
| 18 |
+
|
| 19 |
+
## Defaults
|
| 20 |
+
|
| 21 |
+
- Base URL: `https://datasets-server.huggingface.co`
|
| 22 |
+
- Default API method: `GET`
|
| 23 |
+
- Query params should be URL-encoded.
|
| 24 |
+
- `offset` is 0-based.
|
| 25 |
+
- `length` max is usually `100` for row-like endpoints.
|
| 26 |
+
- Gated/private datasets require `Authorization: Bearer <HF_TOKEN>`.
|
| 27 |
+
|
| 28 |
+
## Dataset Viewer
|
| 29 |
+
|
| 30 |
+
- `Validate dataset`: `/is-valid?dataset=<namespace/repo>`
|
| 31 |
+
- `List subsets and splits`: `/splits?dataset=<namespace/repo>`
|
| 32 |
+
- `Preview first rows`: `/first-rows?dataset=<namespace/repo>&config=<config>&split=<split>`
|
| 33 |
+
- `Paginate rows`: `/rows?dataset=<namespace/repo>&config=<config>&split=<split>&offset=<int>&length=<int>`
|
| 34 |
+
- `Search text`: `/search?dataset=<namespace/repo>&config=<config>&split=<split>&query=<text>&offset=<int>&length=<int>`
|
| 35 |
+
- `Filter with predicates`: `/filter?dataset=<namespace/repo>&config=<config>&split=<split>&where=<predicate>&orderby=<sort>&offset=<int>&length=<int>`
|
| 36 |
+
- `List parquet shards`: `/parquet?dataset=<namespace/repo>`
|
| 37 |
+
- `Get size totals`: `/size?dataset=<namespace/repo>`
|
| 38 |
+
- `Get column statistics`: `/statistics?dataset=<namespace/repo>&config=<config>&split=<split>`
|
| 39 |
+
- `Get Croissant metadata (if available)`: `/croissant?dataset=<namespace/repo>`
|
| 40 |
+
|
| 41 |
+
Pagination pattern:
|
| 42 |
+
|
| 43 |
+
```bash
|
| 44 |
+
curl "https://datasets-server.huggingface.co/rows?dataset=stanfordnlp/imdb&config=plain_text&split=train&offset=0&length=100"
|
| 45 |
+
curl "https://datasets-server.huggingface.co/rows?dataset=stanfordnlp/imdb&config=plain_text&split=train&offset=100&length=100"
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
When pagination is partial, use response fields such as `num_rows_total`, `num_rows_per_page`, and `partial` to drive continuation logic.
|
| 49 |
+
|
| 50 |
+
Search/filter notes:
|
| 51 |
+
|
| 52 |
+
- `/search` matches string columns (full-text style behavior is internal to the API).
|
| 53 |
+
- `/filter` requires predicate syntax in `where` and optional sort in `orderby`.
|
| 54 |
+
- Keep filtering and searches read-only and side-effect free.
|
| 55 |
+
|
| 56 |
+
For CLI-based parquet URL discovery or SQL, use the `hf-cli` skill with `hf datasets parquet` and `hf datasets sql`.
|
| 57 |
+
|
| 58 |
+
## Creating and Uploading Datasets
|
| 59 |
+
|
| 60 |
+
Use one of these flows depending on dependency constraints.
|
| 61 |
+
|
| 62 |
+
Zero local dependencies (Hub UI):
|
| 63 |
+
|
| 64 |
+
- Create dataset repo in browser: `https://huggingface.co/new-dataset`
|
| 65 |
+
- Upload parquet files in the repo "Files and versions" page.
|
| 66 |
+
- Verify shards appear in Dataset Viewer:
|
| 67 |
+
|
| 68 |
+
```bash
|
| 69 |
+
curl -s "https://datasets-server.huggingface.co/parquet?dataset=<namespace>/<repo>"
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
Low dependency CLI flow (`npx @huggingface/hub` / `hfjs`):
|
| 73 |
+
|
| 74 |
+
- Set auth token:
|
| 75 |
+
|
| 76 |
+
```bash
|
| 77 |
+
export HF_TOKEN=<your_hf_token>
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
- Upload parquet folder to a dataset repo (auto-creates repo if missing):
|
| 81 |
+
|
| 82 |
+
```bash
|
| 83 |
+
npx -y @huggingface/hub upload datasets/<namespace>/<repo> ./local/parquet-folder data
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
- Upload as private repo on creation:
|
| 87 |
+
|
| 88 |
+
```bash
|
| 89 |
+
npx -y @huggingface/hub upload datasets/<namespace>/<repo> ./local/parquet-folder data --private
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
After upload, call `/parquet` to discover `<config>/<split>/<shard>` values for querying with `@~parquet`.
|
| 93 |
+
|
| 94 |
+
## Agent Traces
|
| 95 |
+
|
| 96 |
+
The Hub supports raw agent session traces from Claude Code, Codex, and Pi Agent. Upload them to Hugging Face Datasets as original JSONL files and the Hub can auto-detect the trace format, tag the dataset as `Traces`, and enable the trace viewer for browsing sessions, turns, tool calls, and model responses. Common local session directories:
|
| 97 |
+
|
| 98 |
+
- Claude Code: `~/.claude/projects`
|
| 99 |
+
- Codex: `~/.codex/sessions`
|
| 100 |
+
- Pi: `~/.pi/agent/sessions`
|
| 101 |
+
|
| 102 |
+
Default to private dataset repos because traces can contain prompts, file paths, tool outputs, secrets, or PII. Preserve the raw `.jsonl` files and nest them by project/cwd instead of uploading every session at the dataset root.
|
| 103 |
+
|
| 104 |
+
```bash
|
| 105 |
+
hf repos create <namespace>/<repo> --type dataset --private --exist-ok
|
| 106 |
+
hf upload <namespace>/<repo> ~/.codex/sessions codex/<project-or-cwd> --type dataset
|
| 107 |
+
```
|
.agents/skills/huggingface-gradio/SKILL.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: huggingface-gradio
|
| 3 |
+
description: Build Gradio web UIs and demos in Python. Use when creating or editing Gradio apps, components, event listeners, layouts, or chatbots.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Gradio
|
| 7 |
+
|
| 8 |
+
Gradio is a Python library for building interactive web UIs and ML demos. This skill covers the core API, patterns, and examples.
|
| 9 |
+
|
| 10 |
+
## Guides
|
| 11 |
+
|
| 12 |
+
Detailed guides on specific topics (read these when relevant):
|
| 13 |
+
|
| 14 |
+
- [Quickstart](https://www.gradio.app/guides/quickstart)
|
| 15 |
+
- [The Interface Class](https://www.gradio.app/guides/the-interface-class)
|
| 16 |
+
- [Blocks and Event Listeners](https://www.gradio.app/guides/blocks-and-event-listeners)
|
| 17 |
+
- [Controlling Layout](https://www.gradio.app/guides/controlling-layout)
|
| 18 |
+
- [More Blocks Features](https://www.gradio.app/guides/more-blocks-features)
|
| 19 |
+
- [Custom CSS and JS](https://www.gradio.app/guides/custom-CSS-and-JS)
|
| 20 |
+
- [Streaming Outputs](https://www.gradio.app/guides/streaming-outputs)
|
| 21 |
+
- [Streaming Inputs](https://www.gradio.app/guides/streaming-inputs)
|
| 22 |
+
- [Sharing Your App](https://www.gradio.app/guides/sharing-your-app)
|
| 23 |
+
- [Custom HTML Components](https://www.gradio.app/guides/custom-HTML-components)
|
| 24 |
+
- [Getting Started with the Python Client](https://www.gradio.app/guides/getting-started-with-the-python-client)
|
| 25 |
+
- [Getting Started with the JS Client](https://www.gradio.app/guides/getting-started-with-the-js-client)
|
| 26 |
+
|
| 27 |
+
## Core Patterns
|
| 28 |
+
|
| 29 |
+
**Interface** (high-level): wraps a function with input/output components.
|
| 30 |
+
|
| 31 |
+
```python
|
| 32 |
+
import gradio as gr
|
| 33 |
+
|
| 34 |
+
def greet(name):
|
| 35 |
+
return f"Hello {name}!"
|
| 36 |
+
|
| 37 |
+
gr.Interface(fn=greet, inputs="text", outputs="text").launch()
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
**Blocks** (low-level): flexible layout with explicit event wiring.
|
| 41 |
+
|
| 42 |
+
```python
|
| 43 |
+
import gradio as gr
|
| 44 |
+
|
| 45 |
+
with gr.Blocks() as demo:
|
| 46 |
+
name = gr.Textbox(label="Name")
|
| 47 |
+
output = gr.Textbox(label="Greeting")
|
| 48 |
+
btn = gr.Button("Greet")
|
| 49 |
+
btn.click(fn=lambda n: f"Hello {n}!", inputs=name, outputs=output)
|
| 50 |
+
|
| 51 |
+
demo.launch()
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
**ChatInterface**: high-level wrapper for chatbot UIs.
|
| 55 |
+
|
| 56 |
+
```python
|
| 57 |
+
import gradio as gr
|
| 58 |
+
|
| 59 |
+
def respond(message, history):
|
| 60 |
+
return f"You said: {message}"
|
| 61 |
+
|
| 62 |
+
gr.ChatInterface(fn=respond).launch()
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
## Key Component Signatures
|
| 66 |
+
|
| 67 |
+
### `Textbox(value: str | I18nData | Callable | None = None, type: Literal['text', 'password', 'email'] = "text", lines: int = 1, max_lines: int | None = None, placeholder: str | I18nData | None = None, label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, autofocus: bool = False, autoscroll: bool = True, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", text_align: Literal['left', 'right'] | None = None, rtl: bool = False, buttons: list[Literal['copy'] | Button] | None = None, max_length: int | None = None, submit_btn: str | bool | None = False, stop_btn: str | bool | None = False, html_attributes: InputHTMLAttributes | None = None)`
|
| 68 |
+
Creates a textarea for user to enter string input or display string output..
|
| 69 |
+
|
| 70 |
+
### `Number(value: float | Callable | None = None, label: str | I18nData | None = None, placeholder: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", buttons: list[Button] | None = None, precision: int | None = None, minimum: float | None = None, maximum: float | None = None, step: float = 1)`
|
| 71 |
+
Creates a numeric field for user to enter numbers as input or display numeric output..
|
| 72 |
+
|
| 73 |
+
### `Slider(minimum: float = 0, maximum: float = 100, value: float | Callable | None = None, step: float | None = None, precision: int | None = None, label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", randomize: bool = False, buttons: list[Literal['reset']] | None = None)`
|
| 74 |
+
Creates a slider that ranges from {minimum} to {maximum} with a step size of {step}..
|
| 75 |
+
|
| 76 |
+
### `Checkbox(value: bool | Callable = False, label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", buttons: list[Button] | None = None)`
|
| 77 |
+
Creates a checkbox that can be set to `True` or `False`.
|
| 78 |
+
|
| 79 |
+
### `Dropdown(choices: Sequence[str | int | float | tuple[str, str | int | float]] | None = None, value: str | int | float | Sequence[str | int | float] | Callable | DefaultValue | None = DefaultValue(), type: Literal['value', 'index'] = "value", multiselect: bool | None = None, allow_custom_value: bool = False, max_choices: int | None = None, filterable: bool = True, label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", buttons: list[Button] | None = None)`
|
| 80 |
+
Creates a dropdown of choices from which a single entry or multiple entries can be selected (as an input component) or displayed (as an output component)..
|
| 81 |
+
|
| 82 |
+
### `Radio(choices: Sequence[str | int | float | tuple[str, str | int | float]] | None = None, value: str | int | float | Callable | None = None, type: Literal['value', 'index'] = "value", label: str | I18nData | None = None, info: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", rtl: bool = False, buttons: list[Button] | None = None)`
|
| 83 |
+
Creates a set of (string or numeric type) radio buttons of which only one can be selected..
|
| 84 |
+
|
| 85 |
+
### `Image(value: str | PIL.Image.Image | np.ndarray | Callable | None = None, format: str = "webp", height: int | str | None = None, width: int | str | None = None, image_mode: Literal['1', 'L', 'P', 'RGB', 'RGBA', 'CMYK', 'YCbCr', 'LAB', 'HSV', 'I', 'F'] | None = "RGB", sources: list[Literal['upload', 'webcam', 'clipboard']] | Literal['upload', 'webcam', 'clipboard'] | None = None, type: Literal['numpy', 'pil', 'filepath'] = "numpy", label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, buttons: list[Literal['download', 'share', 'fullscreen'] | Button] | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, streaming: bool = False, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", webcam_options: WebcamOptions | None = None, placeholder: str | None = None, watermark: WatermarkOptions | None = None)`
|
| 86 |
+
Creates an image component that can be used to upload images (as an input) or display images (as an output)..
|
| 87 |
+
|
| 88 |
+
### `Audio(value: str | Path | tuple[int, np.ndarray] | Callable | None = None, sources: list[Literal['upload', 'microphone']] | Literal['upload', 'microphone'] | None = None, type: Literal['numpy', 'filepath'] = "numpy", label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, streaming: bool = False, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", format: Literal['wav', 'mp3'] | None = None, autoplay: bool = False, editable: bool = True, buttons: list[Literal['download', 'share'] | Button] | None = None, waveform_options: WaveformOptions | dict | None = None, loop: bool = False, recording: bool = False, subtitles: str | Path | list[dict[str, Any]] | None = None, playback_position: float = 0)`
|
| 89 |
+
Creates an audio component that can be used to upload/record audio (as an input) or display audio (as an output)..
|
| 90 |
+
|
| 91 |
+
### `Video(value: str | Path | Callable | None = None, format: str | None = None, sources: list[Literal['upload', 'webcam']] | Literal['upload', 'webcam'] | None = None, height: int | str | None = None, width: int | str | None = None, label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", webcam_options: WebcamOptions | None = None, include_audio: bool | None = None, autoplay: bool = False, buttons: list[Literal['download', 'share'] | Button] | None = None, loop: bool = False, streaming: bool = False, watermark: WatermarkOptions | None = None, subtitles: str | Path | list[dict[str, Any]] | None = None, playback_position: float = 0)`
|
| 92 |
+
Creates a video component that can be used to upload/record videos (as an input) or display videos (as an output).
|
| 93 |
+
|
| 94 |
+
### `File(value: str | list[str] | Callable | None = None, file_count: Literal['single', 'multiple', 'directory'] = "single", file_types: list[str] | None = None, type: Literal['filepath', 'binary'] = "filepath", label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, height: int | str | float | None = None, interactive: bool | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", allow_reordering: bool = False, buttons: list[Button] | None = None)`
|
| 95 |
+
Creates a file component that allows uploading one or more generic files (when used as an input) or displaying generic files or URLs for download (as output).
|
| 96 |
+
|
| 97 |
+
### `Chatbot(value: list[MessageDict | Message] | Callable | None = None, label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, container: bool = True, scale: int | None = None, min_width: int = 160, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, autoscroll: bool = True, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", height: int | str | None = 400, resizable: bool = False, max_height: int | str | None = None, min_height: int | str | None = None, editable: Literal['user', 'all'] | None = None, latex_delimiters: list[dict[str, str | bool]] | None = None, rtl: bool = False, buttons: list[Literal['share', 'copy', 'copy_all'] | Button] | None = None, watermark: str | None = None, avatar_images: tuple[str | Path | None, str | Path | None] | None = None, sanitize_html: bool = True, render_markdown: bool = True, feedback_options: list[str] | tuple[str, ...] | None = ('Like', 'Dislike'), feedback_value: Sequence[str | None] | None = None, line_breaks: bool = True, layout: Literal['panel', 'bubble'] | None = None, placeholder: str | None = None, examples: list[ExampleMessage] | None = None, allow_file_downloads: <class 'inspect._empty'> = True, group_consecutive_messages: bool = True, allow_tags: list[str] | bool = True, reasoning_tags: list[tuple[str, str]] | None = None, like_user_message: bool = False)`
|
| 98 |
+
Creates a chatbot that displays user-submitted messages and responses.
|
| 99 |
+
|
| 100 |
+
### `Button(value: str | I18nData | Callable = "Run", every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, variant: Literal['primary', 'secondary', 'stop', 'huggingface'] = "secondary", size: Literal['sm', 'md', 'lg'] = "lg", icon: str | Path | None = None, link: str | None = None, link_target: Literal['_self', '_blank', '_parent', '_top'] = "_self", visible: bool | Literal['hidden'] = True, interactive: bool = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", scale: int | None = None, min_width: int | None = None)`
|
| 101 |
+
Creates a button that can be assigned arbitrary .click() events.
|
| 102 |
+
|
| 103 |
+
### `Markdown(value: str | I18nData | Callable | None = None, label: str | I18nData | None = None, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool | None = None, rtl: bool = False, latex_delimiters: list[dict[str, str | bool]] | None = None, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", sanitize_html: bool = True, line_breaks: bool = False, header_links: bool = False, height: int | str | None = None, max_height: int | str | None = None, min_height: int | str | None = None, buttons: list[Literal['copy']] | None = None, container: bool = False, padding: bool = False)`
|
| 104 |
+
Used to render arbitrary Markdown output.
|
| 105 |
+
|
| 106 |
+
### `HTML(value: Any | Callable | None = None, label: str | I18nData | None = None, html_template: str = "${value}", css_template: str = "", js_on_load: str | None = "element.addEventListener('click', function() { trigger('click') });", apply_default_css: bool = True, every: Timer | float | None = None, inputs: Component | Sequence[Component] | set[Component] | None = None, show_label: bool = False, visible: bool | Literal['hidden'] = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | tuple[int | str, ...] | None = None, preserved_by_key: list[str] | str | None = "value", min_height: int | None = None, max_height: int | None = None, container: bool = False, padding: bool = False, autoscroll: bool = False, buttons: list[Button] | None = None, server_functions: list[Callable] | None = None, props: Any)`
|
| 107 |
+
Creates a component with arbitrary HTML.
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
## Custom HTML Components
|
| 111 |
+
|
| 112 |
+
If a task requires significant customization of an existing component or a component that doesn't exist in Gradio, you can create one with `gr.HTML`. It supports `html_template` (with `${}` JS expressions and `{{}}` Handlebars syntax), `css_template` for scoped styles, and `js_on_load` for interactivity β where `props.value` updates the component value and `trigger('event_name')` fires Gradio events. For reuse, subclass `gr.HTML` and define `api_info()` for API/MCP support. See the [full guide](https://www.gradio.app/guides/custom-HTML-components).
|
| 113 |
+
|
| 114 |
+
Here's an example that shows how to create and use these kinds of components:
|
| 115 |
+
|
| 116 |
+
```python
|
| 117 |
+
import gradio as gr
|
| 118 |
+
|
| 119 |
+
class StarRating(gr.HTML):
|
| 120 |
+
def __init__(self, label, value=0, **kwargs):
|
| 121 |
+
html_template = """
|
| 122 |
+
<h2>${label} rating:</h2>
|
| 123 |
+
${Array.from({length: 5}, (_, i) => `<img class='${i < value ? '' : 'faded'}' src='https://upload.wikimedia.org/wikipedia/commons/d/df/Award-star-gold-3d.svg'>`).join('')}
|
| 124 |
+
"""
|
| 125 |
+
css_template = """
|
| 126 |
+
img { height: 50px; display: inline-block; cursor: pointer; }
|
| 127 |
+
.faded { filter: grayscale(100%); opacity: 0.3; }
|
| 128 |
+
"""
|
| 129 |
+
js_on_load = """
|
| 130 |
+
const imgs = element.querySelectorAll('img');
|
| 131 |
+
imgs.forEach((img, index) => {
|
| 132 |
+
img.addEventListener('click', () => {
|
| 133 |
+
props.value = index + 1;
|
| 134 |
+
});
|
| 135 |
+
});
|
| 136 |
+
"""
|
| 137 |
+
super().__init__(value=value, label=label, html_template=html_template, css_template=css_template, js_on_load=js_on_load, **kwargs)
|
| 138 |
+
|
| 139 |
+
def api_info(self):
|
| 140 |
+
return {"type": "integer", "minimum": 0, "maximum": 5}
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
with gr.Blocks() as demo:
|
| 144 |
+
gr.Markdown("# Restaurant Review")
|
| 145 |
+
food_rating = StarRating(label="Food", value=3)
|
| 146 |
+
service_rating = StarRating(label="Service", value=3)
|
| 147 |
+
ambience_rating = StarRating(label="Ambience", value=3)
|
| 148 |
+
average_btn = gr.Button("Calculate Average Rating")
|
| 149 |
+
rating_output = StarRating(label="Average", value=3)
|
| 150 |
+
def calculate_average(food, service, ambience):
|
| 151 |
+
return round((food + service + ambience) / 3)
|
| 152 |
+
average_btn.click(
|
| 153 |
+
fn=calculate_average,
|
| 154 |
+
inputs=[food_rating, service_rating, ambience_rating],
|
| 155 |
+
outputs=rating_output
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
demo.launch()
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
## Event Listeners
|
| 162 |
+
|
| 163 |
+
All event listeners share the same signature:
|
| 164 |
+
|
| 165 |
+
```python
|
| 166 |
+
component.event_name(
|
| 167 |
+
fn: Callable | None | Literal["decorator"] = "decorator",
|
| 168 |
+
inputs: Component | Sequence[Component] | set[Component] | None = None,
|
| 169 |
+
outputs: Component | Sequence[Component] | set[Component] | None = None,
|
| 170 |
+
api_name: str | None = None,
|
| 171 |
+
api_description: str | None | Literal[False] = None,
|
| 172 |
+
scroll_to_output: bool = False,
|
| 173 |
+
show_progress: Literal["full", "minimal", "hidden"] = "full",
|
| 174 |
+
show_progress_on: Component | Sequence[Component] | None = None,
|
| 175 |
+
queue: bool = True,
|
| 176 |
+
batch: bool = False,
|
| 177 |
+
max_batch_size: int = 4,
|
| 178 |
+
preprocess: bool = True,
|
| 179 |
+
postprocess: bool = True,
|
| 180 |
+
cancels: dict[str, Any] | list[dict[str, Any]] | None = None,
|
| 181 |
+
trigger_mode: Literal["once", "multiple", "always_last"] | None = None,
|
| 182 |
+
js: str | Literal[True] | None = None,
|
| 183 |
+
concurrency_limit: int | None | Literal["default"] = "default",
|
| 184 |
+
concurrency_id: str | None = None,
|
| 185 |
+
api_visibility: Literal["public", "private", "undocumented"] = "public",
|
| 186 |
+
time_limit: int | None = None,
|
| 187 |
+
stream_every: float = 0.5,
|
| 188 |
+
key: int | str | tuple[int | str, ...] | None = None,
|
| 189 |
+
validator: Callable | None = None,
|
| 190 |
+
) -> Dependency
|
| 191 |
+
```
|
| 192 |
+
|
| 193 |
+
Supported events per component:
|
| 194 |
+
|
| 195 |
+
- **AnnotatedImage**: select
|
| 196 |
+
- **Audio**: stream, change, clear, play, pause, stop, pause, start_recording, pause_recording, stop_recording, upload, input
|
| 197 |
+
- **BarPlot**: select, double_click
|
| 198 |
+
- **BrowserState**: change
|
| 199 |
+
- **Button**: click
|
| 200 |
+
- **Chatbot**: change, select, like, retry, undo, example_select, option_select, clear, copy, edit
|
| 201 |
+
- **Checkbox**: change, input, select
|
| 202 |
+
- **CheckboxGroup**: change, input, select
|
| 203 |
+
- **ClearButton**: click
|
| 204 |
+
- **Code**: change, input, focus, blur
|
| 205 |
+
- **ColorPicker**: change, input, submit, focus, blur
|
| 206 |
+
- **Dataframe**: change, input, select, edit
|
| 207 |
+
- **Dataset**: click, select
|
| 208 |
+
- **DateTime**: change, submit
|
| 209 |
+
- **DeepLinkButton**: click
|
| 210 |
+
- **Dialogue**: change, input, submit
|
| 211 |
+
- **DownloadButton**: click
|
| 212 |
+
- **Dropdown**: change, input, select, focus, blur, key_up
|
| 213 |
+
- **DuplicateButton**: click
|
| 214 |
+
- **File**: change, select, clear, upload, delete, download
|
| 215 |
+
- **FileExplorer**: change, input, select
|
| 216 |
+
- **Gallery**: select, upload, change, delete, preview_close, preview_open
|
| 217 |
+
- **HTML**: change, input, click, double_click, submit, stop, edit, clear, play, pause, end, start_recording, pause_recording, stop_recording, focus, blur, upload, release, select, stream, like, example_select, option_select, load, key_up, apply, delete, tick, undo, retry, expand, collapse, download, copy
|
| 218 |
+
- **HighlightedText**: change, select
|
| 219 |
+
- **Image**: clear, change, stream, select, upload, input
|
| 220 |
+
- **ImageEditor**: clear, change, input, select, upload, apply
|
| 221 |
+
- **ImageSlider**: clear, change, stream, select, upload, input
|
| 222 |
+
- **JSON**: change
|
| 223 |
+
- **Label**: change, select
|
| 224 |
+
- **LinePlot**: select, double_click
|
| 225 |
+
- **LoginButton**: click
|
| 226 |
+
- **Markdown**: change, copy
|
| 227 |
+
- **Model3D**: change, upload, edit, clear
|
| 228 |
+
- **MultimodalTextbox**: change, input, select, submit, focus, blur, stop
|
| 229 |
+
- **Navbar**: change
|
| 230 |
+
- **Number**: change, input, submit, focus, blur
|
| 231 |
+
- **ParamViewer**: change, upload
|
| 232 |
+
- **Plot**: change
|
| 233 |
+
- **Radio**: select, change, input
|
| 234 |
+
- **ScatterPlot**: select, double_click
|
| 235 |
+
- **SimpleImage**: clear, change, upload
|
| 236 |
+
- **Slider**: change, input, release
|
| 237 |
+
- **State**: change
|
| 238 |
+
- **Textbox**: change, input, select, submit, focus, blur, stop, copy
|
| 239 |
+
- **Timer**: tick
|
| 240 |
+
- **UploadButton**: click, upload
|
| 241 |
+
- **Video**: change, clear, start_recording, stop_recording, stop, play, pause, end, upload, input
|
| 242 |
+
|
| 243 |
+
## Prediction CLI
|
| 244 |
+
|
| 245 |
+
The `gradio` CLI includes `info` and `predict` commands for interacting with Gradio apps programmatically. These are especially useful for coding agents that need to use Spaces in their workflows.
|
| 246 |
+
|
| 247 |
+
### `gradio info` β Discover endpoints and parameters
|
| 248 |
+
|
| 249 |
+
```bash
|
| 250 |
+
gradio info <space_id_or_url>
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
Returns a JSON payload describing all endpoints, their parameters (with types and defaults), and return values.
|
| 254 |
+
|
| 255 |
+
```bash
|
| 256 |
+
gradio info gradio/calculator
|
| 257 |
+
# {
|
| 258 |
+
# "/predict": {
|
| 259 |
+
# "parameters": [
|
| 260 |
+
# {"name": "num1", "required": true, "default": null, "type": {"type": "number"}},
|
| 261 |
+
# {"name": "operation", "required": true, "default": null, "type": {"enum": ["add", "subtract", "multiply", "divide"], "type": "string"}},
|
| 262 |
+
# {"name": "num2", "required": true, "default": null, "type": {"type": "number"}}
|
| 263 |
+
# ],
|
| 264 |
+
# "returns": [{"name": "output", "type": {"type": "number"}}],
|
| 265 |
+
# "description": ""
|
| 266 |
+
# }
|
| 267 |
+
# }
|
| 268 |
+
```
|
| 269 |
+
|
| 270 |
+
File-type parameters show `"type": "filepath"` with instructions to include `"meta": {"_type": "gradio.FileData"}` β this signals the file will be uploaded to the remote server.
|
| 271 |
+
|
| 272 |
+
### `gradio predict` β Send predictions
|
| 273 |
+
|
| 274 |
+
```bash
|
| 275 |
+
gradio predict <space_id_or_url> <endpoint> <json_payload>
|
| 276 |
+
```
|
| 277 |
+
|
| 278 |
+
Returns a JSON object with named output keys.
|
| 279 |
+
|
| 280 |
+
```bash
|
| 281 |
+
# Simple numeric prediction
|
| 282 |
+
gradio predict gradio/calculator /predict '{"num1": 5, "operation": "multiply", "num2": 3}'
|
| 283 |
+
# {"output": 15}
|
| 284 |
+
|
| 285 |
+
# Image generation
|
| 286 |
+
gradio predict black-forest-labs/FLUX.2-dev /infer '{"prompt": "A majestic dragon"}'
|
| 287 |
+
# {"Result": "/tmp/gradio/.../image.webp", "Seed": 1117868604}
|
| 288 |
+
|
| 289 |
+
# File upload (must include meta key)
|
| 290 |
+
gradio predict gradio/image_mod /predict '{"image": {"path": "/path/to/image.png", "meta": {"_type": "gradio.FileData"}}}'
|
| 291 |
+
# {"output": "/tmp/gradio/.../output.png"}
|
| 292 |
+
```
|
| 293 |
+
|
| 294 |
+
Both commands accept `--token` for accessing private Spaces.
|
| 295 |
+
|
| 296 |
+
## Additional Reference
|
| 297 |
+
|
| 298 |
+
- [End-to-End Examples](examples.md) β complete working apps
|
.agents/skills/huggingface-gradio/examples.md
ADDED
|
@@ -0,0 +1,613 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Gradio End-to-End Examples
|
| 2 |
+
|
| 3 |
+
Complete working Gradio apps for reference.
|
| 4 |
+
|
| 5 |
+
## Blocks Essay Simple
|
| 6 |
+
|
| 7 |
+
```python
|
| 8 |
+
import gradio as gr
|
| 9 |
+
|
| 10 |
+
def change_textbox(choice):
|
| 11 |
+
if choice == "short":
|
| 12 |
+
return gr.Textbox(lines=2, visible=True)
|
| 13 |
+
elif choice == "long":
|
| 14 |
+
return gr.Textbox(lines=8, visible=True, value="Lorem ipsum dolor sit amet")
|
| 15 |
+
else:
|
| 16 |
+
return gr.Textbox(visible=False)
|
| 17 |
+
|
| 18 |
+
with gr.Blocks() as demo:
|
| 19 |
+
radio = gr.Radio(
|
| 20 |
+
["short", "long", "none"], label="What kind of essay would you like to write?"
|
| 21 |
+
)
|
| 22 |
+
text = gr.Textbox(lines=2, interactive=True, buttons=["copy"])
|
| 23 |
+
radio.change(fn=change_textbox, inputs=radio, outputs=text)
|
| 24 |
+
|
| 25 |
+
demo.launch()
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
## Blocks Flipper
|
| 29 |
+
|
| 30 |
+
```python
|
| 31 |
+
import numpy as np
|
| 32 |
+
import gradio as gr
|
| 33 |
+
|
| 34 |
+
def flip_text(x):
|
| 35 |
+
return x[::-1]
|
| 36 |
+
|
| 37 |
+
def flip_image(x):
|
| 38 |
+
return np.fliplr(x)
|
| 39 |
+
|
| 40 |
+
with gr.Blocks() as demo:
|
| 41 |
+
gr.Markdown("Flip text or image files using this demo.")
|
| 42 |
+
with gr.Tab("Flip Text"):
|
| 43 |
+
text_input = gr.Textbox()
|
| 44 |
+
text_output = gr.Textbox()
|
| 45 |
+
text_button = gr.Button("Flip")
|
| 46 |
+
with gr.Tab("Flip Image"):
|
| 47 |
+
with gr.Row():
|
| 48 |
+
image_input = gr.Image()
|
| 49 |
+
image_output = gr.Image()
|
| 50 |
+
image_button = gr.Button("Flip")
|
| 51 |
+
|
| 52 |
+
with gr.Accordion("Open for More!", open=False):
|
| 53 |
+
gr.Markdown("Look at me...")
|
| 54 |
+
temp_slider = gr.Slider(
|
| 55 |
+
0, 1,
|
| 56 |
+
value=0.1,
|
| 57 |
+
step=0.1,
|
| 58 |
+
interactive=True,
|
| 59 |
+
label="Slide me",
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
text_button.click(flip_text, inputs=text_input, outputs=text_output)
|
| 63 |
+
image_button.click(flip_image, inputs=image_input, outputs=image_output)
|
| 64 |
+
|
| 65 |
+
demo.launch()
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
## Blocks Form
|
| 69 |
+
|
| 70 |
+
```python
|
| 71 |
+
import gradio as gr
|
| 72 |
+
|
| 73 |
+
with gr.Blocks() as demo:
|
| 74 |
+
name_box = gr.Textbox(label="Name")
|
| 75 |
+
age_box = gr.Number(label="Age", minimum=0, maximum=100)
|
| 76 |
+
symptoms_box = gr.CheckboxGroup(["Cough", "Fever", "Runny Nose"])
|
| 77 |
+
submit_btn = gr.Button("Submit")
|
| 78 |
+
|
| 79 |
+
with gr.Column(visible=False) as output_col:
|
| 80 |
+
diagnosis_box = gr.Textbox(label="Diagnosis")
|
| 81 |
+
patient_summary_box = gr.Textbox(label="Patient Summary")
|
| 82 |
+
|
| 83 |
+
def submit(name, age, symptoms):
|
| 84 |
+
return {
|
| 85 |
+
submit_btn: gr.Button(visible=False),
|
| 86 |
+
output_col: gr.Column(visible=True),
|
| 87 |
+
diagnosis_box: "covid" if "Cough" in symptoms else "flu",
|
| 88 |
+
patient_summary_box: f"{name}, {age} y/o",
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
submit_btn.click(
|
| 92 |
+
submit,
|
| 93 |
+
[name_box, age_box, symptoms_box],
|
| 94 |
+
[submit_btn, diagnosis_box, patient_summary_box, output_col],
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
demo.launch()
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
## Blocks Hello
|
| 101 |
+
|
| 102 |
+
```python
|
| 103 |
+
import gradio as gr
|
| 104 |
+
|
| 105 |
+
def welcome(name):
|
| 106 |
+
return f"Welcome to Gradio, {name}!"
|
| 107 |
+
|
| 108 |
+
with gr.Blocks() as demo:
|
| 109 |
+
gr.Markdown(
|
| 110 |
+
"""
|
| 111 |
+
# Hello World!
|
| 112 |
+
Start typing below to see the output.
|
| 113 |
+
""")
|
| 114 |
+
inp = gr.Textbox(placeholder="What is your name?")
|
| 115 |
+
out = gr.Textbox()
|
| 116 |
+
inp.change(welcome, inp, out)
|
| 117 |
+
|
| 118 |
+
demo.launch()
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
## Blocks Layout
|
| 122 |
+
|
| 123 |
+
```python
|
| 124 |
+
import gradio as gr
|
| 125 |
+
|
| 126 |
+
demo = gr.Blocks()
|
| 127 |
+
|
| 128 |
+
with demo:
|
| 129 |
+
with gr.Row():
|
| 130 |
+
gr.Image(interactive=True, scale=2)
|
| 131 |
+
gr.Image()
|
| 132 |
+
with gr.Row():
|
| 133 |
+
gr.Textbox(label="Text")
|
| 134 |
+
gr.Number(label="Count", scale=2)
|
| 135 |
+
gr.Radio(choices=["One", "Two"])
|
| 136 |
+
with gr.Row():
|
| 137 |
+
gr.Button("500", scale=0, min_width=500)
|
| 138 |
+
gr.Button("A", scale=0)
|
| 139 |
+
gr.Button("grow")
|
| 140 |
+
with gr.Row():
|
| 141 |
+
gr.Textbox()
|
| 142 |
+
gr.Textbox()
|
| 143 |
+
gr.Button()
|
| 144 |
+
with gr.Row():
|
| 145 |
+
with gr.Row():
|
| 146 |
+
with gr.Column():
|
| 147 |
+
gr.Textbox(label="Text")
|
| 148 |
+
gr.Number(label="Count")
|
| 149 |
+
gr.Radio(choices=["One", "Two"])
|
| 150 |
+
gr.Image()
|
| 151 |
+
with gr.Column():
|
| 152 |
+
gr.Image(interactive=True)
|
| 153 |
+
gr.Image()
|
| 154 |
+
gr.Image()
|
| 155 |
+
gr.Textbox(label="Text")
|
| 156 |
+
gr.Number(label="Count")
|
| 157 |
+
gr.Radio(choices=["One", "Two"])
|
| 158 |
+
|
| 159 |
+
demo.launch()
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
## Calculator
|
| 163 |
+
|
| 164 |
+
```python
|
| 165 |
+
import gradio as gr
|
| 166 |
+
|
| 167 |
+
def calculator(num1, operation, num2):
|
| 168 |
+
if operation == "add":
|
| 169 |
+
return num1 + num2
|
| 170 |
+
elif operation == "subtract":
|
| 171 |
+
return num1 - num2
|
| 172 |
+
elif operation == "multiply":
|
| 173 |
+
return num1 * num2
|
| 174 |
+
elif operation == "divide":
|
| 175 |
+
if num2 == 0:
|
| 176 |
+
raise gr.Error("Cannot divide by zero!")
|
| 177 |
+
return num1 / num2
|
| 178 |
+
|
| 179 |
+
demo = gr.Interface(
|
| 180 |
+
calculator,
|
| 181 |
+
[
|
| 182 |
+
"number",
|
| 183 |
+
gr.Radio(["add", "subtract", "multiply", "divide"]),
|
| 184 |
+
"number"
|
| 185 |
+
],
|
| 186 |
+
"number",
|
| 187 |
+
examples=[
|
| 188 |
+
[45, "add", 3],
|
| 189 |
+
[3.14, "divide", 2],
|
| 190 |
+
[144, "multiply", 2.5],
|
| 191 |
+
[0, "subtract", 1.2],
|
| 192 |
+
],
|
| 193 |
+
title="Toy Calculator",
|
| 194 |
+
description="Here's a sample toy calculator.",
|
| 195 |
+
api_name="predict"
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
demo.launch()
|
| 199 |
+
```
|
| 200 |
+
|
| 201 |
+
## Chatbot Simple
|
| 202 |
+
|
| 203 |
+
```python
|
| 204 |
+
import gradio as gr
|
| 205 |
+
import random
|
| 206 |
+
import time
|
| 207 |
+
|
| 208 |
+
with gr.Blocks() as demo:
|
| 209 |
+
chatbot = gr.Chatbot()
|
| 210 |
+
msg = gr.Textbox()
|
| 211 |
+
clear = gr.ClearButton([msg, chatbot])
|
| 212 |
+
|
| 213 |
+
def respond(message, chat_history):
|
| 214 |
+
bot_message = random.choice(["How are you?", "Today is a great day", "I'm very hungry"])
|
| 215 |
+
chat_history.append({"role": "user", "content": message})
|
| 216 |
+
chat_history.append({"role": "assistant", "content": bot_message})
|
| 217 |
+
time.sleep(2)
|
| 218 |
+
return "", chat_history
|
| 219 |
+
|
| 220 |
+
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
| 221 |
+
|
| 222 |
+
demo.launch()
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
## Chatbot Streaming
|
| 226 |
+
|
| 227 |
+
```python
|
| 228 |
+
import gradio as gr
|
| 229 |
+
import random
|
| 230 |
+
import time
|
| 231 |
+
|
| 232 |
+
with gr.Blocks() as demo:
|
| 233 |
+
chatbot = gr.Chatbot()
|
| 234 |
+
msg = gr.Textbox()
|
| 235 |
+
clear = gr.Button("Clear")
|
| 236 |
+
|
| 237 |
+
def user(user_message, history: list):
|
| 238 |
+
return "", history + [{"role": "user", "content": user_message}]
|
| 239 |
+
|
| 240 |
+
def bot(history: list):
|
| 241 |
+
bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"])
|
| 242 |
+
history.append({"role": "assistant", "content": ""})
|
| 243 |
+
for character in bot_message:
|
| 244 |
+
history[-1]['content'] += character
|
| 245 |
+
time.sleep(0.05)
|
| 246 |
+
yield history
|
| 247 |
+
|
| 248 |
+
msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
|
| 249 |
+
bot, chatbot, chatbot
|
| 250 |
+
)
|
| 251 |
+
clear.click(lambda: None, None, chatbot, queue=False)
|
| 252 |
+
|
| 253 |
+
demo.launch()
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
## Custom Css
|
| 257 |
+
|
| 258 |
+
```python
|
| 259 |
+
import gradio as gr
|
| 260 |
+
|
| 261 |
+
with gr.Blocks() as demo:
|
| 262 |
+
with gr.Column(elem_classes="cool-col"):
|
| 263 |
+
gr.Markdown("### Gradio Demo with Custom CSS", elem_classes="darktest")
|
| 264 |
+
gr.Markdown(
|
| 265 |
+
elem_classes="markdown",
|
| 266 |
+
value="Resize the browser window to see the CSS media query in action.",
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
if __name__ == "__main__":
|
| 270 |
+
demo.launch(css_paths=["demo/custom_css/custom_css.css"])
|
| 271 |
+
```
|
| 272 |
+
|
| 273 |
+
## Fake Diffusion
|
| 274 |
+
|
| 275 |
+
```python
|
| 276 |
+
import gradio as gr
|
| 277 |
+
import numpy as np
|
| 278 |
+
import time
|
| 279 |
+
|
| 280 |
+
def fake_diffusion(steps):
|
| 281 |
+
rng = np.random.default_rng()
|
| 282 |
+
for i in range(steps):
|
| 283 |
+
time.sleep(1)
|
| 284 |
+
image = rng.random(size=(600, 600, 3))
|
| 285 |
+
yield image
|
| 286 |
+
image = np.ones((1000,1000,3), np.uint8)
|
| 287 |
+
image[:] = [255, 124, 0]
|
| 288 |
+
yield image
|
| 289 |
+
|
| 290 |
+
demo = gr.Interface(fake_diffusion,
|
| 291 |
+
inputs=gr.Slider(1, 10, 3, step=1),
|
| 292 |
+
outputs="image",
|
| 293 |
+
api_name="predict")
|
| 294 |
+
|
| 295 |
+
demo.launch()
|
| 296 |
+
```
|
| 297 |
+
|
| 298 |
+
## Hello World
|
| 299 |
+
|
| 300 |
+
```python
|
| 301 |
+
import gradio as gr
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def greet(name):
|
| 305 |
+
return "Hello " + name + "!"
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
demo = gr.Interface(fn=greet, inputs="textbox", outputs="textbox", api_name="predict")
|
| 309 |
+
|
| 310 |
+
demo.launch()
|
| 311 |
+
```
|
| 312 |
+
|
| 313 |
+
## Image Editor
|
| 314 |
+
|
| 315 |
+
```python
|
| 316 |
+
import gradio as gr
|
| 317 |
+
import time
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
def sleep(im):
|
| 321 |
+
time.sleep(5)
|
| 322 |
+
return [im["background"], im["layers"][0], im["layers"][1], im["composite"]]
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
def predict(im):
|
| 326 |
+
return im["composite"]
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
with gr.Blocks() as demo:
|
| 330 |
+
with gr.Row():
|
| 331 |
+
im = gr.ImageEditor(
|
| 332 |
+
type="numpy",
|
| 333 |
+
)
|
| 334 |
+
im_preview = gr.Image()
|
| 335 |
+
n_upload = gr.Number(0, label="Number of upload events", step=1)
|
| 336 |
+
n_change = gr.Number(0, label="Number of change events", step=1)
|
| 337 |
+
n_input = gr.Number(0, label="Number of input events", step=1)
|
| 338 |
+
|
| 339 |
+
im.upload(lambda x: x + 1, outputs=n_upload, inputs=n_upload)
|
| 340 |
+
im.change(lambda x: x + 1, outputs=n_change, inputs=n_change)
|
| 341 |
+
im.input(lambda x: x + 1, outputs=n_input, inputs=n_input)
|
| 342 |
+
im.change(predict, outputs=im_preview, inputs=im, show_progress="hidden")
|
| 343 |
+
|
| 344 |
+
demo.launch()
|
| 345 |
+
```
|
| 346 |
+
|
| 347 |
+
## On Listener Decorator
|
| 348 |
+
|
| 349 |
+
```python
|
| 350 |
+
import gradio as gr
|
| 351 |
+
|
| 352 |
+
with gr.Blocks() as demo:
|
| 353 |
+
name = gr.Textbox(label="Name")
|
| 354 |
+
output = gr.Textbox(label="Output Box")
|
| 355 |
+
greet_btn = gr.Button("Greet")
|
| 356 |
+
|
| 357 |
+
@gr.on(triggers=[name.submit, greet_btn.click], inputs=name, outputs=output)
|
| 358 |
+
def greet(name):
|
| 359 |
+
return "Hello " + name + "!"
|
| 360 |
+
|
| 361 |
+
demo.launch()
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
## Render Merge
|
| 365 |
+
|
| 366 |
+
```python
|
| 367 |
+
import gradio as gr
|
| 368 |
+
import time
|
| 369 |
+
|
| 370 |
+
with gr.Blocks() as demo:
|
| 371 |
+
text_count = gr.Slider(1, 5, value=1, step=1, label="Textbox Count")
|
| 372 |
+
|
| 373 |
+
@gr.render(inputs=text_count)
|
| 374 |
+
def render_count(count):
|
| 375 |
+
boxes = []
|
| 376 |
+
for i in range(count):
|
| 377 |
+
box = gr.Textbox(label=f"Box {i}")
|
| 378 |
+
boxes.append(box)
|
| 379 |
+
|
| 380 |
+
def merge(*args):
|
| 381 |
+
time.sleep(0.2) # simulate a delay
|
| 382 |
+
return " ".join(args)
|
| 383 |
+
|
| 384 |
+
merge_btn.click(merge, boxes, output)
|
| 385 |
+
|
| 386 |
+
def clear():
|
| 387 |
+
time.sleep(0.2) # simulate a delay
|
| 388 |
+
return [" "] * count
|
| 389 |
+
|
| 390 |
+
clear_btn.click(clear, None, boxes)
|
| 391 |
+
|
| 392 |
+
def countup():
|
| 393 |
+
time.sleep(0.2) # simulate a delay
|
| 394 |
+
return list(range(count))
|
| 395 |
+
|
| 396 |
+
count_btn.click(countup, None, boxes, queue=False)
|
| 397 |
+
|
| 398 |
+
with gr.Row():
|
| 399 |
+
merge_btn = gr.Button("Merge")
|
| 400 |
+
clear_btn = gr.Button("Clear")
|
| 401 |
+
count_btn = gr.Button("Count")
|
| 402 |
+
|
| 403 |
+
output = gr.Textbox()
|
| 404 |
+
|
| 405 |
+
demo.launch()
|
| 406 |
+
```
|
| 407 |
+
|
| 408 |
+
## Reverse Audio 2
|
| 409 |
+
|
| 410 |
+
```python
|
| 411 |
+
import gradio as gr
|
| 412 |
+
import numpy as np
|
| 413 |
+
|
| 414 |
+
def reverse_audio(audio):
|
| 415 |
+
sr, data = audio
|
| 416 |
+
return (sr, np.flipud(data))
|
| 417 |
+
|
| 418 |
+
demo = gr.Interface(fn=reverse_audio,
|
| 419 |
+
inputs="microphone",
|
| 420 |
+
outputs="audio", api_name="predict")
|
| 421 |
+
|
| 422 |
+
demo.launch()
|
| 423 |
+
```
|
| 424 |
+
|
| 425 |
+
## Sepia Filter
|
| 426 |
+
|
| 427 |
+
```python
|
| 428 |
+
import numpy as np
|
| 429 |
+
import gradio as gr
|
| 430 |
+
|
| 431 |
+
def sepia(input_img):
|
| 432 |
+
sepia_filter = np.array([
|
| 433 |
+
[0.393, 0.769, 0.189],
|
| 434 |
+
[0.349, 0.686, 0.168],
|
| 435 |
+
[0.272, 0.534, 0.131]
|
| 436 |
+
])
|
| 437 |
+
sepia_img = input_img.dot(sepia_filter.T)
|
| 438 |
+
sepia_img /= sepia_img.max()
|
| 439 |
+
return sepia_img
|
| 440 |
+
|
| 441 |
+
demo = gr.Interface(sepia, gr.Image(), "image", api_name="predict")
|
| 442 |
+
demo.launch()
|
| 443 |
+
```
|
| 444 |
+
|
| 445 |
+
## Sort Records
|
| 446 |
+
|
| 447 |
+
```python
|
| 448 |
+
import gradio as gr
|
| 449 |
+
|
| 450 |
+
def sort_records(records):
|
| 451 |
+
return records.sort("Quantity")
|
| 452 |
+
|
| 453 |
+
demo = gr.Interface(
|
| 454 |
+
sort_records,
|
| 455 |
+
gr.Dataframe(
|
| 456 |
+
headers=["Item", "Quantity"],
|
| 457 |
+
datatype=["str", "number"],
|
| 458 |
+
row_count=3,
|
| 459 |
+
column_count=2,
|
| 460 |
+
column_limits=(2, 2),
|
| 461 |
+
type="polars"
|
| 462 |
+
),
|
| 463 |
+
"dataframe",
|
| 464 |
+
description="Sort by Quantity"
|
| 465 |
+
)
|
| 466 |
+
|
| 467 |
+
demo.launch()
|
| 468 |
+
```
|
| 469 |
+
|
| 470 |
+
## Streaming Simple
|
| 471 |
+
|
| 472 |
+
```python
|
| 473 |
+
import gradio as gr
|
| 474 |
+
|
| 475 |
+
with gr.Blocks() as demo:
|
| 476 |
+
with gr.Row():
|
| 477 |
+
with gr.Column():
|
| 478 |
+
input_img = gr.Image(label="Input", sources="webcam")
|
| 479 |
+
with gr.Column():
|
| 480 |
+
output_img = gr.Image(label="Output")
|
| 481 |
+
input_img.stream(lambda s: s, input_img, output_img, time_limit=15, stream_every=0.1, concurrency_limit=30)
|
| 482 |
+
|
| 483 |
+
if __name__ == "__main__":
|
| 484 |
+
|
| 485 |
+
demo.launch()
|
| 486 |
+
```
|
| 487 |
+
|
| 488 |
+
## Tabbed Interface Lite
|
| 489 |
+
|
| 490 |
+
```python
|
| 491 |
+
import gradio as gr
|
| 492 |
+
|
| 493 |
+
hello_world = gr.Interface(lambda name: "Hello " + name, "text", "text", api_name="predict")
|
| 494 |
+
bye_world = gr.Interface(lambda name: "Bye " + name, "text", "text", api_name="predict")
|
| 495 |
+
chat = gr.ChatInterface(lambda *args: "Hello " + args[0], api_name="chat")
|
| 496 |
+
|
| 497 |
+
demo = gr.TabbedInterface([hello_world, bye_world, chat], ["Hello World", "Bye World", "Chat"])
|
| 498 |
+
|
| 499 |
+
demo.launch()
|
| 500 |
+
```
|
| 501 |
+
|
| 502 |
+
## Tax Calculator
|
| 503 |
+
|
| 504 |
+
```python
|
| 505 |
+
import gradio as gr
|
| 506 |
+
|
| 507 |
+
def tax_calculator(income, marital_status, assets):
|
| 508 |
+
tax_brackets = [(10, 0), (25, 8), (60, 12), (120, 20), (250, 30)]
|
| 509 |
+
total_deductible = sum(cost for cost, deductible in zip(assets["Cost"], assets["Deductible"]) if deductible)
|
| 510 |
+
taxable_income = income - total_deductible
|
| 511 |
+
|
| 512 |
+
total_tax = 0
|
| 513 |
+
for bracket, rate in tax_brackets:
|
| 514 |
+
if taxable_income > bracket:
|
| 515 |
+
total_tax += (taxable_income - bracket) * rate / 100
|
| 516 |
+
|
| 517 |
+
if marital_status == "Married":
|
| 518 |
+
total_tax *= 0.75
|
| 519 |
+
elif marital_status == "Divorced":
|
| 520 |
+
total_tax *= 0.8
|
| 521 |
+
|
| 522 |
+
return round(total_tax)
|
| 523 |
+
|
| 524 |
+
demo = gr.Interface(
|
| 525 |
+
tax_calculator,
|
| 526 |
+
[
|
| 527 |
+
"number",
|
| 528 |
+
gr.Radio(["Single", "Married", "Divorced"]),
|
| 529 |
+
gr.Dataframe(
|
| 530 |
+
headers=["Item", "Cost", "Deductible"],
|
| 531 |
+
datatype=["str", "number", "bool"],
|
| 532 |
+
label="Assets Purchased this Year",
|
| 533 |
+
),
|
| 534 |
+
],
|
| 535 |
+
gr.Number(label="Tax due"),
|
| 536 |
+
examples=[
|
| 537 |
+
[10000, "Married", [["Suit", 5000, True], ["Laptop (for work)", 800, False], ["Car", 1800, True]]],
|
| 538 |
+
[80000, "Single", [["Suit", 800, True], ["Watch", 1800, True], ["Food", 800, True]]],
|
| 539 |
+
],
|
| 540 |
+
live=True,
|
| 541 |
+
api_name="predict"
|
| 542 |
+
)
|
| 543 |
+
|
| 544 |
+
demo.launch()
|
| 545 |
+
```
|
| 546 |
+
|
| 547 |
+
## Timer Simple
|
| 548 |
+
|
| 549 |
+
```python
|
| 550 |
+
import gradio as gr
|
| 551 |
+
import random
|
| 552 |
+
import time
|
| 553 |
+
|
| 554 |
+
with gr.Blocks() as demo:
|
| 555 |
+
timer = gr.Timer(1)
|
| 556 |
+
timestamp = gr.Number(label="Time")
|
| 557 |
+
timer.tick(lambda: round(time.time()), outputs=timestamp, api_name="timestamp")
|
| 558 |
+
|
| 559 |
+
number = gr.Number(lambda: random.randint(1, 10), every=timer, label="Random Number")
|
| 560 |
+
with gr.Row():
|
| 561 |
+
gr.Button("Start").click(lambda: gr.Timer(active=True), None, timer)
|
| 562 |
+
gr.Button("Stop").click(lambda: gr.Timer(active=False), None, timer)
|
| 563 |
+
gr.Button("Go Fast").click(lambda: 0.2, None, timer)
|
| 564 |
+
|
| 565 |
+
if __name__ == "__main__":
|
| 566 |
+
demo.launch()
|
| 567 |
+
```
|
| 568 |
+
|
| 569 |
+
## Variable Outputs
|
| 570 |
+
|
| 571 |
+
```python
|
| 572 |
+
import gradio as gr
|
| 573 |
+
|
| 574 |
+
max_textboxes = 10
|
| 575 |
+
|
| 576 |
+
def variable_outputs(k):
|
| 577 |
+
k = int(k)
|
| 578 |
+
return [gr.Textbox(visible=True)]*k + [gr.Textbox(visible=False)]*(max_textboxes-k)
|
| 579 |
+
|
| 580 |
+
with gr.Blocks() as demo:
|
| 581 |
+
s = gr.Slider(1, max_textboxes, value=max_textboxes, step=1, label="How many textboxes to show:")
|
| 582 |
+
textboxes = []
|
| 583 |
+
for i in range(max_textboxes):
|
| 584 |
+
t = gr.Textbox(f"Textbox {i}")
|
| 585 |
+
textboxes.append(t)
|
| 586 |
+
|
| 587 |
+
s.change(variable_outputs, s, textboxes)
|
| 588 |
+
|
| 589 |
+
if __name__ == "__main__":
|
| 590 |
+
demo.launch()
|
| 591 |
+
```
|
| 592 |
+
|
| 593 |
+
## Video Identity
|
| 594 |
+
|
| 595 |
+
```python
|
| 596 |
+
import gradio as gr
|
| 597 |
+
from gradio.media import get_video
|
| 598 |
+
|
| 599 |
+
def video_identity(video):
|
| 600 |
+
return video
|
| 601 |
+
|
| 602 |
+
# get_video() returns file paths to sample media included with Gradio
|
| 603 |
+
demo = gr.Interface(video_identity,
|
| 604 |
+
gr.Video(),
|
| 605 |
+
"playable_video",
|
| 606 |
+
examples=[
|
| 607 |
+
get_video("world.mp4")
|
| 608 |
+
],
|
| 609 |
+
cache_examples=True,
|
| 610 |
+
api_name="predict",)
|
| 611 |
+
|
| 612 |
+
demo.launch()
|
| 613 |
+
```
|
.agents/skills/huggingface-llm-trainer/.hf-skill-manifest.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"installed_revision": "d0d3f4301358fc1351d408a58596d5644b5a8114",
|
| 3 |
+
"schema_version": 1
|
| 4 |
+
}
|
.agents/skills/huggingface-llm-trainer/SKILL.md
ADDED
|
@@ -0,0 +1,738 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: huggingface-llm-trainer
|
| 3 |
+
description: Train or fine-tune language and vision models using TRL (Transformer Reinforcement Learning) or Unsloth with Hugging Face Jobs infrastructure. Covers SFT, DPO, GRPO and reward modeling training methods, plus GGUF conversion for local deployment. Includes guidance on the TRL Jobs package, UV scripts with PEP 723 format, dataset preparation and validation, hardware selection, cost estimation, Trackio monitoring, Hub authentication, model selection/leaderboards and model persistence. Use for tasks involving cloud GPU training, GGUF conversion, or when users mention training on Hugging Face Jobs without local GPU setup.
|
| 4 |
+
license: Complete terms in LICENSE.txt
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
# TRL Training on Hugging Face Jobs
|
| 8 |
+
|
| 9 |
+
## Overview
|
| 10 |
+
|
| 11 |
+
Train language models using TRL (Transformer Reinforcement Learning) on fully managed Hugging Face infrastructure. No local GPU setup requiredβmodels train on cloud GPUs and results are automatically saved to the Hugging Face Hub.
|
| 12 |
+
|
| 13 |
+
**TRL provides multiple training methods:**
|
| 14 |
+
- **SFT** (Supervised Fine-Tuning) - Standard instruction tuning
|
| 15 |
+
- **DPO** (Direct Preference Optimization) - Alignment from preference data
|
| 16 |
+
- **GRPO** (Group Relative Policy Optimization) - Online RL training
|
| 17 |
+
- **Reward Modeling** - Train reward models for RLHF
|
| 18 |
+
|
| 19 |
+
**For detailed TRL method documentation:**
|
| 20 |
+
```python
|
| 21 |
+
hf_doc_search("your query", product="trl")
|
| 22 |
+
hf_doc_fetch("https://huggingface.co/docs/trl/sft_trainer") # SFT
|
| 23 |
+
hf_doc_fetch("https://huggingface.co/docs/trl/dpo_trainer") # DPO
|
| 24 |
+
# etc.
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
**See also:** `references/training_methods.md` for method overviews and selection guidance
|
| 28 |
+
|
| 29 |
+
## When to Use This Skill
|
| 30 |
+
|
| 31 |
+
Use this skill when users want to:
|
| 32 |
+
- Fine-tune language models on cloud GPUs without local infrastructure
|
| 33 |
+
- Train with TRL methods (SFT, DPO, GRPO, etc.)
|
| 34 |
+
- Run training jobs on Hugging Face Jobs infrastructure
|
| 35 |
+
- Convert trained models to GGUF for local deployment (Ollama, LM Studio, llama.cpp)
|
| 36 |
+
- Ensure trained models are permanently saved to the Hub
|
| 37 |
+
- Use modern workflows with optimized defaults
|
| 38 |
+
|
| 39 |
+
### When to Use Unsloth
|
| 40 |
+
|
| 41 |
+
Use **Unsloth** (`references/unsloth.md`) instead of standard TRL when:
|
| 42 |
+
- **Limited GPU memory** - Unsloth uses ~60% less VRAM
|
| 43 |
+
- **Speed matters** - Unsloth is ~2x faster
|
| 44 |
+
- Training **large models (>13B)** - memory efficiency is critical
|
| 45 |
+
- Training **Vision-Language Models (VLMs)** - Unsloth has `FastVisionModel` support
|
| 46 |
+
|
| 47 |
+
See `references/unsloth.md` for complete Unsloth documentation and `scripts/unsloth_sft_example.py` for a production-ready training script.
|
| 48 |
+
|
| 49 |
+
## Key Directives
|
| 50 |
+
|
| 51 |
+
When assisting with training jobs:
|
| 52 |
+
|
| 53 |
+
1. **ALWAYS use `hf_jobs()` MCP tool** - Submit jobs using `hf_jobs("uv", {...})`, NOT bash `trl-jobs` commands. The `script` parameter accepts Python code directly. Do NOT save to local files unless the user explicitly requests it. Pass the script content as a string to `hf_jobs()`. If user asks to "train a model", "fine-tune", or similar requests, you MUST create the training script AND submit the job immediately using `hf_jobs()`.
|
| 54 |
+
|
| 55 |
+
2. **Always include Trackio** - Every training script should include Trackio for real-time monitoring. Use example scripts in `scripts/` as templates.
|
| 56 |
+
|
| 57 |
+
3. **Provide job details after submission** - After submitting, provide job ID, monitoring URL, estimated time, and note that the user can request status checks later.
|
| 58 |
+
|
| 59 |
+
4. **Use example scripts as templates** - Reference `scripts/train_sft_example.py`, `scripts/train_dpo_example.py`, etc. as starting points.
|
| 60 |
+
|
| 61 |
+
## Local Script Execution
|
| 62 |
+
|
| 63 |
+
Repository scripts use PEP 723 inline dependencies. Run them with `uv run`:
|
| 64 |
+
```bash
|
| 65 |
+
uv run scripts/estimate_cost.py --help
|
| 66 |
+
uv run scripts/dataset_inspector.py --help
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
## Prerequisites Checklist
|
| 70 |
+
|
| 71 |
+
Before starting any training job, verify:
|
| 72 |
+
|
| 73 |
+
### β
**Account & Authentication**
|
| 74 |
+
- Hugging Face Account with [Pro](https://hf.co/pro), [Team](https://hf.co/enterprise), or [Enterprise](https://hf.co/enterprise) plan (Jobs require paid plan)
|
| 75 |
+
- Authenticated login: Check with `hf_whoami()`
|
| 76 |
+
- **HF_TOKEN for Hub Push** β οΈ CRITICAL - Training environment is ephemeral, must push to Hub or ALL training results are lost
|
| 77 |
+
- Token must have write permissions
|
| 78 |
+
- **MUST pass `secrets={"HF_TOKEN": "$HF_TOKEN"}` in job config** to make token available (the `$HF_TOKEN` syntax
|
| 79 |
+
references your actual token value)
|
| 80 |
+
|
| 81 |
+
### β
**Dataset Requirements**
|
| 82 |
+
- Dataset must exist on Hub or be loadable via `datasets.load_dataset()`
|
| 83 |
+
- Format must match training method (SFT: "messages"/text/prompt-completion; DPO: chosen/rejected; GRPO: prompt-only)
|
| 84 |
+
- **ALWAYS validate unknown datasets** before GPU training to prevent format failures (see Dataset Validation section below)
|
| 85 |
+
- Size appropriate for hardware (Demo: 50-100 examples on t4-small; Production: 1K-10K+ on a10g-large/a100-large)
|
| 86 |
+
|
| 87 |
+
### β οΈ **Critical Settings**
|
| 88 |
+
- **Timeout must exceed expected training time** - Default 30min is TOO SHORT for most training. Minimum recommended: 1-2 hours. Job fails and loses all progress if timeout is exceeded.
|
| 89 |
+
- **Hub push must be enabled** - Config: `push_to_hub=True`, `hub_model_id="username/model-name"`; Job: `secrets={"HF_TOKEN": "$HF_TOKEN"}`
|
| 90 |
+
|
| 91 |
+
## Asynchronous Job Guidelines
|
| 92 |
+
|
| 93 |
+
**β οΈ IMPORTANT: Training jobs run asynchronously and can take hours**
|
| 94 |
+
|
| 95 |
+
### Action Required
|
| 96 |
+
|
| 97 |
+
**When user requests training:**
|
| 98 |
+
1. **Create the training script** with Trackio included (use `scripts/train_sft_example.py` as template)
|
| 99 |
+
2. **Submit immediately** using `hf_jobs()` MCP tool with script content inline - don't save to file unless user requests
|
| 100 |
+
3. **Report submission** with job ID, monitoring URL, and estimated time
|
| 101 |
+
4. **Wait for user** to request status checks - don't poll automatically
|
| 102 |
+
|
| 103 |
+
### Ground Rules
|
| 104 |
+
- **Jobs run in background** - Submission returns immediately; training continues independently
|
| 105 |
+
- **Initial logs delayed** - Can take 30-60 seconds for logs to appear
|
| 106 |
+
- **User checks status** - Wait for user to request status updates
|
| 107 |
+
- **Avoid polling** - Check logs only on user request; provide monitoring links instead
|
| 108 |
+
|
| 109 |
+
### After Submission
|
| 110 |
+
|
| 111 |
+
**Provide to user:**
|
| 112 |
+
- β
Job ID and monitoring URL
|
| 113 |
+
- β
Expected completion time
|
| 114 |
+
- β
Trackio dashboard URL
|
| 115 |
+
- β
Note that user can request status checks later
|
| 116 |
+
|
| 117 |
+
**Example Response:**
|
| 118 |
+
```
|
| 119 |
+
β
Job submitted successfully!
|
| 120 |
+
|
| 121 |
+
Job ID: abc123xyz
|
| 122 |
+
Monitor: https://huggingface.co/jobs/username/abc123xyz
|
| 123 |
+
|
| 124 |
+
Expected time: ~2 hours
|
| 125 |
+
Estimated cost: ~$10
|
| 126 |
+
|
| 127 |
+
The job is running in the background. Ask me to check status/logs when ready!
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
## Quick Start: Three Approaches
|
| 131 |
+
|
| 132 |
+
**π‘ Tip for Demos:** For quick demos on smaller GPUs (t4-small), omit `eval_dataset` and `eval_strategy` to save ~40% memory. You'll still see training loss and learning progress.
|
| 133 |
+
|
| 134 |
+
### Sequence Length Configuration
|
| 135 |
+
|
| 136 |
+
**TRL config classes use `max_length` (not `max_seq_length`)** to control tokenized sequence length:
|
| 137 |
+
|
| 138 |
+
```python
|
| 139 |
+
# β
CORRECT - If you need to set sequence length
|
| 140 |
+
SFTConfig(max_length=512) # Truncate sequences to 512 tokens
|
| 141 |
+
DPOConfig(max_length=2048) # Longer context (2048 tokens)
|
| 142 |
+
|
| 143 |
+
# β WRONG - This parameter doesn't exist
|
| 144 |
+
SFTConfig(max_seq_length=512) # TypeError!
|
| 145 |
+
```
|
| 146 |
+
|
| 147 |
+
**Default behavior:** `max_length=1024` (truncates from right). This works well for most training.
|
| 148 |
+
|
| 149 |
+
**When to override:**
|
| 150 |
+
- **Longer context**: Set higher (e.g., `max_length=2048`)
|
| 151 |
+
- **Memory constraints**: Set lower (e.g., `max_length=512`)
|
| 152 |
+
- **Vision models**: Set `max_length=None` (prevents cutting image tokens)
|
| 153 |
+
|
| 154 |
+
**Usually you don't need to set this parameter at all** - the examples below use the sensible default.
|
| 155 |
+
|
| 156 |
+
### Approach 1: UV Scripts (RecommendedβDefault Choice)
|
| 157 |
+
|
| 158 |
+
UV scripts use PEP 723 inline dependencies for clean, self-contained training. **This is the primary approach for Claude Code.**
|
| 159 |
+
|
| 160 |
+
```python
|
| 161 |
+
hf_jobs("uv", {
|
| 162 |
+
"script": """
|
| 163 |
+
# /// script
|
| 164 |
+
# dependencies = ["trl>=0.12.0", "peft>=0.7.0", "trackio"]
|
| 165 |
+
# ///
|
| 166 |
+
|
| 167 |
+
from datasets import load_dataset
|
| 168 |
+
from peft import LoraConfig
|
| 169 |
+
from trl import SFTTrainer, SFTConfig
|
| 170 |
+
import trackio
|
| 171 |
+
|
| 172 |
+
dataset = load_dataset("trl-lib/Capybara", split="train")
|
| 173 |
+
|
| 174 |
+
# Create train/eval split for monitoring
|
| 175 |
+
dataset_split = dataset.train_test_split(test_size=0.1, seed=42)
|
| 176 |
+
|
| 177 |
+
trainer = SFTTrainer(
|
| 178 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 179 |
+
train_dataset=dataset_split["train"],
|
| 180 |
+
eval_dataset=dataset_split["test"],
|
| 181 |
+
peft_config=LoraConfig(r=16, lora_alpha=32),
|
| 182 |
+
args=SFTConfig(
|
| 183 |
+
output_dir="my-model",
|
| 184 |
+
push_to_hub=True,
|
| 185 |
+
hub_model_id="username/my-model",
|
| 186 |
+
num_train_epochs=3,
|
| 187 |
+
eval_strategy="steps",
|
| 188 |
+
eval_steps=50,
|
| 189 |
+
report_to="trackio",
|
| 190 |
+
project="meaningful_prject_name", # project name for the training name (trackio)
|
| 191 |
+
run_name="meaningful_run_name", # descriptive name for the specific training run (trackio)
|
| 192 |
+
)
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
trainer.train()
|
| 196 |
+
trainer.push_to_hub()
|
| 197 |
+
""",
|
| 198 |
+
"flavor": "a10g-large",
|
| 199 |
+
"timeout": "2h",
|
| 200 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 201 |
+
})
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
**Benefits:** Direct MCP tool usage, clean code, dependencies declared inline (PEP 723), no file saving required, full control
|
| 205 |
+
**When to use:** Default choice for all training tasks in Claude Code, custom training logic, any scenario requiring `hf_jobs()`
|
| 206 |
+
|
| 207 |
+
#### Working with Scripts
|
| 208 |
+
|
| 209 |
+
β οΈ **Important:** The `script` parameter accepts either inline code (as shown above) OR a URL. **Local file paths do NOT work.**
|
| 210 |
+
|
| 211 |
+
**Why local paths don't work:**
|
| 212 |
+
Jobs run in isolated Docker containers without access to your local filesystem. Scripts must be:
|
| 213 |
+
- Inline code (recommended for custom training)
|
| 214 |
+
- Publicly accessible URLs
|
| 215 |
+
- Private repo URLs (with HF_TOKEN)
|
| 216 |
+
|
| 217 |
+
**Common mistakes:**
|
| 218 |
+
```python
|
| 219 |
+
# β These will all fail
|
| 220 |
+
hf_jobs("uv", {"script": "train.py"})
|
| 221 |
+
hf_jobs("uv", {"script": "./scripts/train.py"})
|
| 222 |
+
hf_jobs("uv", {"script": "/path/to/train.py"})
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
**Correct approaches:**
|
| 226 |
+
```python
|
| 227 |
+
# β
Inline code (recommended)
|
| 228 |
+
hf_jobs("uv", {"script": "# /// script\n# dependencies = [...]\n# ///\n\n<your code>"})
|
| 229 |
+
|
| 230 |
+
# β
From Hugging Face Hub
|
| 231 |
+
hf_jobs("uv", {"script": "https://huggingface.co/user/repo/resolve/main/train.py"})
|
| 232 |
+
|
| 233 |
+
# β
From GitHub
|
| 234 |
+
hf_jobs("uv", {"script": "https://raw.githubusercontent.com/user/repo/main/train.py"})
|
| 235 |
+
|
| 236 |
+
# β
From Gist
|
| 237 |
+
hf_jobs("uv", {"script": "https://gist.githubusercontent.com/user/id/raw/train.py"})
|
| 238 |
+
```
|
| 239 |
+
|
| 240 |
+
**To use local scripts:** Upload to HF Hub first:
|
| 241 |
+
```bash
|
| 242 |
+
hf repos create my-training-scripts --type model
|
| 243 |
+
hf upload my-training-scripts ./train.py train.py
|
| 244 |
+
# Use: https://huggingface.co/USERNAME/my-training-scripts/resolve/main/train.py
|
| 245 |
+
```
|
| 246 |
+
|
| 247 |
+
### Approach 2: TRL Maintained Scripts (Official Examples)
|
| 248 |
+
|
| 249 |
+
TRL provides battle-tested scripts for all methods. Can be run from URLs:
|
| 250 |
+
|
| 251 |
+
```python
|
| 252 |
+
hf_jobs("uv", {
|
| 253 |
+
"script": "https://github.com/huggingface/trl/blob/main/trl/scripts/sft.py",
|
| 254 |
+
"script_args": [
|
| 255 |
+
"--model_name_or_path", "Qwen/Qwen2.5-0.5B",
|
| 256 |
+
"--dataset_name", "trl-lib/Capybara",
|
| 257 |
+
"--output_dir", "my-model",
|
| 258 |
+
"--push_to_hub",
|
| 259 |
+
"--hub_model_id", "username/my-model"
|
| 260 |
+
],
|
| 261 |
+
"flavor": "a10g-large",
|
| 262 |
+
"timeout": "2h",
|
| 263 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 264 |
+
})
|
| 265 |
+
```
|
| 266 |
+
|
| 267 |
+
**Benefits:** No code to write, maintained by TRL team, production-tested
|
| 268 |
+
**When to use:** Standard TRL training, quick experiments, don't need custom code
|
| 269 |
+
**Available:** Scripts are available from https://github.com/huggingface/trl/tree/main/examples/scripts
|
| 270 |
+
|
| 271 |
+
### Finding More UV Scripts on Hub
|
| 272 |
+
|
| 273 |
+
The `uv-scripts` organization provides ready-to-use UV scripts stored as datasets on Hugging Face Hub:
|
| 274 |
+
|
| 275 |
+
```python
|
| 276 |
+
# Discover available UV script collections
|
| 277 |
+
dataset_search({"author": "uv-scripts", "sort": "downloads", "limit": 20})
|
| 278 |
+
|
| 279 |
+
# Explore a specific collection
|
| 280 |
+
hub_repo_details(["uv-scripts/classification"], repo_type="dataset", include_readme=True)
|
| 281 |
+
```
|
| 282 |
+
|
| 283 |
+
**Popular collections:** ocr, classification, synthetic-data, vllm, dataset-creation
|
| 284 |
+
|
| 285 |
+
### Approach 3: HF Jobs CLI (Direct Terminal Commands)
|
| 286 |
+
|
| 287 |
+
When the `hf_jobs()` MCP tool is unavailable, use the `hf jobs` CLI directly.
|
| 288 |
+
|
| 289 |
+
**β οΈ CRITICAL: CLI Syntax Rules**
|
| 290 |
+
|
| 291 |
+
```bash
|
| 292 |
+
# β
CORRECT syntax - flags BEFORE script URL
|
| 293 |
+
hf jobs uv run --flavor a10g-large --timeout 2h --secrets HF_TOKEN "https://example.com/train.py"
|
| 294 |
+
|
| 295 |
+
# β WRONG - "run uv" instead of "uv run"
|
| 296 |
+
hf jobs run uv "https://example.com/train.py" --flavor a10g-large
|
| 297 |
+
|
| 298 |
+
# β WRONG - flags AFTER script URL (will be ignored!)
|
| 299 |
+
hf jobs uv run "https://example.com/train.py" --flavor a10g-large
|
| 300 |
+
|
| 301 |
+
# β WRONG - "--secret" instead of "--secrets" (plural)
|
| 302 |
+
hf jobs uv run --secret HF_TOKEN "https://example.com/train.py"
|
| 303 |
+
```
|
| 304 |
+
|
| 305 |
+
**Key syntax rules:**
|
| 306 |
+
1. Command order is `hf jobs uv run` (NOT `hf jobs run uv`)
|
| 307 |
+
2. All flags (`--flavor`, `--timeout`, `--secrets`) must come BEFORE the script URL
|
| 308 |
+
3. Use `--secrets` (plural), not `--secret`
|
| 309 |
+
4. Script URL must be the last positional argument
|
| 310 |
+
|
| 311 |
+
**Complete CLI example:**
|
| 312 |
+
```bash
|
| 313 |
+
hf jobs uv run \
|
| 314 |
+
--flavor a10g-large \
|
| 315 |
+
--timeout 2h \
|
| 316 |
+
--secrets HF_TOKEN \
|
| 317 |
+
"https://huggingface.co/user/repo/resolve/main/train.py"
|
| 318 |
+
```
|
| 319 |
+
|
| 320 |
+
**Check job status via CLI:**
|
| 321 |
+
```bash
|
| 322 |
+
hf jobs ps # List all jobs
|
| 323 |
+
hf jobs logs <job-id> # View logs
|
| 324 |
+
hf jobs inspect <job-id> # Job details
|
| 325 |
+
hf jobs cancel <job-id> # Cancel a job
|
| 326 |
+
```
|
| 327 |
+
|
| 328 |
+
### Approach 4: TRL Jobs Package (Simplified Training)
|
| 329 |
+
|
| 330 |
+
The `trl-jobs` package provides optimized defaults and one-liner training.
|
| 331 |
+
|
| 332 |
+
```bash
|
| 333 |
+
uvx trl-jobs sft \
|
| 334 |
+
--model_name Qwen/Qwen2.5-0.5B \
|
| 335 |
+
--dataset_name trl-lib/Capybara
|
| 336 |
+
|
| 337 |
+
```
|
| 338 |
+
|
| 339 |
+
**Benefits:** Pre-configured settings, automatic Trackio integration, automatic Hub push, one-line commands
|
| 340 |
+
**When to use:** User working in terminal directly (not Claude Code context), quick local experimentation
|
| 341 |
+
**Repository:** https://github.com/huggingface/trl-jobs
|
| 342 |
+
|
| 343 |
+
β οΈ **In Claude Code context, prefer using `hf_jobs()` MCP tool (Approach 1) when available.**
|
| 344 |
+
|
| 345 |
+
## Hardware Selection
|
| 346 |
+
|
| 347 |
+
| Model Size | Recommended Hardware | Cost (approx/hr) | Use Case |
|
| 348 |
+
|------------|---------------------|------------------|----------|
|
| 349 |
+
| <1B params | `t4-small` | ~$0.75 | Demos, quick tests only without eval steps |
|
| 350 |
+
| 1-3B params | `t4-medium`, `l4x1` | ~$1.50-2.50 | Development |
|
| 351 |
+
| 3-7B params | `a10g-small`, `a10g-large` | ~$3.50-5.00 | Production training |
|
| 352 |
+
| 7-13B params | `a10g-large`, `a100-large` | ~$5-10 | Large models (use LoRA) |
|
| 353 |
+
| 13B+ params | `a100-large`, `a10g-largex2` | ~$10-20 | Very large (use LoRA) |
|
| 354 |
+
|
| 355 |
+
**GPU Flavors:** cpu-basic/upgrade/performance/xl, t4-small/medium, l4x1/x4, a10g-small/large/largex2/largex4, a100-large, h100/h100x8
|
| 356 |
+
|
| 357 |
+
**Guidelines:**
|
| 358 |
+
- Use **LoRA/PEFT** for models >7B to reduce memory
|
| 359 |
+
- Multi-GPU automatically handled by TRL/Accelerate
|
| 360 |
+
- Start with smaller hardware for testing
|
| 361 |
+
|
| 362 |
+
**See:** `references/hardware_guide.md` for detailed specifications
|
| 363 |
+
|
| 364 |
+
## Critical: Saving Results to Hub
|
| 365 |
+
|
| 366 |
+
**β οΈ EPHEMERAL ENVIRONMENTβMUST PUSH TO HUB**
|
| 367 |
+
|
| 368 |
+
The Jobs environment is temporary. All files are deleted when the job ends. If the model isn't pushed to Hub, **ALL TRAINING IS LOST**.
|
| 369 |
+
|
| 370 |
+
### Required Configuration
|
| 371 |
+
|
| 372 |
+
**In training script/config:**
|
| 373 |
+
```python
|
| 374 |
+
SFTConfig(
|
| 375 |
+
push_to_hub=True,
|
| 376 |
+
hub_model_id="username/model-name", # MUST specify
|
| 377 |
+
hub_strategy="every_save", # Optional: push checkpoints
|
| 378 |
+
)
|
| 379 |
+
```
|
| 380 |
+
|
| 381 |
+
**In job submission:**
|
| 382 |
+
```python
|
| 383 |
+
{
|
| 384 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"} # Enables authentication
|
| 385 |
+
}
|
| 386 |
+
```
|
| 387 |
+
|
| 388 |
+
### Verification Checklist
|
| 389 |
+
|
| 390 |
+
Before submitting:
|
| 391 |
+
- [ ] `push_to_hub=True` set in config
|
| 392 |
+
- [ ] `hub_model_id` includes username/repo-name
|
| 393 |
+
- [ ] `secrets` parameter includes HF_TOKEN
|
| 394 |
+
- [ ] User has write access to target repo
|
| 395 |
+
|
| 396 |
+
**See:** `references/hub_saving.md` for detailed troubleshooting
|
| 397 |
+
|
| 398 |
+
## Timeout Management
|
| 399 |
+
|
| 400 |
+
**β οΈ DEFAULT: 30 MINUTESβTOO SHORT FOR TRAINING**
|
| 401 |
+
|
| 402 |
+
### Setting Timeouts
|
| 403 |
+
|
| 404 |
+
```python
|
| 405 |
+
{
|
| 406 |
+
"timeout": "2h" # 2 hours (formats: "90m", "2h", "1.5h", or seconds as integer)
|
| 407 |
+
}
|
| 408 |
+
```
|
| 409 |
+
|
| 410 |
+
### Timeout Guidelines
|
| 411 |
+
|
| 412 |
+
| Scenario | Recommended | Notes |
|
| 413 |
+
|----------|-------------|-------|
|
| 414 |
+
| Quick demo (50-100 examples) | 10-30 min | Verify setup |
|
| 415 |
+
| Development training | 1-2 hours | Small datasets |
|
| 416 |
+
| Production (3-7B model) | 4-6 hours | Full datasets |
|
| 417 |
+
| Large model with LoRA | 3-6 hours | Depends on dataset |
|
| 418 |
+
|
| 419 |
+
**Always add 20-30% buffer** for model/dataset loading, checkpoint saving, Hub push operations, and network delays.
|
| 420 |
+
|
| 421 |
+
**On timeout:** Job killed immediately, all unsaved progress lost, must restart from beginning
|
| 422 |
+
|
| 423 |
+
## Choose a Base Model (Model Selection)
|
| 424 |
+
|
| 425 |
+
**Identify models to train based on task type or benchmark results.**
|
| 426 |
+
|
| 427 |
+
Use `scripts/hf_benchmarks.py` to identify top-performing models for specific tasks. This helps the user select a model as the base for training, whilst keeping size and hardware constraints in mind.
|
| 428 |
+
|
| 429 |
+
```bash
|
| 430 |
+
# Get help on the benchmarks command:
|
| 431 |
+
uv run scripts/hf_benchmarks.py --help
|
| 432 |
+
```
|
| 433 |
+
|
| 434 |
+
### Example -- choosing an OCR base model
|
| 435 |
+
```bash
|
| 436 |
+
# Search for benchmarks containing whose name contains the text `ocr`
|
| 437 |
+
uv run scripts/hf_benchmarks.py search --query ocr
|
| 438 |
+
|
| 439 |
+
# Get the ranked leaderboard for the allenai/olmOCR-bench benchmark
|
| 440 |
+
uv run scripts/hf_benchmarks.py leaderboard allenai/olmOCR-bench
|
| 441 |
+
```
|
| 442 |
+
|
| 443 |
+
## Cost Estimation
|
| 444 |
+
|
| 445 |
+
**Offer to estimate cost when planning jobs with known parameters.** Use `scripts/estimate_cost.py`:
|
| 446 |
+
|
| 447 |
+
```bash
|
| 448 |
+
uv run scripts/estimate_cost.py \
|
| 449 |
+
--model meta-llama/Llama-2-7b-hf \
|
| 450 |
+
--dataset trl-lib/Capybara \
|
| 451 |
+
--hardware a10g-large \
|
| 452 |
+
--dataset-size 16000 \
|
| 453 |
+
--epochs 3
|
| 454 |
+
```
|
| 455 |
+
|
| 456 |
+
Output includes estimated time, cost, recommended timeout (with buffer), and optimization suggestions.
|
| 457 |
+
|
| 458 |
+
**When to offer:** User planning a job, asks about cost/time, choosing hardware, job will run >1 hour or cost >$5
|
| 459 |
+
|
| 460 |
+
## Example Training Scripts
|
| 461 |
+
|
| 462 |
+
**Production-ready templates with all best practices:**
|
| 463 |
+
|
| 464 |
+
Load these scripts for correctly:
|
| 465 |
+
|
| 466 |
+
- **`scripts/train_sft_example.py`** - Complete SFT training with Trackio, LoRA, checkpoints
|
| 467 |
+
- **`scripts/train_dpo_example.py`** - DPO training for preference learning
|
| 468 |
+
- **`scripts/train_grpo_example.py`** - GRPO training for online RL
|
| 469 |
+
|
| 470 |
+
These scripts demonstrate proper Hub saving, Trackio integration, checkpoint management, and optimized parameters. Pass their content inline to `hf_jobs()` or use as templates for custom scripts.
|
| 471 |
+
|
| 472 |
+
## Monitoring and Tracking
|
| 473 |
+
|
| 474 |
+
**Trackio** provides real-time metrics visualization. See `references/trackio_guide.md` for complete setup guide.
|
| 475 |
+
|
| 476 |
+
**Key points:**
|
| 477 |
+
- Add `trackio` to dependencies
|
| 478 |
+
- Configure trainer with `report_to="trackio" and run_name="meaningful_name"`
|
| 479 |
+
|
| 480 |
+
### Trackio Configuration Defaults
|
| 481 |
+
|
| 482 |
+
**Use sensible defaults unless user specifies otherwise.** When generating training scripts with Trackio:
|
| 483 |
+
|
| 484 |
+
**Default Configuration:**
|
| 485 |
+
- **Space ID**: `{username}/trackio` (use "trackio" as default space name)
|
| 486 |
+
- **Run naming**: Unless otherwise specified, name the run in a way the user will recognize (e.g., descriptive of the task, model, or purpose)
|
| 487 |
+
- **Config**: Keep minimal - only include hyperparameters and model/dataset info
|
| 488 |
+
- **Project Name**: Use a Project Name to associate runs with a particular Project
|
| 489 |
+
|
| 490 |
+
**User overrides:** If user requests specific trackio configuration (custom space, run naming, grouping, or additional config), apply their preferences instead of defaults.
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
This is useful for managing multiple jobs with the same configuration or keeping training scripts portable.
|
| 494 |
+
|
| 495 |
+
See `references/trackio_guide.md` for complete documentation including grouping runs for experiments.
|
| 496 |
+
|
| 497 |
+
### Check Job Status
|
| 498 |
+
|
| 499 |
+
```python
|
| 500 |
+
# List all jobs
|
| 501 |
+
hf_jobs("ps")
|
| 502 |
+
|
| 503 |
+
# Inspect specific job
|
| 504 |
+
hf_jobs("inspect", {"job_id": "your-job-id"})
|
| 505 |
+
|
| 506 |
+
# View logs
|
| 507 |
+
hf_jobs("logs", {"job_id": "your-job-id"})
|
| 508 |
+
```
|
| 509 |
+
|
| 510 |
+
**Remember:** Wait for user to request status checks. Avoid polling repeatedly.
|
| 511 |
+
|
| 512 |
+
## Dataset Validation
|
| 513 |
+
|
| 514 |
+
**Validate dataset format BEFORE launching GPU training to prevent the #1 cause of training failures: format mismatches.**
|
| 515 |
+
|
| 516 |
+
### Why Validate
|
| 517 |
+
|
| 518 |
+
- 50%+ of training failures are due to dataset format issues
|
| 519 |
+
- DPO especially strict: requires exact column names (`prompt`, `chosen`, `rejected`)
|
| 520 |
+
- Failed GPU jobs waste $1-10 and 30-60 minutes
|
| 521 |
+
- Validation on CPU costs ~$0.01 and takes <1 minute
|
| 522 |
+
|
| 523 |
+
### When to Validate
|
| 524 |
+
|
| 525 |
+
**ALWAYS validate for:**
|
| 526 |
+
- Unknown or custom datasets
|
| 527 |
+
- DPO training (CRITICAL - 90% of datasets need mapping)
|
| 528 |
+
- Any dataset not explicitly TRL-compatible
|
| 529 |
+
|
| 530 |
+
**Skip validation for known TRL datasets:**
|
| 531 |
+
- `trl-lib/ultrachat_200k`, `trl-lib/Capybara`, `HuggingFaceH4/ultrachat_200k`, etc.
|
| 532 |
+
|
| 533 |
+
### Usage
|
| 534 |
+
|
| 535 |
+
```python
|
| 536 |
+
hf_jobs("uv", {
|
| 537 |
+
"script": "https://huggingface.co/datasets/mcp-tools/skills/raw/main/dataset_inspector.py",
|
| 538 |
+
"script_args": ["--dataset", "username/dataset-name", "--split", "train"]
|
| 539 |
+
})
|
| 540 |
+
```
|
| 541 |
+
|
| 542 |
+
The script is fast, and will usually complete synchronously.
|
| 543 |
+
|
| 544 |
+
### Reading Results
|
| 545 |
+
|
| 546 |
+
The output shows compatibility for each training method:
|
| 547 |
+
|
| 548 |
+
- **`β READY`** - Dataset is compatible, use directly
|
| 549 |
+
- **`β NEEDS MAPPING`** - Compatible but needs preprocessing (mapping code provided)
|
| 550 |
+
- **`β INCOMPATIBLE`** - Cannot be used for this method
|
| 551 |
+
|
| 552 |
+
When mapping is needed, the output includes a **"MAPPING CODE"** section with copy-paste ready Python code.
|
| 553 |
+
|
| 554 |
+
### Example Workflow
|
| 555 |
+
|
| 556 |
+
```python
|
| 557 |
+
# 1. Inspect dataset (costs ~$0.01, <1 min on CPU)
|
| 558 |
+
hf_jobs("uv", {
|
| 559 |
+
"script": "https://huggingface.co/datasets/mcp-tools/skills/raw/main/dataset_inspector.py",
|
| 560 |
+
"script_args": ["--dataset", "argilla/distilabel-math-preference-dpo", "--split", "train"]
|
| 561 |
+
})
|
| 562 |
+
|
| 563 |
+
# 2. Check output markers:
|
| 564 |
+
# β READY β proceed with training
|
| 565 |
+
# β NEEDS MAPPING β apply mapping code below
|
| 566 |
+
# β INCOMPATIBLE β choose different method/dataset
|
| 567 |
+
|
| 568 |
+
# 3. If mapping needed, apply before training:
|
| 569 |
+
def format_for_dpo(example):
|
| 570 |
+
return {
|
| 571 |
+
'prompt': example['instruction'],
|
| 572 |
+
'chosen': example['chosen_response'],
|
| 573 |
+
'rejected': example['rejected_response'],
|
| 574 |
+
}
|
| 575 |
+
dataset = dataset.map(format_for_dpo, remove_columns=dataset.column_names)
|
| 576 |
+
|
| 577 |
+
# 4. Launch training job with confidence
|
| 578 |
+
```
|
| 579 |
+
|
| 580 |
+
### Common Scenario: DPO Format Mismatch
|
| 581 |
+
|
| 582 |
+
Most DPO datasets use non-standard column names. Example:
|
| 583 |
+
|
| 584 |
+
```
|
| 585 |
+
Dataset has: instruction, chosen_response, rejected_response
|
| 586 |
+
DPO expects: prompt, chosen, rejected
|
| 587 |
+
```
|
| 588 |
+
|
| 589 |
+
The validator detects this and provides exact mapping code to fix it.
|
| 590 |
+
|
| 591 |
+
## Converting Models to GGUF
|
| 592 |
+
|
| 593 |
+
After training, convert models to **GGUF format** for use with llama.cpp, Ollama, LM Studio, and other local inference tools.
|
| 594 |
+
|
| 595 |
+
**What is GGUF:**
|
| 596 |
+
- Optimized for CPU/GPU inference with llama.cpp
|
| 597 |
+
- Supports quantization (4-bit, 5-bit, 8-bit) to reduce model size
|
| 598 |
+
- Compatible with Ollama, LM Studio, Jan, GPT4All, llama.cpp
|
| 599 |
+
- Typically 2-8GB for 7B models (vs 14GB unquantized)
|
| 600 |
+
|
| 601 |
+
**When to convert:**
|
| 602 |
+
- Running models locally with Ollama or LM Studio
|
| 603 |
+
- Reducing model size with quantization
|
| 604 |
+
- Deploying to edge devices
|
| 605 |
+
- Sharing models for local-first use
|
| 606 |
+
|
| 607 |
+
**See:** `references/gguf_conversion.md` for complete conversion guide, including production-ready conversion script, quantization options, hardware requirements, usage examples, and troubleshooting.
|
| 608 |
+
|
| 609 |
+
**Quick conversion:**
|
| 610 |
+
```python
|
| 611 |
+
hf_jobs("uv", {
|
| 612 |
+
"script": "<see references/gguf_conversion.md for complete script>",
|
| 613 |
+
"flavor": "a10g-large",
|
| 614 |
+
"timeout": "45m",
|
| 615 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"},
|
| 616 |
+
"env": {
|
| 617 |
+
"ADAPTER_MODEL": "username/my-finetuned-model",
|
| 618 |
+
"BASE_MODEL": "Qwen/Qwen2.5-0.5B",
|
| 619 |
+
"OUTPUT_REPO": "username/my-model-gguf"
|
| 620 |
+
}
|
| 621 |
+
})
|
| 622 |
+
```
|
| 623 |
+
|
| 624 |
+
## Common Training Patterns
|
| 625 |
+
|
| 626 |
+
See `references/training_patterns.md` for detailed examples including:
|
| 627 |
+
- Quick demo (5-10 minutes)
|
| 628 |
+
- Production with checkpoints
|
| 629 |
+
- Multi-GPU training
|
| 630 |
+
- DPO training (preference learning)
|
| 631 |
+
- GRPO training (online RL)
|
| 632 |
+
|
| 633 |
+
## Common Failure Modes
|
| 634 |
+
|
| 635 |
+
### Out of Memory (OOM)
|
| 636 |
+
|
| 637 |
+
**Fix (try in order):**
|
| 638 |
+
1. Reduce batch size: `per_device_train_batch_size=1`, increase `gradient_accumulation_steps=8`. Effective batch size is `per_device_train_batch_size` x `gradient_accumulation_steps`. For best performance keep effective batch size close to 128.
|
| 639 |
+
2. Enable: `gradient_checkpointing=True`
|
| 640 |
+
3. Upgrade hardware: t4-small β l4x1, a10g-small β a10g-large etc.
|
| 641 |
+
|
| 642 |
+
### Dataset Misformatted
|
| 643 |
+
|
| 644 |
+
**Fix:**
|
| 645 |
+
1. Validate first with dataset inspector:
|
| 646 |
+
```bash
|
| 647 |
+
uv run https://huggingface.co/datasets/mcp-tools/skills/raw/main/dataset_inspector.py \
|
| 648 |
+
--dataset name --split train
|
| 649 |
+
```
|
| 650 |
+
2. Check output for compatibility markers (β READY, β NEEDS MAPPING, β INCOMPATIBLE)
|
| 651 |
+
3. Apply mapping code from inspector output if needed
|
| 652 |
+
|
| 653 |
+
### Job Timeout
|
| 654 |
+
|
| 655 |
+
**Fix:**
|
| 656 |
+
1. Check logs for actual runtime: `hf_jobs("logs", {"job_id": "..."})`
|
| 657 |
+
2. Increase timeout with buffer: `"timeout": "3h"` (add 30% to estimated time)
|
| 658 |
+
3. Or reduce training: lower `num_train_epochs`, use smaller dataset, enable `max_steps`
|
| 659 |
+
4. Save checkpoints: `save_strategy="steps"`, `save_steps=500`, `hub_strategy="every_save"`
|
| 660 |
+
|
| 661 |
+
**Note:** Default 30min is insufficient for real training. Minimum 1-2 hours.
|
| 662 |
+
|
| 663 |
+
### Hub Push Failures
|
| 664 |
+
|
| 665 |
+
**Fix:**
|
| 666 |
+
1. Add to job: `secrets={"HF_TOKEN": "$HF_TOKEN"}`
|
| 667 |
+
2. Add to config: `push_to_hub=True`, `hub_model_id="username/model-name"`
|
| 668 |
+
3. Verify auth: `mcp__huggingface__hf_whoami()`
|
| 669 |
+
4. Check token has write permissions and repo exists (or set `hub_private_repo=True`)
|
| 670 |
+
|
| 671 |
+
### Missing Dependencies
|
| 672 |
+
|
| 673 |
+
**Fix:**
|
| 674 |
+
Add to PEP 723 header:
|
| 675 |
+
```python
|
| 676 |
+
# /// script
|
| 677 |
+
# dependencies = ["trl>=0.12.0", "peft>=0.7.0", "trackio", "missing-package"]
|
| 678 |
+
# ///
|
| 679 |
+
```
|
| 680 |
+
|
| 681 |
+
## Troubleshooting
|
| 682 |
+
|
| 683 |
+
**Common issues:**
|
| 684 |
+
- Job times out β Increase timeout, reduce epochs/dataset, use smaller model/LoRA
|
| 685 |
+
- Model not saved to Hub β Check push_to_hub=True, hub_model_id, secrets=HF_TOKEN
|
| 686 |
+
- Out of Memory (OOM) β Reduce batch size, increase gradient accumulation, enable LoRA, use larger GPU
|
| 687 |
+
- Dataset format error β Validate with dataset inspector (see Dataset Validation section)
|
| 688 |
+
- Import/module errors β Add PEP 723 header with dependencies, verify format
|
| 689 |
+
- Authentication errors β Check `mcp__huggingface__hf_whoami()`, token permissions, secrets parameter
|
| 690 |
+
|
| 691 |
+
**See:** `references/troubleshooting.md` for complete troubleshooting guide
|
| 692 |
+
|
| 693 |
+
## Resources
|
| 694 |
+
|
| 695 |
+
### References (In This Skill)
|
| 696 |
+
- `references/training_methods.md` - Overview of SFT, DPO, GRPO, KTO, PPO, Reward Modeling
|
| 697 |
+
- `references/training_patterns.md` - Common training patterns and examples
|
| 698 |
+
- `references/unsloth.md` - Unsloth for fast VLM training (~2x speed, 60% less VRAM)
|
| 699 |
+
- `references/gguf_conversion.md` - Complete GGUF conversion guide
|
| 700 |
+
- `references/trackio_guide.md` - Trackio monitoring setup
|
| 701 |
+
- `references/hardware_guide.md` - Hardware specs and selection
|
| 702 |
+
- `references/hub_saving.md` - Hub authentication troubleshooting
|
| 703 |
+
- `references/troubleshooting.md` - Common issues and solutions
|
| 704 |
+
- `references/local_training_macos.md` - Local training on macOS
|
| 705 |
+
|
| 706 |
+
### Scripts (In This Skill)
|
| 707 |
+
- `scripts/train_sft_example.py` - Production SFT template
|
| 708 |
+
- `scripts/train_dpo_example.py` - Production DPO template
|
| 709 |
+
- `scripts/train_grpo_example.py` - Production GRPO template
|
| 710 |
+
- `scripts/unsloth_sft_example.py` - Unsloth text LLM training template (faster, less VRAM)
|
| 711 |
+
- `scripts/estimate_cost.py` - Estimate time and cost (offer when appropriate)
|
| 712 |
+
- `scripts/convert_to_gguf.py` - Complete GGUF conversion script
|
| 713 |
+
- `scripts/hf_benchmarks.py` - Search for benchmark results and leaderboards by task, alias or free text.
|
| 714 |
+
|
| 715 |
+
### External Scripts
|
| 716 |
+
- [Dataset Inspector](https://huggingface.co/datasets/mcp-tools/skills/raw/main/dataset_inspector.py) - Validate dataset format before training (use via `uv run` or `hf_jobs`)
|
| 717 |
+
|
| 718 |
+
### External Links
|
| 719 |
+
- [TRL Documentation](https://huggingface.co/docs/trl)
|
| 720 |
+
- [TRL Jobs Training Guide](https://huggingface.co/docs/trl/en/jobs_training)
|
| 721 |
+
- [TRL Jobs Package](https://github.com/huggingface/trl-jobs)
|
| 722 |
+
- [HF Jobs Documentation](https://huggingface.co/docs/huggingface_hub/guides/jobs)
|
| 723 |
+
- [TRL Example Scripts](https://github.com/huggingface/trl/tree/main/examples/scripts)
|
| 724 |
+
- [UV Scripts Guide](https://docs.astral.sh/uv/guides/scripts/)
|
| 725 |
+
- [UV Scripts Organization](https://huggingface.co/uv-scripts)
|
| 726 |
+
|
| 727 |
+
## Key Takeaways
|
| 728 |
+
|
| 729 |
+
1. **Submit scripts inline** - The `script` parameter accepts Python code directly; no file saving required unless user requests
|
| 730 |
+
2. **Jobs are asynchronous** - Don't wait/poll; let user check when ready
|
| 731 |
+
3. **Always set timeout** - Default 30 min is insufficient; minimum 1-2 hours recommended
|
| 732 |
+
4. **Always enable Hub push** - Environment is ephemeral; without push, all results lost
|
| 733 |
+
5. **Include Trackio** - Use example scripts as templates for real-time monitoring
|
| 734 |
+
6. **Offer cost estimation** - When parameters are known, use `scripts/estimate_cost.py`
|
| 735 |
+
7. **Use UV scripts (Approach 1)** - Default to `hf_jobs("uv", {...})` with inline scripts; TRL maintained scripts for standard training; avoid bash `trl-jobs` commands in Claude Code
|
| 736 |
+
8. **Use hf_doc_fetch/hf_doc_search** for latest TRL documentation
|
| 737 |
+
9. **Validate dataset format** before training with dataset inspector (see Dataset Validation section)
|
| 738 |
+
10. **Choose appropriate hardware** for model size; use LoRA for models >7B
|
.agents/skills/huggingface-llm-trainer/references/gguf_conversion.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GGUF Conversion Guide
|
| 2 |
+
|
| 3 |
+
After training models with TRL on Hugging Face Jobs, convert them to **GGUF format** for use with llama.cpp, Ollama, LM Studio, and other local inference tools.
|
| 4 |
+
|
| 5 |
+
**This guide provides production-ready, tested code based on successful conversions.** All critical dependencies and build steps are included.
|
| 6 |
+
|
| 7 |
+
## What is GGUF?
|
| 8 |
+
|
| 9 |
+
**GGUF** (GPT-Generated Unified Format):
|
| 10 |
+
- Optimized format for CPU/GPU inference with llama.cpp
|
| 11 |
+
- Supports quantization (4-bit, 5-bit, 8-bit) to reduce model size
|
| 12 |
+
- Compatible with: Ollama, LM Studio, Jan, GPT4All, llama.cpp
|
| 13 |
+
- Typically 2-8GB for 7B models (vs 14GB unquantized)
|
| 14 |
+
|
| 15 |
+
## When to Convert to GGUF
|
| 16 |
+
|
| 17 |
+
**Convert when:**
|
| 18 |
+
- Running models locally with Ollama or LM Studio
|
| 19 |
+
- Using CPU-optimized inference
|
| 20 |
+
- Reducing model size with quantization
|
| 21 |
+
- Deploying to edge devices
|
| 22 |
+
- Sharing models for local-first use
|
| 23 |
+
|
| 24 |
+
## Critical Success Factors
|
| 25 |
+
|
| 26 |
+
Based on production testing, these are **essential** for reliable conversion:
|
| 27 |
+
|
| 28 |
+
### 1. β
Install Build Tools FIRST
|
| 29 |
+
**Before cloning llama.cpp**, install build dependencies:
|
| 30 |
+
```python
|
| 31 |
+
subprocess.run(["apt-get", "update", "-qq"], check=True, capture_output=True)
|
| 32 |
+
subprocess.run(["apt-get", "install", "-y", "-qq", "build-essential", "cmake"], check=True, capture_output=True)
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
**Why:** The quantization tool requires gcc and cmake. Installing after cloning doesn't help.
|
| 36 |
+
|
| 37 |
+
### 2. β
Use CMake (Not Make)
|
| 38 |
+
**Build the quantize tool with CMake:**
|
| 39 |
+
```python
|
| 40 |
+
# Create build directory
|
| 41 |
+
os.makedirs("/tmp/llama.cpp/build", exist_ok=True)
|
| 42 |
+
|
| 43 |
+
# Configure
|
| 44 |
+
subprocess.run([
|
| 45 |
+
"cmake", "-B", "/tmp/llama.cpp/build", "-S", "/tmp/llama.cpp",
|
| 46 |
+
"-DGGML_CUDA=OFF" # Faster build, CUDA not needed for quantization
|
| 47 |
+
], check=True, capture_output=True, text=True)
|
| 48 |
+
|
| 49 |
+
# Build
|
| 50 |
+
subprocess.run([
|
| 51 |
+
"cmake", "--build", "/tmp/llama.cpp/build",
|
| 52 |
+
"--target", "llama-quantize", "-j", "4"
|
| 53 |
+
], check=True, capture_output=True, text=True)
|
| 54 |
+
|
| 55 |
+
# Binary path
|
| 56 |
+
quantize_bin = "/tmp/llama.cpp/build/bin/llama-quantize"
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
**Why:** CMake is more reliable than `make` and produces consistent binary paths.
|
| 60 |
+
|
| 61 |
+
### 3. β
Include All Dependencies
|
| 62 |
+
**PEP 723 header must include:**
|
| 63 |
+
```python
|
| 64 |
+
# /// script
|
| 65 |
+
# dependencies = [
|
| 66 |
+
# "transformers>=4.36.0",
|
| 67 |
+
# "peft>=0.7.0",
|
| 68 |
+
# "torch>=2.0.0",
|
| 69 |
+
# "accelerate>=0.24.0",
|
| 70 |
+
# "huggingface_hub>=0.20.0",
|
| 71 |
+
# "sentencepiece>=0.1.99", # Required for tokenizer
|
| 72 |
+
# "protobuf>=3.20.0", # Required for tokenizer
|
| 73 |
+
# "numpy",
|
| 74 |
+
# "gguf",
|
| 75 |
+
# ]
|
| 76 |
+
# ///
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
**Why:** `sentencepiece` and `protobuf` are critical for tokenizer conversion. Missing them causes silent failures.
|
| 80 |
+
|
| 81 |
+
### 4. β
Verify Names Before Use
|
| 82 |
+
**Always verify repos exist:**
|
| 83 |
+
```python
|
| 84 |
+
# Before submitting job, verify:
|
| 85 |
+
hub_repo_details([ADAPTER_MODEL], repo_type="model")
|
| 86 |
+
hub_repo_details([BASE_MODEL], repo_type="model")
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
**Why:** Non-existent dataset/model names cause job failures that could be caught in seconds.
|
| 90 |
+
|
| 91 |
+
## Complete Conversion Script
|
| 92 |
+
|
| 93 |
+
See `scripts/convert_to_gguf.py` for the complete, production-ready script.
|
| 94 |
+
|
| 95 |
+
**Key features:**
|
| 96 |
+
- β
All dependencies in PEP 723 header
|
| 97 |
+
- β
Build tools installed automatically
|
| 98 |
+
- β
CMake build process (reliable)
|
| 99 |
+
- β
Comprehensive error handling
|
| 100 |
+
- β
Environment variable configuration
|
| 101 |
+
- β
Automatic README generation
|
| 102 |
+
|
| 103 |
+
## Quick Conversion Job
|
| 104 |
+
|
| 105 |
+
```python
|
| 106 |
+
# Before submitting: VERIFY MODELS EXIST
|
| 107 |
+
hub_repo_details(["username/my-finetuned-model"], repo_type="model")
|
| 108 |
+
hub_repo_details(["Qwen/Qwen2.5-0.5B"], repo_type="model")
|
| 109 |
+
|
| 110 |
+
# Submit conversion job
|
| 111 |
+
hf_jobs("uv", {
|
| 112 |
+
"script": open("trl/scripts/convert_to_gguf.py").read(), # Or inline the script
|
| 113 |
+
"flavor": "a10g-large",
|
| 114 |
+
"timeout": "45m",
|
| 115 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"},
|
| 116 |
+
"env": {
|
| 117 |
+
"ADAPTER_MODEL": "username/my-finetuned-model",
|
| 118 |
+
"BASE_MODEL": "Qwen/Qwen2.5-0.5B",
|
| 119 |
+
"OUTPUT_REPO": "username/my-model-gguf",
|
| 120 |
+
"HF_USERNAME": "username" # Optional, for README
|
| 121 |
+
}
|
| 122 |
+
})
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
## Conversion Process
|
| 126 |
+
|
| 127 |
+
The script performs these steps:
|
| 128 |
+
|
| 129 |
+
1. **Load and Merge** - Load base model and LoRA adapter, merge them
|
| 130 |
+
2. **Install Build Tools** - Install gcc, cmake (CRITICAL: before cloning llama.cpp)
|
| 131 |
+
3. **Setup llama.cpp** - Clone repo, install Python dependencies
|
| 132 |
+
4. **Convert to GGUF** - Create FP16 GGUF using llama.cpp converter
|
| 133 |
+
5. **Build Quantize Tool** - Use CMake to build `llama-quantize`
|
| 134 |
+
6. **Quantize** - Create Q4_K_M, Q5_K_M, Q8_0 versions
|
| 135 |
+
7. **Upload** - Upload all versions + README to Hub
|
| 136 |
+
|
| 137 |
+
## Quantization Options
|
| 138 |
+
|
| 139 |
+
Common quantization formats (from smallest to largest):
|
| 140 |
+
|
| 141 |
+
| Format | Size | Quality | Use Case |
|
| 142 |
+
|--------|------|---------|----------|
|
| 143 |
+
| **Q4_K_M** | ~300MB | Good | **Recommended** - best balance of size/quality |
|
| 144 |
+
| **Q5_K_M** | ~350MB | Better | Higher quality, slightly larger |
|
| 145 |
+
| **Q8_0** | ~500MB | Very High | Near-original quality |
|
| 146 |
+
| **F16** | ~1GB | Original | Full precision, largest file |
|
| 147 |
+
|
| 148 |
+
**Recommendation:** Create Q4_K_M, Q5_K_M, and Q8_0 versions to give users options.
|
| 149 |
+
|
| 150 |
+
## Hardware Requirements
|
| 151 |
+
|
| 152 |
+
**For conversion:**
|
| 153 |
+
- Small models (<1B): CPU-basic works, but slow
|
| 154 |
+
- Medium models (1-7B): a10g-large recommended
|
| 155 |
+
- Large models (7B+): a10g-large or a100-large
|
| 156 |
+
|
| 157 |
+
**Time estimates:**
|
| 158 |
+
- 0.5B model: ~15-25 minutes on A10G
|
| 159 |
+
- 3B model: ~30-45 minutes on A10G
|
| 160 |
+
- 7B model: ~45-60 minutes on A10G
|
| 161 |
+
|
| 162 |
+
## Using GGUF Models
|
| 163 |
+
|
| 164 |
+
**GGUF models work on both CPU and GPU.** They're optimized for CPU inference but can also leverage GPU acceleration when available.
|
| 165 |
+
|
| 166 |
+
### With Ollama (auto-detects GPU)
|
| 167 |
+
```bash
|
| 168 |
+
# Download GGUF
|
| 169 |
+
hf download username/my-model-gguf model-q4_k_m.gguf
|
| 170 |
+
|
| 171 |
+
# Create Modelfile
|
| 172 |
+
echo "FROM ./model-q4_k_m.gguf" > Modelfile
|
| 173 |
+
|
| 174 |
+
# Create and run (uses GPU automatically if available)
|
| 175 |
+
ollama create my-model -f Modelfile
|
| 176 |
+
ollama run my-model
|
| 177 |
+
```
|
| 178 |
+
|
| 179 |
+
### With llama.cpp
|
| 180 |
+
```bash
|
| 181 |
+
# CPU only
|
| 182 |
+
./llama-cli -m model-q4_k_m.gguf -p "Your prompt"
|
| 183 |
+
|
| 184 |
+
# With GPU acceleration (offload 32 layers to GPU)
|
| 185 |
+
./llama-cli -m model-q4_k_m.gguf -ngl 32 -p "Your prompt"
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
### With LM Studio
|
| 189 |
+
1. Download the `.gguf` file
|
| 190 |
+
2. Import into LM Studio
|
| 191 |
+
3. Start chatting
|
| 192 |
+
|
| 193 |
+
## Best Practices
|
| 194 |
+
|
| 195 |
+
### β
DO:
|
| 196 |
+
1. **Verify repos exist** before submitting jobs (use `hub_repo_details`)
|
| 197 |
+
2. **Install build tools FIRST** before cloning llama.cpp
|
| 198 |
+
3. **Use CMake** for building quantize tool (not make)
|
| 199 |
+
4. **Include all dependencies** in PEP 723 header (especially sentencepiece, protobuf)
|
| 200 |
+
5. **Create multiple quantizations** - Give users choice
|
| 201 |
+
6. **Test on known models** before production use
|
| 202 |
+
7. **Use A10G GPU** for faster conversion
|
| 203 |
+
|
| 204 |
+
### β DON'T:
|
| 205 |
+
1. **Assume repos exist** - Always verify with hub tools
|
| 206 |
+
2. **Use make** instead of CMake - Less reliable
|
| 207 |
+
3. **Remove dependencies** to "simplify" - They're all needed
|
| 208 |
+
4. **Skip build tools** - Quantization will fail silently
|
| 209 |
+
5. **Use default paths** - CMake puts binaries in build/bin/
|
| 210 |
+
|
| 211 |
+
## Common Issues
|
| 212 |
+
|
| 213 |
+
### Out of memory during merge
|
| 214 |
+
**Fix:**
|
| 215 |
+
- Use larger GPU (a10g-large or a100-large)
|
| 216 |
+
- Ensure `device_map="auto"` for automatic placement
|
| 217 |
+
- Use `dtype=torch.float16` or `torch.bfloat16`
|
| 218 |
+
|
| 219 |
+
### Conversion fails with architecture error
|
| 220 |
+
**Fix:**
|
| 221 |
+
- Ensure llama.cpp supports the model architecture
|
| 222 |
+
- Check for standard architecture (Qwen, Llama, Mistral, etc.)
|
| 223 |
+
- Update llama.cpp to latest: `git clone --depth 1 https://github.com/ggerganov/llama.cpp.git`
|
| 224 |
+
- Check llama.cpp documentation for model support
|
| 225 |
+
|
| 226 |
+
### Quantization fails
|
| 227 |
+
**Fix:**
|
| 228 |
+
- Verify build tools installed: `apt-get install build-essential cmake`
|
| 229 |
+
- Use CMake (not make) to build quantize tool
|
| 230 |
+
- Check binary path: `/tmp/llama.cpp/build/bin/llama-quantize`
|
| 231 |
+
- Verify FP16 GGUF exists before quantizing
|
| 232 |
+
|
| 233 |
+
### Missing sentencepiece error
|
| 234 |
+
**Fix:**
|
| 235 |
+
- Add to PEP 723 header: `"sentencepiece>=0.1.99", "protobuf>=3.20.0"`
|
| 236 |
+
- Don't remove dependencies to "simplify" - all are required
|
| 237 |
+
|
| 238 |
+
### Upload fails or times out
|
| 239 |
+
**Fix:**
|
| 240 |
+
- Large models (>2GB) need longer timeout: `"timeout": "1h"`
|
| 241 |
+
- Upload quantized versions separately if needed
|
| 242 |
+
- Check network/Hub status
|
| 243 |
+
|
| 244 |
+
## Lessons Learned
|
| 245 |
+
|
| 246 |
+
These are from production testing and real failures:
|
| 247 |
+
|
| 248 |
+
### 1. Always Verify Before Use
|
| 249 |
+
**Lesson:** Don't assume repos/datasets exist. Check first.
|
| 250 |
+
```python
|
| 251 |
+
# BEFORE submitting job
|
| 252 |
+
hub_repo_details(["trl-lib/argilla-dpo-mix-7k"], repo_type="dataset") # Would catch error
|
| 253 |
+
```
|
| 254 |
+
**Prevented failures:** Non-existent dataset names, typos in model names
|
| 255 |
+
|
| 256 |
+
### 2. Prioritize Reliability Over Performance
|
| 257 |
+
**Lesson:** Default to what's most likely to succeed.
|
| 258 |
+
- Use CMake (not make) - more reliable
|
| 259 |
+
- Disable CUDA in build - faster, not needed
|
| 260 |
+
- Include all dependencies - don't "simplify"
|
| 261 |
+
|
| 262 |
+
**Prevented failures:** Build failures, missing binaries
|
| 263 |
+
|
| 264 |
+
### 3. Create Atomic, Self-Contained Scripts
|
| 265 |
+
**Lesson:** Don't remove dependencies or steps. Scripts should work as a unit.
|
| 266 |
+
- All dependencies in PEP 723 header
|
| 267 |
+
- All build steps included
|
| 268 |
+
- Clear error messages
|
| 269 |
+
|
| 270 |
+
**Prevented failures:** Missing tokenizer libraries, build tool failures
|
| 271 |
+
|
| 272 |
+
## References
|
| 273 |
+
|
| 274 |
+
**In this skill:**
|
| 275 |
+
- `scripts/convert_to_gguf.py` - Complete, production-ready script
|
| 276 |
+
|
| 277 |
+
**External:**
|
| 278 |
+
- [llama.cpp Repository](https://github.com/ggerganov/llama.cpp)
|
| 279 |
+
- [GGUF Specification](https://github.com/ggerganov/ggml/blob/master/docs/gguf.md)
|
| 280 |
+
- [Ollama Documentation](https://ollama.ai)
|
| 281 |
+
- [LM Studio](https://lmstudio.ai)
|
| 282 |
+
|
| 283 |
+
## Summary
|
| 284 |
+
|
| 285 |
+
**Critical checklist for GGUF conversion:**
|
| 286 |
+
- [ ] Verify adapter and base models exist on Hub
|
| 287 |
+
- [ ] Use production script from `scripts/convert_to_gguf.py`
|
| 288 |
+
- [ ] All dependencies in PEP 723 header (including sentencepiece, protobuf)
|
| 289 |
+
- [ ] Build tools installed before cloning llama.cpp
|
| 290 |
+
- [ ] CMake used for building quantize tool (not make)
|
| 291 |
+
- [ ] Correct binary path: `/tmp/llama.cpp/build/bin/llama-quantize`
|
| 292 |
+
- [ ] A10G GPU selected for reasonable conversion time
|
| 293 |
+
- [ ] Timeout set to 45m minimum
|
| 294 |
+
- [ ] HF_TOKEN in secrets for Hub upload
|
| 295 |
+
|
| 296 |
+
**The script in `scripts/convert_to_gguf.py` incorporates all these lessons and has been tested successfully in production.**
|
.agents/skills/huggingface-llm-trainer/references/hardware_guide.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hardware Selection Guide
|
| 2 |
+
|
| 3 |
+
Choosing the right hardware (flavor) is critical for cost-effective training.
|
| 4 |
+
|
| 5 |
+
## Available Hardware
|
| 6 |
+
|
| 7 |
+
### CPU
|
| 8 |
+
- `cpu-basic` - Basic CPU, testing only
|
| 9 |
+
- `cpu-upgrade` - Enhanced CPU
|
| 10 |
+
|
| 11 |
+
**Use cases:** Dataset validation, preprocessing, testing scripts
|
| 12 |
+
**Not recommended for training:** Too slow for any meaningful training
|
| 13 |
+
|
| 14 |
+
### GPU Options
|
| 15 |
+
|
| 16 |
+
| Flavor | GPU | Memory | Use Case | Cost/hour |
|
| 17 |
+
|--------|-----|--------|----------|-----------|
|
| 18 |
+
| `t4-small` | NVIDIA T4 | 16GB | <1B models, demos | ~$0.50-1 |
|
| 19 |
+
| `t4-medium` | NVIDIA T4 | 16GB | 1-3B models, development | ~$1-2 |
|
| 20 |
+
| `l4x1` | NVIDIA L4 | 24GB | 3-7B models, efficient training | ~$2-3 |
|
| 21 |
+
| `l4x4` | 4x NVIDIA L4 | 96GB | Multi-GPU training | ~$8-12 |
|
| 22 |
+
| `a10g-small` | NVIDIA A10G | 24GB | 3-7B models, production | ~$3-4 |
|
| 23 |
+
| `a10g-large` | NVIDIA A10G | 24GB | 7-13B models | ~$4-6 |
|
| 24 |
+
| `a10g-largex2` | 2x NVIDIA A10G | 48GB | Multi-GPU, large models | ~$8-12 |
|
| 25 |
+
| `a10g-largex4` | 4x NVIDIA A10G | 96GB | Multi-GPU, very large models | ~$16-24 |
|
| 26 |
+
| `a100-large` | NVIDIA A100 | 40GB | 13B+ models, fast training | ~$8-12 |
|
| 27 |
+
|
| 28 |
+
### TPU Options
|
| 29 |
+
|
| 30 |
+
| Flavor | Type | Use Case |
|
| 31 |
+
|--------|------|----------|
|
| 32 |
+
| `v5e-1x1` | TPU v5e | Small TPU workloads |
|
| 33 |
+
| `v5e-2x2` | 4x TPU v5e | Medium TPU workloads |
|
| 34 |
+
| `v5e-2x4` | 8x TPU v5e | Large TPU workloads |
|
| 35 |
+
|
| 36 |
+
**Note:** TPUs require TPU-optimized code. Most TRL training uses GPUs.
|
| 37 |
+
|
| 38 |
+
## Selection Guidelines
|
| 39 |
+
|
| 40 |
+
### By Model Size
|
| 41 |
+
|
| 42 |
+
**Tiny Models (<1B parameters)**
|
| 43 |
+
- **Recommended:** `t4-small`
|
| 44 |
+
- **Example:** Qwen2.5-0.5B, TinyLlama
|
| 45 |
+
- **Batch size:** 4-8
|
| 46 |
+
- **Training time:** 1-2 hours for 1K examples
|
| 47 |
+
|
| 48 |
+
**Small Models (1-3B parameters)**
|
| 49 |
+
- **Recommended:** `t4-medium` or `a10g-small`
|
| 50 |
+
- **Example:** Qwen2.5-1.5B, Phi-2
|
| 51 |
+
- **Batch size:** 2-4
|
| 52 |
+
- **Training time:** 2-4 hours for 10K examples
|
| 53 |
+
|
| 54 |
+
**Medium Models (3-7B parameters)**
|
| 55 |
+
- **Recommended:** `a10g-small` or `a10g-large`
|
| 56 |
+
- **Example:** Qwen2.5-7B, Mistral-7B
|
| 57 |
+
- **Batch size:** 1-2 (or LoRA with 4-8)
|
| 58 |
+
- **Training time:** 4-8 hours for 10K examples
|
| 59 |
+
|
| 60 |
+
**Large Models (7-13B parameters)**
|
| 61 |
+
- **Recommended:** `a10g-large` or `a100-large`
|
| 62 |
+
- **Example:** Llama-3-8B, Mixtral-8x7B (with LoRA)
|
| 63 |
+
- **Batch size:** 1 (full fine-tuning) or 2-4 (LoRA)
|
| 64 |
+
- **Training time:** 6-12 hours for 10K examples
|
| 65 |
+
- **Note:** Always use LoRA/PEFT
|
| 66 |
+
|
| 67 |
+
**Very Large Models (13B+ parameters)**
|
| 68 |
+
- **Recommended:** `a100-large` with LoRA
|
| 69 |
+
- **Example:** Llama-3-13B, Llama-3-70B (LoRA only)
|
| 70 |
+
- **Batch size:** 1-2 with LoRA
|
| 71 |
+
- **Training time:** 8-24 hours for 10K examples
|
| 72 |
+
- **Note:** Full fine-tuning not feasible, use LoRA/PEFT
|
| 73 |
+
|
| 74 |
+
### By Budget
|
| 75 |
+
|
| 76 |
+
**Minimal Budget (<$5 total)**
|
| 77 |
+
- Use `t4-small`
|
| 78 |
+
- Train on subset of data (100-500 examples)
|
| 79 |
+
- Limit to 1-2 epochs
|
| 80 |
+
- Use small model (<1B)
|
| 81 |
+
|
| 82 |
+
**Small Budget ($5-20)**
|
| 83 |
+
- Use `t4-medium` or `a10g-small`
|
| 84 |
+
- Train on 1K-5K examples
|
| 85 |
+
- 2-3 epochs
|
| 86 |
+
- Model up to 3B parameters
|
| 87 |
+
|
| 88 |
+
**Medium Budget ($20-50)**
|
| 89 |
+
- Use `a10g-small` or `a10g-large`
|
| 90 |
+
- Train on 5K-20K examples
|
| 91 |
+
- 3-5 epochs
|
| 92 |
+
- Model up to 7B parameters
|
| 93 |
+
|
| 94 |
+
**Large Budget ($50-200)**
|
| 95 |
+
- Use `a10g-large` or `a100-large`
|
| 96 |
+
- Full dataset training
|
| 97 |
+
- Multiple epochs
|
| 98 |
+
- Model up to 13B parameters with LoRA
|
| 99 |
+
|
| 100 |
+
### By Training Type
|
| 101 |
+
|
| 102 |
+
**Quick Demo/Experiment**
|
| 103 |
+
- `t4-small`
|
| 104 |
+
- 50-100 examples
|
| 105 |
+
- 5-10 steps
|
| 106 |
+
- ~10-15 minutes
|
| 107 |
+
|
| 108 |
+
**Development/Iteration**
|
| 109 |
+
- `t4-medium` or `a10g-small`
|
| 110 |
+
- 1K examples
|
| 111 |
+
- 1 epoch
|
| 112 |
+
- ~30-60 minutes
|
| 113 |
+
|
| 114 |
+
**Production Training**
|
| 115 |
+
- `a10g-large` or `a100-large`
|
| 116 |
+
- Full dataset
|
| 117 |
+
- 3-5 epochs
|
| 118 |
+
- 4-12 hours
|
| 119 |
+
|
| 120 |
+
**Research/Experimentation**
|
| 121 |
+
- `a100-large`
|
| 122 |
+
- Multiple runs
|
| 123 |
+
- Various hyperparameters
|
| 124 |
+
- Budget for 20-50 hours
|
| 125 |
+
|
| 126 |
+
## Memory Considerations
|
| 127 |
+
|
| 128 |
+
### Estimating Memory Requirements
|
| 129 |
+
|
| 130 |
+
**Full fine-tuning:**
|
| 131 |
+
```
|
| 132 |
+
Memory (GB) β (Model params in billions) Γ 20
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
**LoRA fine-tuning:**
|
| 136 |
+
```
|
| 137 |
+
Memory (GB) β (Model params in billions) Γ 4
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
**Examples:**
|
| 141 |
+
- Qwen2.5-0.5B full: ~10GB β
fits t4-small
|
| 142 |
+
- Qwen2.5-1.5B full: ~30GB β exceeds most GPUs
|
| 143 |
+
- Qwen2.5-1.5B LoRA: ~6GB β
fits t4-small
|
| 144 |
+
- Qwen2.5-7B full: ~140GB β not feasible
|
| 145 |
+
- Qwen2.5-7B LoRA: ~28GB β
fits a10g-large
|
| 146 |
+
|
| 147 |
+
### Memory Optimization
|
| 148 |
+
|
| 149 |
+
If hitting memory limits:
|
| 150 |
+
|
| 151 |
+
1. **Use LoRA/PEFT**
|
| 152 |
+
```python
|
| 153 |
+
peft_config=LoraConfig(r=16, lora_alpha=32)
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
2. **Reduce batch size**
|
| 157 |
+
```python
|
| 158 |
+
per_device_train_batch_size=1
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
3. **Increase gradient accumulation**
|
| 162 |
+
```python
|
| 163 |
+
gradient_accumulation_steps=8 # Effective batch size = 1Γ8
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
4. **Enable gradient checkpointing**
|
| 167 |
+
```python
|
| 168 |
+
gradient_checkpointing=True
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
5. **Use mixed precision**
|
| 172 |
+
```python
|
| 173 |
+
bf16=True # or fp16=True
|
| 174 |
+
```
|
| 175 |
+
|
| 176 |
+
6. **Upgrade to larger GPU**
|
| 177 |
+
- t4 β a10g β a100
|
| 178 |
+
|
| 179 |
+
## Cost Estimation
|
| 180 |
+
|
| 181 |
+
### Formula
|
| 182 |
+
|
| 183 |
+
```
|
| 184 |
+
Total Cost = (Hours of training) Γ (Cost per hour)
|
| 185 |
+
```
|
| 186 |
+
|
| 187 |
+
### Example Calculations
|
| 188 |
+
|
| 189 |
+
**Quick demo:**
|
| 190 |
+
- Hardware: t4-small ($0.75/hour)
|
| 191 |
+
- Time: 15 minutes (0.25 hours)
|
| 192 |
+
- Cost: $0.19
|
| 193 |
+
|
| 194 |
+
**Development training:**
|
| 195 |
+
- Hardware: a10g-small ($3.50/hour)
|
| 196 |
+
- Time: 2 hours
|
| 197 |
+
- Cost: $7.00
|
| 198 |
+
|
| 199 |
+
**Production training:**
|
| 200 |
+
- Hardware: a10g-large ($5/hour)
|
| 201 |
+
- Time: 6 hours
|
| 202 |
+
- Cost: $30.00
|
| 203 |
+
|
| 204 |
+
**Large model with LoRA:**
|
| 205 |
+
- Hardware: a100-large ($10/hour)
|
| 206 |
+
- Time: 8 hours
|
| 207 |
+
- Cost: $80.00
|
| 208 |
+
|
| 209 |
+
### Cost Optimization Tips
|
| 210 |
+
|
| 211 |
+
1. **Start small:** Test on t4-small with subset
|
| 212 |
+
2. **Use LoRA:** 4-5x cheaper than full fine-tuning
|
| 213 |
+
3. **Optimize hyperparameters:** Fewer epochs if possible
|
| 214 |
+
4. **Set appropriate timeout:** Don't waste compute on stalled jobs
|
| 215 |
+
5. **Use checkpointing:** Resume if job fails
|
| 216 |
+
6. **Monitor costs:** Check running jobs regularly
|
| 217 |
+
|
| 218 |
+
## Multi-GPU Training
|
| 219 |
+
|
| 220 |
+
TRL automatically handles multi-GPU training with Accelerate when using multi-GPU flavors.
|
| 221 |
+
|
| 222 |
+
**Multi-GPU flavors:**
|
| 223 |
+
- `l4x4` - 4x L4 GPUs
|
| 224 |
+
- `a10g-largex2` - 2x A10G GPUs
|
| 225 |
+
- `a10g-largex4` - 4x A10G GPUs
|
| 226 |
+
|
| 227 |
+
**When to use:**
|
| 228 |
+
- Models >13B parameters
|
| 229 |
+
- Need faster training (linear speedup)
|
| 230 |
+
- Large datasets (>50K examples)
|
| 231 |
+
|
| 232 |
+
**Example:**
|
| 233 |
+
```python
|
| 234 |
+
hf_jobs("uv", {
|
| 235 |
+
"script": "train.py",
|
| 236 |
+
"flavor": "a10g-largex2", # 2 GPUs
|
| 237 |
+
"timeout": "4h",
|
| 238 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 239 |
+
})
|
| 240 |
+
```
|
| 241 |
+
|
| 242 |
+
No code changes neededβTRL/Accelerate handles distribution automatically.
|
| 243 |
+
|
| 244 |
+
## Choosing Between Options
|
| 245 |
+
|
| 246 |
+
### a10g vs a100
|
| 247 |
+
|
| 248 |
+
**Choose a10g when:**
|
| 249 |
+
- Model <13B parameters
|
| 250 |
+
- Budget conscious
|
| 251 |
+
- Training time not critical
|
| 252 |
+
|
| 253 |
+
**Choose a100 when:**
|
| 254 |
+
- Model 13B+ parameters
|
| 255 |
+
- Need fastest training
|
| 256 |
+
- Memory requirements high
|
| 257 |
+
- Budget allows
|
| 258 |
+
|
| 259 |
+
### Single vs Multi-GPU
|
| 260 |
+
|
| 261 |
+
**Choose single GPU when:**
|
| 262 |
+
- Model <7B parameters
|
| 263 |
+
- Budget constrained
|
| 264 |
+
- Simpler debugging
|
| 265 |
+
|
| 266 |
+
**Choose multi-GPU when:**
|
| 267 |
+
- Model >13B parameters
|
| 268 |
+
- Need faster training
|
| 269 |
+
- Large batch sizes required
|
| 270 |
+
- Cost-effective for large jobs
|
| 271 |
+
|
| 272 |
+
## Quick Reference
|
| 273 |
+
|
| 274 |
+
```python
|
| 275 |
+
# Model size β Hardware selection
|
| 276 |
+
HARDWARE_MAP = {
|
| 277 |
+
"<1B": "t4-small",
|
| 278 |
+
"1-3B": "a10g-small",
|
| 279 |
+
"3-7B": "a10g-large",
|
| 280 |
+
"7-13B": "a10g-large (LoRA) or a100-large",
|
| 281 |
+
">13B": "a100-large (LoRA required)"
|
| 282 |
+
}
|
| 283 |
+
```
|
.agents/skills/huggingface-llm-trainer/references/hub_saving.md
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Saving Training Results to Hugging Face Hub
|
| 2 |
+
|
| 3 |
+
**β οΈ CRITICAL:** Training environments are ephemeral. ALL results are lost when a job completes unless pushed to the Hub.
|
| 4 |
+
|
| 5 |
+
## Why Hub Push is Required
|
| 6 |
+
|
| 7 |
+
When running on Hugging Face Jobs:
|
| 8 |
+
- Environment is temporary
|
| 9 |
+
- All files deleted on job completion
|
| 10 |
+
- No local disk persistence
|
| 11 |
+
- Cannot access results after job ends
|
| 12 |
+
|
| 13 |
+
**Without Hub push, training is completely wasted.**
|
| 14 |
+
|
| 15 |
+
## Required Configuration
|
| 16 |
+
|
| 17 |
+
### 1. Training Configuration
|
| 18 |
+
|
| 19 |
+
In your SFTConfig or trainer config:
|
| 20 |
+
|
| 21 |
+
```python
|
| 22 |
+
SFTConfig(
|
| 23 |
+
push_to_hub=True, # Enable Hub push
|
| 24 |
+
hub_model_id="username/model-name", # Target repository
|
| 25 |
+
)
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
### 2. Job Configuration
|
| 29 |
+
|
| 30 |
+
When submitting the job:
|
| 31 |
+
|
| 32 |
+
```python
|
| 33 |
+
hf_jobs("uv", {
|
| 34 |
+
"script": "train.py",
|
| 35 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"} # Provide authentication
|
| 36 |
+
})
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
**The `$HF_TOKEN` placeholder is automatically replaced with your Hugging Face token.**
|
| 40 |
+
|
| 41 |
+
## Complete Example
|
| 42 |
+
|
| 43 |
+
```python
|
| 44 |
+
# train.py
|
| 45 |
+
# /// script
|
| 46 |
+
# dependencies = ["trl"]
|
| 47 |
+
# ///
|
| 48 |
+
|
| 49 |
+
from trl import SFTTrainer, SFTConfig
|
| 50 |
+
from datasets import load_dataset
|
| 51 |
+
|
| 52 |
+
dataset = load_dataset("trl-lib/Capybara", split="train")
|
| 53 |
+
|
| 54 |
+
# Configure with Hub push
|
| 55 |
+
config = SFTConfig(
|
| 56 |
+
output_dir="my-model",
|
| 57 |
+
num_train_epochs=3,
|
| 58 |
+
|
| 59 |
+
# β
CRITICAL: Hub push configuration
|
| 60 |
+
push_to_hub=True,
|
| 61 |
+
hub_model_id="myusername/my-trained-model",
|
| 62 |
+
|
| 63 |
+
# Optional: Push strategy
|
| 64 |
+
push_to_hub_model_id="myusername/my-trained-model",
|
| 65 |
+
push_to_hub_organization=None,
|
| 66 |
+
push_to_hub_token=None, # Uses environment token
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
trainer = SFTTrainer(
|
| 70 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 71 |
+
train_dataset=dataset,
|
| 72 |
+
args=config,
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
trainer.train()
|
| 76 |
+
|
| 77 |
+
# β
Push final model
|
| 78 |
+
trainer.push_to_hub()
|
| 79 |
+
|
| 80 |
+
print("β
Model saved to: https://huggingface.co/myusername/my-trained-model")
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
**Submit with authentication:**
|
| 84 |
+
|
| 85 |
+
```python
|
| 86 |
+
hf_jobs("uv", {
|
| 87 |
+
"script": "train.py",
|
| 88 |
+
"flavor": "a10g-large",
|
| 89 |
+
"timeout": "2h",
|
| 90 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"} # β
Required!
|
| 91 |
+
})
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
## What Gets Saved
|
| 95 |
+
|
| 96 |
+
When `push_to_hub=True`:
|
| 97 |
+
|
| 98 |
+
1. **Model weights** - Final trained parameters
|
| 99 |
+
2. **Tokenizer** - Associated tokenizer
|
| 100 |
+
3. **Configuration** - Model config (config.json)
|
| 101 |
+
4. **Training arguments** - Hyperparameters used
|
| 102 |
+
5. **Model card** - Auto-generated documentation
|
| 103 |
+
6. **Checkpoints** - If `save_strategy="steps"` enabled
|
| 104 |
+
|
| 105 |
+
## Checkpoint Saving
|
| 106 |
+
|
| 107 |
+
Save intermediate checkpoints during training:
|
| 108 |
+
|
| 109 |
+
```python
|
| 110 |
+
SFTConfig(
|
| 111 |
+
output_dir="my-model",
|
| 112 |
+
push_to_hub=True,
|
| 113 |
+
hub_model_id="username/my-model",
|
| 114 |
+
|
| 115 |
+
# Checkpoint configuration
|
| 116 |
+
save_strategy="steps",
|
| 117 |
+
save_steps=100, # Save every 100 steps
|
| 118 |
+
save_total_limit=3, # Keep only last 3 checkpoints
|
| 119 |
+
)
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
**Benefits:**
|
| 123 |
+
- Resume training if job fails
|
| 124 |
+
- Compare checkpoint performance
|
| 125 |
+
- Use intermediate models
|
| 126 |
+
|
| 127 |
+
**Checkpoints are pushed to:** `username/my-model` (same repo)
|
| 128 |
+
|
| 129 |
+
## Authentication Methods
|
| 130 |
+
|
| 131 |
+
### Method 1: Automatic Token (Recommended)
|
| 132 |
+
|
| 133 |
+
```python
|
| 134 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
Uses your logged-in Hugging Face token automatically.
|
| 138 |
+
|
| 139 |
+
### Method 2: Explicit Token
|
| 140 |
+
|
| 141 |
+
```python
|
| 142 |
+
"secrets": {"HF_TOKEN": "hf_abc123..."}
|
| 143 |
+
```
|
| 144 |
+
|
| 145 |
+
Provide token explicitly (not recommended for security).
|
| 146 |
+
|
| 147 |
+
### Method 3: Environment Variable
|
| 148 |
+
|
| 149 |
+
```python
|
| 150 |
+
"env": {"HF_TOKEN": "hf_abc123..."}
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
Pass as regular environment variable (less secure than secrets).
|
| 154 |
+
|
| 155 |
+
**Always prefer Method 1** for security and convenience.
|
| 156 |
+
|
| 157 |
+
## Verification Checklist
|
| 158 |
+
|
| 159 |
+
Before submitting any training job, verify:
|
| 160 |
+
|
| 161 |
+
- [ ] `push_to_hub=True` in training config
|
| 162 |
+
- [ ] `hub_model_id` is specified (format: `username/model-name`)
|
| 163 |
+
- [ ] `secrets={"HF_TOKEN": "$HF_TOKEN"}` in job config
|
| 164 |
+
- [ ] Repository name doesn't conflict with existing repos
|
| 165 |
+
- [ ] You have write access to the target namespace
|
| 166 |
+
|
| 167 |
+
## Repository Setup
|
| 168 |
+
|
| 169 |
+
### Automatic Creation
|
| 170 |
+
|
| 171 |
+
If repository doesn't exist, it's created automatically when first pushing.
|
| 172 |
+
|
| 173 |
+
### Manual Creation
|
| 174 |
+
|
| 175 |
+
Create repository before training:
|
| 176 |
+
|
| 177 |
+
```python
|
| 178 |
+
from huggingface_hub import HfApi
|
| 179 |
+
|
| 180 |
+
api = HfApi()
|
| 181 |
+
api.create_repo(
|
| 182 |
+
repo_id="username/model-name",
|
| 183 |
+
repo_type="model",
|
| 184 |
+
private=False, # or True for private repo
|
| 185 |
+
)
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
### Repository Naming
|
| 189 |
+
|
| 190 |
+
**Valid names:**
|
| 191 |
+
- `username/my-model`
|
| 192 |
+
- `username/model-name`
|
| 193 |
+
- `organization/model-name`
|
| 194 |
+
|
| 195 |
+
**Invalid names:**
|
| 196 |
+
- `model-name` (missing username)
|
| 197 |
+
- `username/model name` (spaces not allowed)
|
| 198 |
+
- `username/MODEL` (uppercase discouraged)
|
| 199 |
+
|
| 200 |
+
## Troubleshooting
|
| 201 |
+
|
| 202 |
+
### Error: 401 Unauthorized
|
| 203 |
+
|
| 204 |
+
**Cause:** HF_TOKEN not provided or invalid
|
| 205 |
+
|
| 206 |
+
**Solutions:**
|
| 207 |
+
1. Verify `secrets={"HF_TOKEN": "$HF_TOKEN"}` in job config
|
| 208 |
+
2. Check you're logged in: `hf auth whoami`
|
| 209 |
+
3. Re-login: `hf auth login`
|
| 210 |
+
|
| 211 |
+
### Error: 403 Forbidden
|
| 212 |
+
|
| 213 |
+
**Cause:** No write access to repository
|
| 214 |
+
|
| 215 |
+
**Solutions:**
|
| 216 |
+
1. Check repository namespace matches your username
|
| 217 |
+
2. Verify you're a member of organization (if using org namespace)
|
| 218 |
+
3. Check repository isn't private (if accessing org repo)
|
| 219 |
+
|
| 220 |
+
### Error: Repository not found
|
| 221 |
+
|
| 222 |
+
**Cause:** Repository doesn't exist and auto-creation failed
|
| 223 |
+
|
| 224 |
+
**Solutions:**
|
| 225 |
+
1. Manually create repository first
|
| 226 |
+
2. Check repository name format
|
| 227 |
+
3. Verify namespace exists
|
| 228 |
+
|
| 229 |
+
### Error: Push failed during training
|
| 230 |
+
|
| 231 |
+
**Cause:** Network issues or Hub unavailable
|
| 232 |
+
|
| 233 |
+
**Solutions:**
|
| 234 |
+
1. Training continues but final push fails
|
| 235 |
+
2. Checkpoints may be saved
|
| 236 |
+
3. Re-run push manually after job completes
|
| 237 |
+
|
| 238 |
+
### Issue: Model saved but not visible
|
| 239 |
+
|
| 240 |
+
**Possible causes:**
|
| 241 |
+
1. Repository is privateβcheck https://huggingface.co/username
|
| 242 |
+
2. Wrong namespaceβverify `hub_model_id` matches login
|
| 243 |
+
3. Push still in progressβwait a few minutes
|
| 244 |
+
|
| 245 |
+
## Manual Push After Training
|
| 246 |
+
|
| 247 |
+
If training completes but push fails, push manually:
|
| 248 |
+
|
| 249 |
+
```python
|
| 250 |
+
from transformers import AutoModel, AutoTokenizer
|
| 251 |
+
|
| 252 |
+
# Load from local checkpoint
|
| 253 |
+
model = AutoModel.from_pretrained("./output_dir")
|
| 254 |
+
tokenizer = AutoTokenizer.from_pretrained("./output_dir")
|
| 255 |
+
|
| 256 |
+
# Push to Hub
|
| 257 |
+
model.push_to_hub("username/model-name", token="hf_abc123...")
|
| 258 |
+
tokenizer.push_to_hub("username/model-name", token="hf_abc123...")
|
| 259 |
+
```
|
| 260 |
+
|
| 261 |
+
**Note:** Only possible if job hasn't completed (files still exist).
|
| 262 |
+
|
| 263 |
+
## Best Practices
|
| 264 |
+
|
| 265 |
+
1. **Always enable `push_to_hub=True`**
|
| 266 |
+
2. **Use checkpoint saving** for long training runs
|
| 267 |
+
3. **Verify Hub push** in logs before job completes
|
| 268 |
+
4. **Set appropriate `save_total_limit`** to avoid excessive checkpoints
|
| 269 |
+
5. **Use descriptive repo names** (e.g., `qwen-capybara-sft` not `model1`)
|
| 270 |
+
6. **Add model card** with training details
|
| 271 |
+
7. **Tag models** with relevant tags (e.g., `text-generation`, `fine-tuned`)
|
| 272 |
+
|
| 273 |
+
## Monitoring Push Progress
|
| 274 |
+
|
| 275 |
+
Check logs for push progress:
|
| 276 |
+
|
| 277 |
+
```python
|
| 278 |
+
hf_jobs("logs", {"job_id": "your-job-id"})
|
| 279 |
+
```
|
| 280 |
+
|
| 281 |
+
**Look for:**
|
| 282 |
+
```
|
| 283 |
+
Pushing model to username/model-name...
|
| 284 |
+
Upload file pytorch_model.bin: 100%
|
| 285 |
+
β
Model pushed successfully
|
| 286 |
+
```
|
| 287 |
+
|
| 288 |
+
## Example: Full Production Setup
|
| 289 |
+
|
| 290 |
+
```python
|
| 291 |
+
# production_train.py
|
| 292 |
+
# /// script
|
| 293 |
+
# dependencies = ["trl>=0.12.0", "peft>=0.7.0"]
|
| 294 |
+
# ///
|
| 295 |
+
|
| 296 |
+
from datasets import load_dataset
|
| 297 |
+
from peft import LoraConfig
|
| 298 |
+
from trl import SFTTrainer, SFTConfig
|
| 299 |
+
import os
|
| 300 |
+
|
| 301 |
+
# Verify token is available
|
| 302 |
+
assert "HF_TOKEN" in os.environ, "HF_TOKEN not found in environment!"
|
| 303 |
+
|
| 304 |
+
# Load dataset
|
| 305 |
+
dataset = load_dataset("trl-lib/Capybara", split="train")
|
| 306 |
+
print(f"β
Dataset loaded: {len(dataset)} examples")
|
| 307 |
+
|
| 308 |
+
# Configure with comprehensive Hub settings
|
| 309 |
+
config = SFTConfig(
|
| 310 |
+
output_dir="qwen-capybara-sft",
|
| 311 |
+
|
| 312 |
+
# Hub configuration
|
| 313 |
+
push_to_hub=True,
|
| 314 |
+
hub_model_id="myusername/qwen-capybara-sft",
|
| 315 |
+
hub_strategy="checkpoint", # Push checkpoints
|
| 316 |
+
|
| 317 |
+
# Checkpoint configuration
|
| 318 |
+
save_strategy="steps",
|
| 319 |
+
save_steps=100,
|
| 320 |
+
save_total_limit=3,
|
| 321 |
+
|
| 322 |
+
# Training settings
|
| 323 |
+
num_train_epochs=3,
|
| 324 |
+
per_device_train_batch_size=4,
|
| 325 |
+
|
| 326 |
+
# Logging
|
| 327 |
+
logging_steps=10,
|
| 328 |
+
logging_first_step=True,
|
| 329 |
+
)
|
| 330 |
+
|
| 331 |
+
# Train with LoRA
|
| 332 |
+
trainer = SFTTrainer(
|
| 333 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 334 |
+
train_dataset=dataset,
|
| 335 |
+
args=config,
|
| 336 |
+
peft_config=LoraConfig(r=16, lora_alpha=32),
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
print("π Starting training...")
|
| 340 |
+
trainer.train()
|
| 341 |
+
|
| 342 |
+
print("πΎ Pushing final model to Hub...")
|
| 343 |
+
trainer.push_to_hub()
|
| 344 |
+
|
| 345 |
+
print("β
Training complete!")
|
| 346 |
+
print(f"Model available at: https://huggingface.co/myusername/qwen-capybara-sft")
|
| 347 |
+
```
|
| 348 |
+
|
| 349 |
+
**Submit:**
|
| 350 |
+
|
| 351 |
+
```python
|
| 352 |
+
hf_jobs("uv", {
|
| 353 |
+
"script": "production_train.py",
|
| 354 |
+
"flavor": "a10g-large",
|
| 355 |
+
"timeout": "6h",
|
| 356 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 357 |
+
})
|
| 358 |
+
```
|
| 359 |
+
|
| 360 |
+
## Key Takeaway
|
| 361 |
+
|
| 362 |
+
**Without `push_to_hub=True` and `secrets={"HF_TOKEN": "$HF_TOKEN"}`, all training results are permanently lost.**
|
| 363 |
+
|
| 364 |
+
Always verify both are configured before submitting any training job.
|
.agents/skills/huggingface-llm-trainer/references/local_training_macos.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Local Training on macOS (Apple Silicon)
|
| 2 |
+
|
| 3 |
+
Run small LoRA fine-tuning jobs locally on Mac for smoke tests and quick iteration before submitting to HF Jobs.
|
| 4 |
+
|
| 5 |
+
## When to Use Local Mac vs HF Jobs
|
| 6 |
+
|
| 7 |
+
| Local Mac | HF Jobs / Cloud GPU |
|
| 8 |
+
|-----------|-------------------|
|
| 9 |
+
| Model β€3B, text-only | Model 7B+ |
|
| 10 |
+
| LoRA/PEFT only | QLoRA 4-bit (CUDA/bitsandbytes) |
|
| 11 |
+
| Short context (β€1024) | Long context / full fine-tuning |
|
| 12 |
+
| Smoke tests, dataset validation | Production runs, VLMs |
|
| 13 |
+
|
| 14 |
+
**Typical workflow:** local smoke test β HF Jobs with same config β export/quantize ([gguf_conversion.md](gguf_conversion.md))
|
| 15 |
+
|
| 16 |
+
## Recommended Defaults
|
| 17 |
+
|
| 18 |
+
| Setting | Value | Notes |
|
| 19 |
+
|---------|-------|-------|
|
| 20 |
+
| Model size | 0.5Bβ1.5B first run | Scale up after verifying |
|
| 21 |
+
| Max seq length | 512β1024 | Lower = less memory |
|
| 22 |
+
| Batch size | 1 | Scale via gradient accumulation |
|
| 23 |
+
| Gradient accumulation | 8β16 | Effective batch = 8β16 |
|
| 24 |
+
| LoRA rank (r) | 8β16 | alpha = 2Γr |
|
| 25 |
+
| Dtype | float32 | fp16 causes NaN on MPS; bf16 only on M1 Pro+ and M2/M3/M4 |
|
| 26 |
+
|
| 27 |
+
### Memory by hardware
|
| 28 |
+
|
| 29 |
+
| Unified RAM | Max Model Size |
|
| 30 |
+
|-------------|---------------|
|
| 31 |
+
| 16 GB | ~0.5Bβ1.5B |
|
| 32 |
+
| 32 GB | ~1.5Bβ3B |
|
| 33 |
+
| 64 GB | ~3B (short context) |
|
| 34 |
+
|
| 35 |
+
## Setup
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
xcode-select --install
|
| 39 |
+
python3 -m venv .venv && source .venv/bin/activate
|
| 40 |
+
pip install -U "torch>=2.2" "transformers>=4.40" "trl>=0.12" "peft>=0.10" \
|
| 41 |
+
datasets accelerate safetensors huggingface_hub
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
Verify MPS:
|
| 45 |
+
```bash
|
| 46 |
+
python -c "import torch; print(torch.__version__, '| MPS:', torch.backends.mps.is_available())"
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
Optional β configure Accelerate for local Mac (no distributed, no mixed precision, MPS device):
|
| 50 |
+
```bash
|
| 51 |
+
accelerate config
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
## Training Script
|
| 55 |
+
|
| 56 |
+
<details>
|
| 57 |
+
<summary><strong>train_lora_sft.py</strong></summary>
|
| 58 |
+
|
| 59 |
+
```python
|
| 60 |
+
import os
|
| 61 |
+
from dataclasses import dataclass
|
| 62 |
+
from typing import Optional
|
| 63 |
+
import torch
|
| 64 |
+
from datasets import load_dataset
|
| 65 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer, set_seed
|
| 66 |
+
from peft import LoraConfig
|
| 67 |
+
from trl import SFTTrainer, SFTConfig
|
| 68 |
+
|
| 69 |
+
set_seed(42)
|
| 70 |
+
|
| 71 |
+
@dataclass
|
| 72 |
+
class Cfg:
|
| 73 |
+
model_id: str = os.environ.get("MODEL_ID", "Qwen/Qwen2.5-0.5B-Instruct")
|
| 74 |
+
dataset_id: str = os.environ.get("DATASET_ID", "HuggingFaceH4/ultrachat_200k")
|
| 75 |
+
dataset_split: str = os.environ.get("DATASET_SPLIT", "train_sft[:500]")
|
| 76 |
+
data_files: Optional[str] = os.environ.get("DATA_FILES", None)
|
| 77 |
+
text_field: str = os.environ.get("TEXT_FIELD", "")
|
| 78 |
+
messages_field: str = os.environ.get("MESSAGES_FIELD", "messages")
|
| 79 |
+
out_dir: str = os.environ.get("OUT_DIR", "outputs/local-lora")
|
| 80 |
+
max_seq_length: int = int(os.environ.get("MAX_SEQ_LENGTH", "512"))
|
| 81 |
+
max_steps: int = int(os.environ.get("MAX_STEPS", "-1"))
|
| 82 |
+
|
| 83 |
+
cfg = Cfg()
|
| 84 |
+
device = "mps" if torch.backends.mps.is_available() else "cpu"
|
| 85 |
+
|
| 86 |
+
tokenizer = AutoTokenizer.from_pretrained(cfg.model_id, use_fast=True)
|
| 87 |
+
if tokenizer.pad_token is None:
|
| 88 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 89 |
+
tokenizer.padding_side = "right"
|
| 90 |
+
|
| 91 |
+
model = AutoModelForCausalLM.from_pretrained(cfg.model_id, torch_dtype=torch.float32)
|
| 92 |
+
model.to(device)
|
| 93 |
+
model.config.use_cache = False
|
| 94 |
+
|
| 95 |
+
if cfg.data_files:
|
| 96 |
+
ds = load_dataset("json", data_files=cfg.data_files, split="train")
|
| 97 |
+
else:
|
| 98 |
+
ds = load_dataset(cfg.dataset_id, split=cfg.dataset_split)
|
| 99 |
+
|
| 100 |
+
def format_example(ex):
|
| 101 |
+
if cfg.text_field and isinstance(ex.get(cfg.text_field), str):
|
| 102 |
+
ex["text"] = ex[cfg.text_field]
|
| 103 |
+
return ex
|
| 104 |
+
msgs = ex.get(cfg.messages_field)
|
| 105 |
+
if isinstance(msgs, list):
|
| 106 |
+
if hasattr(tokenizer, "apply_chat_template"):
|
| 107 |
+
try:
|
| 108 |
+
ex["text"] = tokenizer.apply_chat_template(msgs, tokenize=False, add_generation_prompt=False)
|
| 109 |
+
return ex
|
| 110 |
+
except Exception:
|
| 111 |
+
pass
|
| 112 |
+
ex["text"] = "\n".join([str(m) for m in msgs])
|
| 113 |
+
return ex
|
| 114 |
+
ex["text"] = str(ex)
|
| 115 |
+
return ex
|
| 116 |
+
|
| 117 |
+
ds = ds.map(format_example)
|
| 118 |
+
ds = ds.remove_columns([c for c in ds.column_names if c != "text"])
|
| 119 |
+
|
| 120 |
+
lora = LoraConfig(r=16, lora_alpha=32, lora_dropout=0.05, bias="none",
|
| 121 |
+
task_type="CAUSAL_LM", target_modules=["q_proj", "k_proj", "v_proj", "o_proj"])
|
| 122 |
+
|
| 123 |
+
sft_kwargs = dict(
|
| 124 |
+
output_dir=cfg.out_dir, per_device_train_batch_size=1, gradient_accumulation_steps=8,
|
| 125 |
+
learning_rate=2e-4, logging_steps=10, save_steps=200, save_total_limit=2,
|
| 126 |
+
gradient_checkpointing=True, report_to="none", fp16=False, bf16=False,
|
| 127 |
+
max_seq_length=cfg.max_seq_length, dataset_text_field="text",
|
| 128 |
+
)
|
| 129 |
+
if cfg.max_steps > 0:
|
| 130 |
+
sft_kwargs["max_steps"] = cfg.max_steps
|
| 131 |
+
else:
|
| 132 |
+
sft_kwargs["num_train_epochs"] = 1
|
| 133 |
+
|
| 134 |
+
trainer = SFTTrainer(model=model, train_dataset=ds, peft_config=lora,
|
| 135 |
+
args=SFTConfig(**sft_kwargs), processing_class=tokenizer)
|
| 136 |
+
trainer.train()
|
| 137 |
+
trainer.save_model(cfg.out_dir)
|
| 138 |
+
print(f"β
Saved to: {cfg.out_dir}")
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
</details>
|
| 142 |
+
|
| 143 |
+
### Run
|
| 144 |
+
|
| 145 |
+
```bash
|
| 146 |
+
python train_lora_sft.py
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
**Env overrides:**
|
| 150 |
+
|
| 151 |
+
```bash
|
| 152 |
+
MODEL_ID="Qwen/Qwen2.5-1.5B-Instruct" python train_lora_sft.py # different model
|
| 153 |
+
MAX_STEPS=50 python train_lora_sft.py # quick 50-step test
|
| 154 |
+
DATA_FILES="my_data.jsonl" python train_lora_sft.py # local JSONL file
|
| 155 |
+
PYTORCH_ENABLE_MPS_FALLBACK=1 python train_lora_sft.py # MPS op fallback to CPU
|
| 156 |
+
PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 python train_lora_sft.py # disable MPS memory limit (use with caution)
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
**Local JSONL format** β chat messages or plain text:
|
| 160 |
+
```jsonl
|
| 161 |
+
{"messages": [{"role": "user", "content": "Hello"}, {"role": "assistant", "content": "Hi!"}]}
|
| 162 |
+
```
|
| 163 |
+
```jsonl
|
| 164 |
+
{"text": "User: Hello\nAssistant: Hi!"}
|
| 165 |
+
```
|
| 166 |
+
For plain text: `DATA_FILES="file.jsonl" TEXT_FIELD="text" MESSAGES_FIELD="" python train_lora_sft.py`
|
| 167 |
+
|
| 168 |
+
### Verify Success
|
| 169 |
+
|
| 170 |
+
- Loss decreases over steps
|
| 171 |
+
- `outputs/local-lora/` contains `adapter_config.json` + `*.safetensors`
|
| 172 |
+
|
| 173 |
+
## Quick Evaluation
|
| 174 |
+
|
| 175 |
+
<details>
|
| 176 |
+
<summary><strong>eval_generate.py</strong></summary>
|
| 177 |
+
|
| 178 |
+
```python
|
| 179 |
+
import os, torch
|
| 180 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM
|
| 181 |
+
from peft import PeftModel
|
| 182 |
+
|
| 183 |
+
BASE = os.environ.get("MODEL_ID", "Qwen/Qwen2.5-0.5B-Instruct")
|
| 184 |
+
ADAPTER = os.environ.get("ADAPTER_DIR", "outputs/local-lora")
|
| 185 |
+
device = "mps" if torch.backends.mps.is_available() else "cpu"
|
| 186 |
+
|
| 187 |
+
tokenizer = AutoTokenizer.from_pretrained(BASE, use_fast=True)
|
| 188 |
+
model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype=torch.float32)
|
| 189 |
+
model.to(device)
|
| 190 |
+
model = PeftModel.from_pretrained(model, ADAPTER)
|
| 191 |
+
|
| 192 |
+
prompt = os.environ.get("PROMPT", "Explain gradient accumulation in 3 bullet points.")
|
| 193 |
+
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
|
| 194 |
+
with torch.no_grad():
|
| 195 |
+
out = model.generate(**inputs, max_new_tokens=120, do_sample=True, temperature=0.7, top_p=0.9)
|
| 196 |
+
print(tokenizer.decode(out[0], skip_special_tokens=True))
|
| 197 |
+
```
|
| 198 |
+
|
| 199 |
+
</details>
|
| 200 |
+
|
| 201 |
+
## Troubleshooting (macOS-Specific)
|
| 202 |
+
|
| 203 |
+
For general training issues, see [troubleshooting.md](troubleshooting.md).
|
| 204 |
+
|
| 205 |
+
| Problem | Fix |
|
| 206 |
+
|---------|-----|
|
| 207 |
+
| MPS unsupported op / crash | `PYTORCH_ENABLE_MPS_FALLBACK=1` |
|
| 208 |
+
| OOM / system instability | Reduce `MAX_SEQ_LENGTH`, use smaller model, set `PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0` (caution) |
|
| 209 |
+
| fp16 NaN / loss explosion | Keep `fp16=False` (default), lower learning rate |
|
| 210 |
+
| LoRA "module not found" | Print `model.named_modules()` to find correct target names |
|
| 211 |
+
| TRL TypeError on args | Check TRL version; script uses `SFTConfig` + `processing_class` (TRL β₯0.12) |
|
| 212 |
+
| Intel Mac | No MPS β use HF Jobs instead |
|
| 213 |
+
|
| 214 |
+
**Common LoRA target modules by architecture:**
|
| 215 |
+
|
| 216 |
+
| Architecture | target_modules |
|
| 217 |
+
|-------------|---------------|
|
| 218 |
+
| Llama/Qwen/Mistral | `q_proj`, `k_proj`, `v_proj`, `o_proj` |
|
| 219 |
+
| GPT-2/GPT-J | `c_attn`, `c_proj` |
|
| 220 |
+
| BLOOM | `query_key_value`, `dense` |
|
| 221 |
+
|
| 222 |
+
## MLX Alternative
|
| 223 |
+
|
| 224 |
+
[MLX](https://github.com/ml-explore/mlx) offers tighter Apple Silicon integration but has a smaller ecosystem and less mature training APIs. For this skill's workflow (local validation β HF Jobs), PyTorch + MPS is recommended for consistency. See [mlx-lm](https://github.com/ml-explore/mlx-lm) for MLX-based fine-tuning.
|
| 225 |
+
|
| 226 |
+
## See Also
|
| 227 |
+
|
| 228 |
+
- [troubleshooting.md](troubleshooting.md) β General TRL troubleshooting
|
| 229 |
+
- [hardware_guide.md](hardware_guide.md) β GPU selection for HF Jobs
|
| 230 |
+
- [gguf_conversion.md](gguf_conversion.md) β Export for on-device inference
|
| 231 |
+
- [training_methods.md](training_methods.md) β SFT, DPO, GRPO overview
|
.agents/skills/huggingface-llm-trainer/references/reliability_principles.md
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Reliability Principles for Training Jobs
|
| 2 |
+
|
| 3 |
+
These principles are derived from real production failures and successful fixes. Following them prevents common failure modes and ensures reliable job execution.
|
| 4 |
+
|
| 5 |
+
## Principle 1: Always Verify Before Use
|
| 6 |
+
|
| 7 |
+
**Rule:** Never assume repos, datasets, or resources exist. Verify with tools first.
|
| 8 |
+
|
| 9 |
+
### What It Prevents
|
| 10 |
+
|
| 11 |
+
- **Non-existent datasets** - Jobs fail immediately when dataset doesn't exist
|
| 12 |
+
- **Typos in names** - Simple mistakes like "argilla-dpo-mix-7k" vs "ultrafeedback_binarized"
|
| 13 |
+
- **Incorrect paths** - Old or moved repos, renamed files
|
| 14 |
+
- **Missing dependencies** - Undocumented requirements
|
| 15 |
+
|
| 16 |
+
### How to Apply
|
| 17 |
+
|
| 18 |
+
**Before submitting ANY job:**
|
| 19 |
+
|
| 20 |
+
```python
|
| 21 |
+
# Verify dataset exists
|
| 22 |
+
dataset_search({"query": "dataset-name", "author": "author-name", "limit": 5})
|
| 23 |
+
hub_repo_details(["author/dataset-name"], repo_type="dataset")
|
| 24 |
+
|
| 25 |
+
# Verify model exists
|
| 26 |
+
hub_repo_details(["org/model-name"], repo_type="model")
|
| 27 |
+
|
| 28 |
+
# Check script/file paths (for URL-based scripts)
|
| 29 |
+
# Verify before using: https://github.com/user/repo/blob/main/script.py
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
**Examples that would have caught errors:**
|
| 33 |
+
|
| 34 |
+
```python
|
| 35 |
+
# β WRONG: Assumed dataset exists
|
| 36 |
+
hf_jobs("uv", {
|
| 37 |
+
"script": """...""",
|
| 38 |
+
"env": {"DATASET": "trl-lib/argilla-dpo-mix-7k"} # Doesn't exist!
|
| 39 |
+
})
|
| 40 |
+
|
| 41 |
+
# β
CORRECT: Verify first
|
| 42 |
+
dataset_search({"query": "argilla dpo", "author": "trl-lib"})
|
| 43 |
+
# Would show: "trl-lib/ultrafeedback_binarized" is the correct name
|
| 44 |
+
|
| 45 |
+
hub_repo_details(["trl-lib/ultrafeedback_binarized"], repo_type="dataset")
|
| 46 |
+
# Confirms it exists before using
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
### Implementation Checklist
|
| 50 |
+
|
| 51 |
+
- [ ] Check dataset exists before training
|
| 52 |
+
- [ ] Verify base model exists before fine-tuning
|
| 53 |
+
- [ ] Confirm adapter model exists before GGUF conversion
|
| 54 |
+
- [ ] Test script URLs are valid before submitting
|
| 55 |
+
- [ ] Validate file paths in repositories
|
| 56 |
+
- [ ] Check for recent updates/renames of resources
|
| 57 |
+
|
| 58 |
+
**Time cost:** 5-10 seconds
|
| 59 |
+
**Time saved:** Hours of failed job time + debugging
|
| 60 |
+
|
| 61 |
+
---
|
| 62 |
+
|
| 63 |
+
## Principle 2: Prioritize Reliability Over Performance
|
| 64 |
+
|
| 65 |
+
**Rule:** Default to what is most likely to succeed, not what is theoretically fastest.
|
| 66 |
+
|
| 67 |
+
### What It Prevents
|
| 68 |
+
|
| 69 |
+
- **Hardware incompatibilities** - Features that fail on certain GPUs
|
| 70 |
+
- **Unstable optimizations** - Speed-ups that cause crashes
|
| 71 |
+
- **Complex configurations** - More failure points
|
| 72 |
+
- **Build system issues** - Unreliable compilation methods
|
| 73 |
+
|
| 74 |
+
### How to Apply
|
| 75 |
+
|
| 76 |
+
**Choose reliability:**
|
| 77 |
+
|
| 78 |
+
```python
|
| 79 |
+
# β RISKY: Aggressive optimization that may fail
|
| 80 |
+
SFTConfig(
|
| 81 |
+
torch_compile=True, # Can fail on T4, A10G GPUs
|
| 82 |
+
optim="adamw_bnb_8bit", # Requires specific setup
|
| 83 |
+
fp16=False, # May cause training instability
|
| 84 |
+
...
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
# β
SAFE: Proven defaults
|
| 88 |
+
SFTConfig(
|
| 89 |
+
# torch_compile=True, # Commented with note: "Enable on H100 for 20% speedup"
|
| 90 |
+
optim="adamw_torch", # Standard, always works
|
| 91 |
+
fp16=True, # Stable and fast
|
| 92 |
+
...
|
| 93 |
+
)
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
**For build processes:**
|
| 97 |
+
|
| 98 |
+
```python
|
| 99 |
+
# β UNRELIABLE: Uses make (platform-dependent)
|
| 100 |
+
subprocess.run(["make", "-C", "/tmp/llama.cpp", "llama-quantize"], check=True)
|
| 101 |
+
|
| 102 |
+
# β
RELIABLE: Uses CMake (consistent, documented)
|
| 103 |
+
subprocess.run([
|
| 104 |
+
"cmake", "-B", "/tmp/llama.cpp/build", "-S", "/tmp/llama.cpp",
|
| 105 |
+
"-DGGML_CUDA=OFF" # Disable CUDA for faster, more reliable build
|
| 106 |
+
], check=True)
|
| 107 |
+
|
| 108 |
+
subprocess.run([
|
| 109 |
+
"cmake", "--build", "/tmp/llama.cpp/build",
|
| 110 |
+
"--target", "llama-quantize", "-j", "4"
|
| 111 |
+
], check=True)
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
### Real-World Example
|
| 115 |
+
|
| 116 |
+
**The `torch.compile` failure:**
|
| 117 |
+
- Added for "20% speedup" on H100
|
| 118 |
+
- **Failed fatally on T4-medium** with cryptic error
|
| 119 |
+
- Misdiagnosed as dataset issue (cost hours)
|
| 120 |
+
- **Fix:** Disable by default, add as optional comment
|
| 121 |
+
|
| 122 |
+
**Result:** Reliability > 20% performance gain
|
| 123 |
+
|
| 124 |
+
### Implementation Checklist
|
| 125 |
+
|
| 126 |
+
- [ ] Use proven, standard configurations by default
|
| 127 |
+
- [ ] Comment out performance optimizations with hardware notes
|
| 128 |
+
- [ ] Use stable build systems (CMake > make)
|
| 129 |
+
- [ ] Test on target hardware before production
|
| 130 |
+
- [ ] Document known incompatibilities
|
| 131 |
+
- [ ] Provide "safe" and "fast" variants when needed
|
| 132 |
+
|
| 133 |
+
**Performance loss:** 10-20% in best case
|
| 134 |
+
**Reliability gain:** 95%+ success rate vs 60-70%
|
| 135 |
+
|
| 136 |
+
---
|
| 137 |
+
|
| 138 |
+
## Principle 3: Create Atomic, Self-Contained Scripts
|
| 139 |
+
|
| 140 |
+
**Rule:** Scripts should work as complete, independent units. Don't remove parts to "simplify."
|
| 141 |
+
|
| 142 |
+
### What It Prevents
|
| 143 |
+
|
| 144 |
+
- **Missing dependencies** - Removed "unnecessary" packages that are actually required
|
| 145 |
+
- **Incomplete processes** - Skipped steps that seem redundant
|
| 146 |
+
- **Environment assumptions** - Scripts that need pre-setup
|
| 147 |
+
- **Partial failures** - Some parts work, others fail silently
|
| 148 |
+
|
| 149 |
+
### How to Apply
|
| 150 |
+
|
| 151 |
+
**Complete dependency specifications:**
|
| 152 |
+
|
| 153 |
+
```python
|
| 154 |
+
# β INCOMPLETE: "Simplified" by removing dependencies
|
| 155 |
+
# /// script
|
| 156 |
+
# dependencies = [
|
| 157 |
+
# "transformers",
|
| 158 |
+
# "peft",
|
| 159 |
+
# "torch",
|
| 160 |
+
# ]
|
| 161 |
+
# ///
|
| 162 |
+
|
| 163 |
+
# β
COMPLETE: All dependencies explicit
|
| 164 |
+
# /// script
|
| 165 |
+
# dependencies = [
|
| 166 |
+
# "transformers>=4.36.0",
|
| 167 |
+
# "peft>=0.7.0",
|
| 168 |
+
# "torch>=2.0.0",
|
| 169 |
+
# "accelerate>=0.24.0",
|
| 170 |
+
# "huggingface_hub>=0.20.0",
|
| 171 |
+
# "sentencepiece>=0.1.99", # Required for tokenizers
|
| 172 |
+
# "protobuf>=3.20.0", # Required for tokenizers
|
| 173 |
+
# "numpy",
|
| 174 |
+
# "gguf",
|
| 175 |
+
# ]
|
| 176 |
+
# ///
|
| 177 |
+
```
|
| 178 |
+
|
| 179 |
+
**Complete build processes:**
|
| 180 |
+
|
| 181 |
+
```python
|
| 182 |
+
# β INCOMPLETE: Assumes build tools exist
|
| 183 |
+
subprocess.run(["git", "clone", "https://github.com/ggerganov/llama.cpp.git", "/tmp/llama.cpp"])
|
| 184 |
+
subprocess.run(["make", "-C", "/tmp/llama.cpp", "llama-quantize"]) # FAILS: no gcc/make
|
| 185 |
+
|
| 186 |
+
# β
COMPLETE: Installs all requirements
|
| 187 |
+
subprocess.run(["apt-get", "update", "-qq"], check=True)
|
| 188 |
+
subprocess.run(["apt-get", "install", "-y", "-qq", "build-essential", "cmake"], check=True)
|
| 189 |
+
subprocess.run(["git", "clone", "https://github.com/ggerganov/llama.cpp.git", "/tmp/llama.cpp"])
|
| 190 |
+
# ... then build
|
| 191 |
+
```
|
| 192 |
+
|
| 193 |
+
### Real-World Example
|
| 194 |
+
|
| 195 |
+
**The `sentencepiece` failure:**
|
| 196 |
+
- Original script had it: worked fine
|
| 197 |
+
- "Simplified" version removed it: "doesn't look necessary"
|
| 198 |
+
- **GGUF conversion failed silently** - tokenizer couldn't convert
|
| 199 |
+
- Hard to debug: no obvious error message
|
| 200 |
+
- **Fix:** Restore all original dependencies
|
| 201 |
+
|
| 202 |
+
**Result:** Don't remove dependencies without thorough testing
|
| 203 |
+
|
| 204 |
+
### Implementation Checklist
|
| 205 |
+
|
| 206 |
+
- [ ] All dependencies in PEP 723 header with version pins
|
| 207 |
+
- [ ] All system packages installed by script
|
| 208 |
+
- [ ] No assumptions about pre-existing environment
|
| 209 |
+
- [ ] No "optional" steps that are actually required
|
| 210 |
+
- [ ] Test scripts in clean environment
|
| 211 |
+
- [ ] Document why each dependency is needed
|
| 212 |
+
|
| 213 |
+
**Complexity:** Slightly longer scripts
|
| 214 |
+
**Reliability:** Scripts "just work" every time
|
| 215 |
+
|
| 216 |
+
---
|
| 217 |
+
|
| 218 |
+
## Principle 4: Provide Clear Error Context
|
| 219 |
+
|
| 220 |
+
**Rule:** When things fail, make it obvious what went wrong and how to fix it.
|
| 221 |
+
|
| 222 |
+
### How to Apply
|
| 223 |
+
|
| 224 |
+
**Wrap subprocess calls:**
|
| 225 |
+
|
| 226 |
+
```python
|
| 227 |
+
# β UNCLEAR: Silent failure
|
| 228 |
+
subprocess.run([...], check=True, capture_output=True)
|
| 229 |
+
|
| 230 |
+
# β
CLEAR: Shows what failed
|
| 231 |
+
try:
|
| 232 |
+
result = subprocess.run(
|
| 233 |
+
[...],
|
| 234 |
+
check=True,
|
| 235 |
+
capture_output=True,
|
| 236 |
+
text=True
|
| 237 |
+
)
|
| 238 |
+
print(result.stdout)
|
| 239 |
+
if result.stderr:
|
| 240 |
+
print("Warnings:", result.stderr)
|
| 241 |
+
except subprocess.CalledProcessError as e:
|
| 242 |
+
print(f"β Command failed!")
|
| 243 |
+
print("STDOUT:", e.stdout)
|
| 244 |
+
print("STDERR:", e.stderr)
|
| 245 |
+
raise
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
**Validate inputs:**
|
| 249 |
+
|
| 250 |
+
```python
|
| 251 |
+
# β UNCLEAR: Fails later with cryptic error
|
| 252 |
+
model = load_model(MODEL_NAME)
|
| 253 |
+
|
| 254 |
+
# β
CLEAR: Fails fast with clear message
|
| 255 |
+
if not MODEL_NAME:
|
| 256 |
+
raise ValueError("MODEL_NAME environment variable not set!")
|
| 257 |
+
|
| 258 |
+
print(f"Loading model: {MODEL_NAME}")
|
| 259 |
+
try:
|
| 260 |
+
model = load_model(MODEL_NAME)
|
| 261 |
+
print(f"β
Model loaded successfully")
|
| 262 |
+
except Exception as e:
|
| 263 |
+
print(f"β Failed to load model: {MODEL_NAME}")
|
| 264 |
+
print(f"Error: {e}")
|
| 265 |
+
print("Hint: Check that model exists on Hub")
|
| 266 |
+
raise
|
| 267 |
+
```
|
| 268 |
+
|
| 269 |
+
### Implementation Checklist
|
| 270 |
+
|
| 271 |
+
- [ ] Wrap external calls with try/except
|
| 272 |
+
- [ ] Print stdout/stderr on failure
|
| 273 |
+
- [ ] Validate environment variables early
|
| 274 |
+
- [ ] Add progress indicators (β
, β, π)
|
| 275 |
+
- [ ] Include hints for common failures
|
| 276 |
+
- [ ] Log configuration at start
|
| 277 |
+
|
| 278 |
+
---
|
| 279 |
+
|
| 280 |
+
## Principle 5: Test the Happy Path on Known-Good Inputs
|
| 281 |
+
|
| 282 |
+
**Rule:** Before using new code in production, test with inputs you know work.
|
| 283 |
+
|
| 284 |
+
### How to Apply
|
| 285 |
+
|
| 286 |
+
**Known-good test inputs:**
|
| 287 |
+
|
| 288 |
+
```python
|
| 289 |
+
# For training
|
| 290 |
+
TEST_DATASET = "trl-lib/Capybara" # Small, well-formatted, widely used
|
| 291 |
+
TEST_MODEL = "Qwen/Qwen2.5-0.5B" # Small, fast, reliable
|
| 292 |
+
|
| 293 |
+
# For GGUF conversion
|
| 294 |
+
TEST_ADAPTER = "evalstate/qwen-capybara-medium" # Known working model
|
| 295 |
+
TEST_BASE = "Qwen/Qwen2.5-0.5B" # Compatible base
|
| 296 |
+
```
|
| 297 |
+
|
| 298 |
+
**Testing workflow:**
|
| 299 |
+
|
| 300 |
+
1. Test with known-good inputs first
|
| 301 |
+
2. If that works, try production inputs
|
| 302 |
+
3. If production fails, you know it's the inputs (not code)
|
| 303 |
+
4. Isolate the difference
|
| 304 |
+
|
| 305 |
+
### Implementation Checklist
|
| 306 |
+
|
| 307 |
+
- [ ] Maintain list of known-good test models/datasets
|
| 308 |
+
- [ ] Test new scripts with test inputs first
|
| 309 |
+
- [ ] Document what makes inputs "good"
|
| 310 |
+
- [ ] Keep test jobs cheap (small models, short timeouts)
|
| 311 |
+
- [ ] Only move to production after test succeeds
|
| 312 |
+
|
| 313 |
+
**Time cost:** 5-10 minutes for test run
|
| 314 |
+
**Debugging time saved:** Hours
|
| 315 |
+
|
| 316 |
+
---
|
| 317 |
+
|
| 318 |
+
## Summary: The Reliability Checklist
|
| 319 |
+
|
| 320 |
+
Before submitting ANY job:
|
| 321 |
+
|
| 322 |
+
### Pre-Flight Checks
|
| 323 |
+
- [ ] **Verified** all repos/datasets exist (hub_repo_details)
|
| 324 |
+
- [ ] **Tested** with known-good inputs if new code
|
| 325 |
+
- [ ] **Using** proven hardware/configuration
|
| 326 |
+
- [ ] **Included** all dependencies in PEP 723 header
|
| 327 |
+
- [ ] **Installed** system requirements (build tools, etc.)
|
| 328 |
+
- [ ] **Set** appropriate timeout (not default 30m)
|
| 329 |
+
- [ ] **Configured** Hub push with HF_TOKEN
|
| 330 |
+
- [ ] **Added** clear error handling
|
| 331 |
+
|
| 332 |
+
### Script Quality
|
| 333 |
+
- [ ] Self-contained (no external setup needed)
|
| 334 |
+
- [ ] Complete dependencies listed
|
| 335 |
+
- [ ] Build tools installed by script
|
| 336 |
+
- [ ] Progress indicators included
|
| 337 |
+
- [ ] Error messages are clear
|
| 338 |
+
- [ ] Configuration logged at start
|
| 339 |
+
|
| 340 |
+
### Job Configuration
|
| 341 |
+
- [ ] Timeout > expected runtime + 30% buffer
|
| 342 |
+
- [ ] Hardware appropriate for model size
|
| 343 |
+
- [ ] Secrets include HF_TOKEN
|
| 344 |
+
- [ ] Environment variables set correctly
|
| 345 |
+
- [ ] Cost estimated and acceptable
|
| 346 |
+
|
| 347 |
+
**Following these principles transforms job success rate from ~60-70% to ~95%+**
|
| 348 |
+
|
| 349 |
+
---
|
| 350 |
+
|
| 351 |
+
## When Principles Conflict
|
| 352 |
+
|
| 353 |
+
Sometimes reliability and performance conflict. Here's how to choose:
|
| 354 |
+
|
| 355 |
+
| Scenario | Choose | Rationale |
|
| 356 |
+
|----------|--------|-----------|
|
| 357 |
+
| Demo/test | Reliability | Fast failure is worse than slow success |
|
| 358 |
+
| Production (first run) | Reliability | Prove it works before optimizing |
|
| 359 |
+
| Production (proven) | Performance | Safe to optimize after validation |
|
| 360 |
+
| Time-critical | Reliability | Failures cause more delay than slow runs |
|
| 361 |
+
| Cost-critical | Balanced | Test with small model, then optimize |
|
| 362 |
+
|
| 363 |
+
**General rule:** Reliability first, optimize second.
|
| 364 |
+
|
| 365 |
+
---
|
| 366 |
+
|
| 367 |
+
## Further Reading
|
| 368 |
+
|
| 369 |
+
- `troubleshooting.md` - Common issues and fixes
|
| 370 |
+
- `training_patterns.md` - Proven training configurations
|
| 371 |
+
- `gguf_conversion.md` - Production GGUF workflow
|
.agents/skills/huggingface-llm-trainer/references/trackio_guide.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Trackio Integration for TRL Training
|
| 2 |
+
|
| 3 |
+
**Trackio** is an experiment tracking library that provides real-time metrics visualization for remote training on Hugging Face Jobs infrastructure.
|
| 4 |
+
|
| 5 |
+
β οΈ **IMPORTANT**: For Jobs training (remote cloud GPUs):
|
| 6 |
+
- Training happens on ephemeral cloud runners (not your local machine)
|
| 7 |
+
- Trackio syncs metrics to a Hugging Face Space for real-time monitoring
|
| 8 |
+
- Without a Space, metrics are lost when the job completes
|
| 9 |
+
- The Space dashboard persists your training metrics permanently
|
| 10 |
+
|
| 11 |
+
## Setting Up Trackio for Jobs
|
| 12 |
+
|
| 13 |
+
**Step 1: Add trackio dependency**
|
| 14 |
+
```python
|
| 15 |
+
# /// script
|
| 16 |
+
# dependencies = [
|
| 17 |
+
# "trl>=0.12.0",
|
| 18 |
+
# "trackio", # Required!
|
| 19 |
+
# ]
|
| 20 |
+
# ///
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
**Step 2: Create a Trackio Space (one-time setup)**
|
| 24 |
+
|
| 25 |
+
**Option A: Let Trackio auto-create (Recommended)**
|
| 26 |
+
Pass a `space_id` to `trackio.init()` and Trackio will automatically create the Space if it doesn't exist.
|
| 27 |
+
|
| 28 |
+
**Option B: Create manually**
|
| 29 |
+
- Create Space via Hub UI at https://huggingface.co/new-space
|
| 30 |
+
- Select Gradio SDK
|
| 31 |
+
- OR use command: `hf repos create my-trackio-dashboard --type space --space-sdk gradio`
|
| 32 |
+
|
| 33 |
+
**Step 3: Initialize Trackio with space_id**
|
| 34 |
+
```python
|
| 35 |
+
import trackio
|
| 36 |
+
|
| 37 |
+
trackio.init(
|
| 38 |
+
project="my-training",
|
| 39 |
+
space_id="username/trackio", # CRITICAL for Jobs! Replace 'username' with your HF username
|
| 40 |
+
config={
|
| 41 |
+
"model": "Qwen/Qwen2.5-0.5B",
|
| 42 |
+
"dataset": "trl-lib/Capybara",
|
| 43 |
+
"learning_rate": 2e-5,
|
| 44 |
+
}
|
| 45 |
+
)
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
**Step 4: Configure TRL to use Trackio**
|
| 49 |
+
```python
|
| 50 |
+
SFTConfig(
|
| 51 |
+
report_to="trackio",
|
| 52 |
+
# ... other config
|
| 53 |
+
)
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
**Step 5: Finish tracking**
|
| 57 |
+
```python
|
| 58 |
+
trainer.train()
|
| 59 |
+
trackio.finish() # Ensures final metrics are synced
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
## What Trackio Tracks
|
| 63 |
+
|
| 64 |
+
Trackio automatically logs:
|
| 65 |
+
- β
Training loss
|
| 66 |
+
- β
Learning rate
|
| 67 |
+
- β
GPU utilization
|
| 68 |
+
- β
Memory usage
|
| 69 |
+
- β
Training throughput
|
| 70 |
+
- β
Custom metrics
|
| 71 |
+
|
| 72 |
+
## How It Works with Jobs
|
| 73 |
+
|
| 74 |
+
1. **Training runs** β Metrics logged to local SQLite DB
|
| 75 |
+
2. **Every 5 minutes** β Trackio syncs DB to HF Dataset (Parquet)
|
| 76 |
+
3. **Space dashboard** β Reads from Dataset, displays metrics in real-time
|
| 77 |
+
4. **Job completes** β Final sync ensures all metrics persisted
|
| 78 |
+
|
| 79 |
+
## Default Configuration Pattern
|
| 80 |
+
|
| 81 |
+
**Use sensible defaults for trackio configuration unless user requests otherwise.**
|
| 82 |
+
|
| 83 |
+
### Recommended Defaults
|
| 84 |
+
|
| 85 |
+
```python
|
| 86 |
+
import trackio
|
| 87 |
+
|
| 88 |
+
trackio.init(
|
| 89 |
+
project="qwen-capybara-sft",
|
| 90 |
+
name="baseline-run", # Descriptive name user will recognize
|
| 91 |
+
space_id="username/trackio", # Default space: {username}/trackio
|
| 92 |
+
config={
|
| 93 |
+
# Keep config minimal - hyperparameters and model/dataset info only
|
| 94 |
+
"model": "Qwen/Qwen2.5-0.5B",
|
| 95 |
+
"dataset": "trl-lib/Capybara",
|
| 96 |
+
"learning_rate": 2e-5,
|
| 97 |
+
"num_epochs": 3,
|
| 98 |
+
}
|
| 99 |
+
)
|
| 100 |
+
```
|
| 101 |
+
|
| 102 |
+
**Key principles:**
|
| 103 |
+
- **Space ID**: Use `{username}/trackio` with "trackio" as default space name
|
| 104 |
+
- **Run naming**: Unless otherwise specified, name the run in a way the user will recognize
|
| 105 |
+
- **Config**: Keep minimal - don't automatically capture job metadata unless requested
|
| 106 |
+
- **Grouping**: Optional - only use if user requests organizing related experiments
|
| 107 |
+
|
| 108 |
+
## Grouping Runs (Optional)
|
| 109 |
+
|
| 110 |
+
The `group` parameter helps organize related runs together in the dashboard sidebar. This is useful when user is running multiple experiments with different configurations but wants to compare them together:
|
| 111 |
+
|
| 112 |
+
```python
|
| 113 |
+
# Example: Group runs by experiment type
|
| 114 |
+
trackio.init(project="my-project", run_name="baseline-run-1", group="baseline")
|
| 115 |
+
trackio.init(project="my-project", run_name="augmented-run-1", group="augmented")
|
| 116 |
+
trackio.init(project="my-project", run_name="tuned-run-1", group="tuned")
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
Runs with the same group name can be grouped together in the sidebar, making it easier to compare related experiments. You can group by any configuration parameter:
|
| 120 |
+
|
| 121 |
+
```python
|
| 122 |
+
# Hyperparameter sweep - group by learning rate
|
| 123 |
+
trackio.init(project="hyperparam-sweep", run_name="lr-0.001-run", group="lr_0.001")
|
| 124 |
+
trackio.init(project="hyperparam-sweep", run_name="lr-0.01-run", group="lr_0.01")
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
## Environment Variables for Jobs
|
| 128 |
+
|
| 129 |
+
You can configure trackio using environment variables instead of passing parameters to `trackio.init()`. This is useful for managing configuration across multiple jobs.
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
**`HF_TOKEN`**
|
| 134 |
+
Required for creating Spaces and writing to datasets (passed via `secrets`):
|
| 135 |
+
```python
|
| 136 |
+
hf_jobs("uv", {
|
| 137 |
+
"script": "...",
|
| 138 |
+
"secrets": {
|
| 139 |
+
"HF_TOKEN": "$HF_TOKEN" # Enables Space creation and Hub push
|
| 140 |
+
}
|
| 141 |
+
})
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
### Example with Environment Variables
|
| 145 |
+
|
| 146 |
+
```python
|
| 147 |
+
hf_jobs("uv", {
|
| 148 |
+
"script": """
|
| 149 |
+
# Training script - trackio config from environment
|
| 150 |
+
import trackio
|
| 151 |
+
from datetime import datetime
|
| 152 |
+
|
| 153 |
+
# Auto-generate run name
|
| 154 |
+
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M")
|
| 155 |
+
run_name = f"sft_qwen25_{timestamp}"
|
| 156 |
+
|
| 157 |
+
# Project and space_id can come from environment variables
|
| 158 |
+
trackio.init(run_name=run_name, group="SFT")
|
| 159 |
+
|
| 160 |
+
# ... training code ...
|
| 161 |
+
trackio.finish()
|
| 162 |
+
""",
|
| 163 |
+
"flavor": "a10g-large",
|
| 164 |
+
"timeout": "2h",
|
| 165 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 166 |
+
})
|
| 167 |
+
```
|
| 168 |
+
|
| 169 |
+
**When to use environment variables:**
|
| 170 |
+
- Managing multiple jobs with same configuration
|
| 171 |
+
- Keeping training scripts portable across projects
|
| 172 |
+
- Separating configuration from code
|
| 173 |
+
|
| 174 |
+
**When to use direct parameters:**
|
| 175 |
+
- Single job with specific configuration
|
| 176 |
+
- When clarity in code is preferred
|
| 177 |
+
- When each job has different project/space
|
| 178 |
+
|
| 179 |
+
## Viewing the Dashboard
|
| 180 |
+
|
| 181 |
+
After starting training:
|
| 182 |
+
1. Navigate to the Space: `https://huggingface.co/spaces/username/trackio`
|
| 183 |
+
2. The Gradio dashboard shows all tracked experiments
|
| 184 |
+
3. Filter by project, compare runs, view charts with smoothing
|
| 185 |
+
|
| 186 |
+
## Recommendation
|
| 187 |
+
|
| 188 |
+
- **Trackio**: Best for real-time monitoring during long training runs
|
| 189 |
+
- **Weights & Biases**: Best for team collaboration, requires account
|
.agents/skills/huggingface-llm-trainer/references/training_methods.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# TRL Training Methods Overview
|
| 2 |
+
|
| 3 |
+
TRL (Transformer Reinforcement Learning) provides multiple training methods for fine-tuning and aligning language models. This reference provides a brief overview of each method.
|
| 4 |
+
|
| 5 |
+
## Supervised Fine-Tuning (SFT)
|
| 6 |
+
|
| 7 |
+
**What it is:** Standard instruction tuning with supervised learning on demonstration data.
|
| 8 |
+
|
| 9 |
+
**When to use:**
|
| 10 |
+
- Initial fine-tuning of base models on task-specific data
|
| 11 |
+
- Teaching new capabilities or domains
|
| 12 |
+
- Most common starting point for fine-tuning
|
| 13 |
+
|
| 14 |
+
**Dataset format:** Conversational format with "messages" field, OR text field, OR prompt/completion pairs
|
| 15 |
+
|
| 16 |
+
**Example:**
|
| 17 |
+
```python
|
| 18 |
+
from trl import SFTTrainer, SFTConfig
|
| 19 |
+
|
| 20 |
+
trainer = SFTTrainer(
|
| 21 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 22 |
+
train_dataset=dataset,
|
| 23 |
+
args=SFTConfig(
|
| 24 |
+
output_dir="my-model",
|
| 25 |
+
push_to_hub=True,
|
| 26 |
+
hub_model_id="username/my-model",
|
| 27 |
+
eval_strategy="no", # Disable eval for simple example
|
| 28 |
+
# max_length=1024 is the default - only set if you need different length
|
| 29 |
+
)
|
| 30 |
+
)
|
| 31 |
+
trainer.train()
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
**Note:** For production training with evaluation monitoring, see `scripts/train_sft_example.py`
|
| 35 |
+
|
| 36 |
+
**Documentation:** `hf_doc_fetch("https://huggingface.co/docs/trl/sft_trainer")`
|
| 37 |
+
|
| 38 |
+
## Direct Preference Optimization (DPO)
|
| 39 |
+
|
| 40 |
+
**What it is:** Alignment method that trains directly on preference pairs (chosen vs rejected responses) without requiring a reward model.
|
| 41 |
+
|
| 42 |
+
**When to use:**
|
| 43 |
+
- Aligning models to human preferences
|
| 44 |
+
- Improving response quality after SFT
|
| 45 |
+
- Have paired preference data (chosen/rejected responses)
|
| 46 |
+
|
| 47 |
+
**Dataset format:** Preference pairs with "chosen" and "rejected" fields
|
| 48 |
+
|
| 49 |
+
**Example:**
|
| 50 |
+
```python
|
| 51 |
+
from trl import DPOTrainer, DPOConfig
|
| 52 |
+
|
| 53 |
+
trainer = DPOTrainer(
|
| 54 |
+
model="Qwen/Qwen2.5-0.5B-Instruct", # Use instruct model
|
| 55 |
+
train_dataset=dataset,
|
| 56 |
+
args=DPOConfig(
|
| 57 |
+
output_dir="dpo-model",
|
| 58 |
+
beta=0.1, # KL penalty coefficient
|
| 59 |
+
eval_strategy="no", # Disable eval for simple example
|
| 60 |
+
# max_length=1024 is the default - only set if you need different length
|
| 61 |
+
)
|
| 62 |
+
)
|
| 63 |
+
trainer.train()
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
**Note:** For production training with evaluation monitoring, see `scripts/train_dpo_example.py`
|
| 67 |
+
|
| 68 |
+
**Documentation:** `hf_doc_fetch("https://huggingface.co/docs/trl/dpo_trainer")`
|
| 69 |
+
|
| 70 |
+
## Group Relative Policy Optimization (GRPO)
|
| 71 |
+
|
| 72 |
+
**What it is:** Online RL method that optimizes relative to group performance, useful for tasks with verifiable rewards.
|
| 73 |
+
|
| 74 |
+
**When to use:**
|
| 75 |
+
- Tasks with automatic reward signals (code execution, math verification)
|
| 76 |
+
- Online learning scenarios
|
| 77 |
+
- When DPO offline data is insufficient
|
| 78 |
+
|
| 79 |
+
**Dataset format:** Prompt-only format (model generates responses, reward computed online)
|
| 80 |
+
|
| 81 |
+
**Example:**
|
| 82 |
+
```python
|
| 83 |
+
# Use TRL maintained script
|
| 84 |
+
hf_jobs("uv", {
|
| 85 |
+
"script": "https://raw.githubusercontent.com/huggingface/trl/main/examples/scripts/grpo.py",
|
| 86 |
+
"script_args": [
|
| 87 |
+
"--model_name_or_path", "Qwen/Qwen2.5-0.5B-Instruct",
|
| 88 |
+
"--dataset_name", "trl-lib/math_shepherd",
|
| 89 |
+
"--output_dir", "grpo-model"
|
| 90 |
+
],
|
| 91 |
+
"flavor": "a10g-large",
|
| 92 |
+
"timeout": "4h",
|
| 93 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 94 |
+
})
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
**Documentation:** `hf_doc_fetch("https://huggingface.co/docs/trl/grpo_trainer")`
|
| 98 |
+
|
| 99 |
+
## Reward Modeling
|
| 100 |
+
|
| 101 |
+
**What it is:** Train a reward model to score responses, used as a component in RLHF pipelines.
|
| 102 |
+
|
| 103 |
+
**When to use:**
|
| 104 |
+
- Building RLHF pipeline
|
| 105 |
+
- Need automatic quality scoring
|
| 106 |
+
- Creating reward signals for PPO training
|
| 107 |
+
|
| 108 |
+
**Dataset format:** Preference pairs with "chosen" and "rejected" responses
|
| 109 |
+
|
| 110 |
+
**Documentation:** `hf_doc_fetch("https://huggingface.co/docs/trl/reward_trainer")`
|
| 111 |
+
|
| 112 |
+
## Method Selection Guide
|
| 113 |
+
|
| 114 |
+
| Method | Complexity | Data Required | Use Case |
|
| 115 |
+
|--------|-----------|---------------|----------|
|
| 116 |
+
| **SFT** | Low | Demonstrations | Initial fine-tuning |
|
| 117 |
+
| **DPO** | Medium | Paired preferences | Post-SFT alignment |
|
| 118 |
+
| **GRPO** | Medium | Prompts + reward fn | Online RL with automatic rewards |
|
| 119 |
+
| **Reward** | Medium | Paired preferences | Building RLHF pipeline |
|
| 120 |
+
|
| 121 |
+
## Recommended Pipeline
|
| 122 |
+
|
| 123 |
+
**For most use cases:**
|
| 124 |
+
1. **Start with SFT** - Fine-tune base model on task data
|
| 125 |
+
2. **Follow with DPO** - Align to preferences using paired data
|
| 126 |
+
3. **Optional: GGUF conversion** - Deploy for local inference
|
| 127 |
+
|
| 128 |
+
**For advanced RL scenarios:**
|
| 129 |
+
1. **Start with SFT** - Fine-tune base model
|
| 130 |
+
2. **Train reward model** - On preference data
|
| 131 |
+
|
| 132 |
+
## Dataset Format Reference
|
| 133 |
+
|
| 134 |
+
For complete dataset format specifications, use:
|
| 135 |
+
```python
|
| 136 |
+
hf_doc_fetch("https://huggingface.co/docs/trl/dataset_formats")
|
| 137 |
+
```
|
| 138 |
+
|
| 139 |
+
Or validate your dataset:
|
| 140 |
+
```bash
|
| 141 |
+
uv run https://huggingface.co/datasets/mcp-tools/skills/raw/main/dataset_inspector.py \
|
| 142 |
+
--dataset your/dataset --split train
|
| 143 |
+
```
|
| 144 |
+
|
| 145 |
+
## See Also
|
| 146 |
+
|
| 147 |
+
- `references/training_patterns.md` - Common training patterns and examples
|
| 148 |
+
- `scripts/train_sft_example.py` - Complete SFT template
|
| 149 |
+
- `scripts/train_dpo_example.py` - Complete DPO template
|
| 150 |
+
- [Dataset Inspector](https://huggingface.co/datasets/mcp-tools/skills/raw/main/dataset_inspector.py) - Dataset format validation tool
|
.agents/skills/huggingface-llm-trainer/references/training_patterns.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Common Training Patterns
|
| 2 |
+
|
| 3 |
+
This guide provides common training patterns and use cases for TRL on Hugging Face Jobs.
|
| 4 |
+
|
| 5 |
+
## Multi-GPU Training
|
| 6 |
+
|
| 7 |
+
Automatic distributed training across multiple GPUs. TRL/Accelerate handles distribution automatically:
|
| 8 |
+
|
| 9 |
+
```python
|
| 10 |
+
hf_jobs("uv", {
|
| 11 |
+
"script": """
|
| 12 |
+
# Your training script here (same as single GPU)
|
| 13 |
+
# No changes needed - Accelerate detects multiple GPUs
|
| 14 |
+
""",
|
| 15 |
+
"flavor": "a10g-largex2", # 2x A10G GPUs
|
| 16 |
+
"timeout": "4h",
|
| 17 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 18 |
+
})
|
| 19 |
+
```
|
| 20 |
+
|
| 21 |
+
**Tips for multi-GPU:**
|
| 22 |
+
- No code changes needed
|
| 23 |
+
- Use `per_device_train_batch_size` (per GPU, not total)
|
| 24 |
+
- Effective batch size = `per_device_train_batch_size` Γ `num_gpus` Γ `gradient_accumulation_steps`
|
| 25 |
+
- Monitor GPU utilization to ensure both GPUs are being used
|
| 26 |
+
|
| 27 |
+
## DPO Training (Preference Learning)
|
| 28 |
+
|
| 29 |
+
Train with preference data for alignment:
|
| 30 |
+
|
| 31 |
+
```python
|
| 32 |
+
hf_jobs("uv", {
|
| 33 |
+
"script": """
|
| 34 |
+
# /// script
|
| 35 |
+
# dependencies = ["trl>=0.12.0", "trackio"]
|
| 36 |
+
# ///
|
| 37 |
+
|
| 38 |
+
from datasets import load_dataset
|
| 39 |
+
from trl import DPOTrainer, DPOConfig
|
| 40 |
+
import trackio
|
| 41 |
+
|
| 42 |
+
dataset = load_dataset("trl-lib/ultrafeedback_binarized", split="train")
|
| 43 |
+
|
| 44 |
+
# Create train/eval split
|
| 45 |
+
dataset_split = dataset.train_test_split(test_size=0.1, seed=42)
|
| 46 |
+
|
| 47 |
+
config = DPOConfig(
|
| 48 |
+
output_dir="dpo-model",
|
| 49 |
+
push_to_hub=True,
|
| 50 |
+
hub_model_id="username/dpo-model",
|
| 51 |
+
num_train_epochs=1,
|
| 52 |
+
beta=0.1, # KL penalty coefficient
|
| 53 |
+
eval_strategy="steps",
|
| 54 |
+
eval_steps=50,
|
| 55 |
+
report_to="trackio",
|
| 56 |
+
run_name="baseline_run", # use a meaningful run name
|
| 57 |
+
# max_length=1024, # Default - only set if you need different sequence length
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
trainer = DPOTrainer(
|
| 61 |
+
model="Qwen/Qwen2.5-0.5B-Instruct", # Use instruct model as base
|
| 62 |
+
train_dataset=dataset_split["train"],
|
| 63 |
+
eval_dataset=dataset_split["test"], # IMPORTANT: Provide eval_dataset when eval_strategy is enabled
|
| 64 |
+
args=config,
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
trainer.train()
|
| 68 |
+
trainer.push_to_hub()
|
| 69 |
+
trackio.finish()
|
| 70 |
+
""",
|
| 71 |
+
"flavor": "a10g-large",
|
| 72 |
+
"timeout": "3h",
|
| 73 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 74 |
+
})
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
**For DPO documentation:** Use `hf_doc_fetch("https://huggingface.co/docs/trl/dpo_trainer")`
|
| 78 |
+
|
| 79 |
+
## GRPO Training (Online RL)
|
| 80 |
+
|
| 81 |
+
Group Relative Policy Optimization for online reinforcement learning:
|
| 82 |
+
|
| 83 |
+
```python
|
| 84 |
+
hf_jobs("uv", {
|
| 85 |
+
"script": "https://raw.githubusercontent.com/huggingface/trl/main/examples/scripts/grpo.py",
|
| 86 |
+
"script_args": [
|
| 87 |
+
"--model_name_or_path", "Qwen/Qwen2.5-0.5B-Instruct",
|
| 88 |
+
"--dataset_name", "trl-lib/math_shepherd",
|
| 89 |
+
"--output_dir", "grpo-model",
|
| 90 |
+
"--push_to_hub",
|
| 91 |
+
"--hub_model_id", "username/grpo-model"
|
| 92 |
+
],
|
| 93 |
+
"flavor": "a10g-large",
|
| 94 |
+
"timeout": "4h",
|
| 95 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 96 |
+
})
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
**For GRPO documentation:** Use `hf_doc_fetch("https://huggingface.co/docs/trl/grpo_trainer")`
|
| 100 |
+
|
| 101 |
+
## Trackio Configuration
|
| 102 |
+
|
| 103 |
+
**Use sensible defaults for trackio setup.** See `references/trackio_guide.md` for complete documentation including grouping runs for experiments.
|
| 104 |
+
|
| 105 |
+
### Basic Pattern
|
| 106 |
+
|
| 107 |
+
```python
|
| 108 |
+
import trackio
|
| 109 |
+
|
| 110 |
+
trackio.init(
|
| 111 |
+
project="my-training",
|
| 112 |
+
run_name="baseline-run", # Descriptive name user will recognize
|
| 113 |
+
space_id="username/trackio", # Default space: {username}/trackio
|
| 114 |
+
config={
|
| 115 |
+
# Keep config minimal - hyperparameters and model/dataset info only
|
| 116 |
+
"model": "Qwen/Qwen2.5-0.5B",
|
| 117 |
+
"dataset": "trl-lib/Capybara",
|
| 118 |
+
"learning_rate": 2e-5,
|
| 119 |
+
}
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
# Your training code...
|
| 123 |
+
|
| 124 |
+
trackio.finish()
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
### Grouping for Experiments (Optional)
|
| 128 |
+
|
| 129 |
+
When user wants to compare related runs, use the `group` parameter:
|
| 130 |
+
|
| 131 |
+
```python
|
| 132 |
+
# Hyperparameter sweep
|
| 133 |
+
trackio.init(project="hyperparam-sweep", run_name="lr-0.001", group="lr_0.001")
|
| 134 |
+
trackio.init(project="hyperparam-sweep", run_name="lr-0.01", group="lr_0.01")
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
## Pattern Selection Guide
|
| 138 |
+
|
| 139 |
+
| Use Case | Pattern | Hardware | Time |
|
| 140 |
+
|----------|---------|----------|------|
|
| 141 |
+
| SFT training | `scripts/train_sft_example.py` | a10g-large | 2-6 hours |
|
| 142 |
+
| Large dataset (>10K) | Multi-GPU | a10g-largex2 | 4-12 hours |
|
| 143 |
+
| Preference learning | DPO Training | a10g-large | 2-4 hours |
|
| 144 |
+
| Online RL | GRPO Training | a10g-large | 3-6 hours |
|
| 145 |
+
|
| 146 |
+
## Critical: Evaluation Dataset Requirements
|
| 147 |
+
|
| 148 |
+
**β οΈ IMPORTANT**: If you set `eval_strategy="steps"` or `eval_strategy="epoch"`, you **MUST** provide an `eval_dataset` to the trainer, or the training will hang.
|
| 149 |
+
|
| 150 |
+
### β
CORRECT - With eval dataset:
|
| 151 |
+
```python
|
| 152 |
+
dataset_split = dataset.train_test_split(test_size=0.1, seed=42)
|
| 153 |
+
|
| 154 |
+
trainer = SFTTrainer(
|
| 155 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 156 |
+
train_dataset=dataset_split["train"],
|
| 157 |
+
eval_dataset=dataset_split["test"], # β MUST provide when eval_strategy is enabled
|
| 158 |
+
args=SFTConfig(eval_strategy="steps", ...),
|
| 159 |
+
)
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
### β WRONG - Will hang:
|
| 163 |
+
```python
|
| 164 |
+
trainer = SFTTrainer(
|
| 165 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 166 |
+
train_dataset=dataset,
|
| 167 |
+
# NO eval_dataset but eval_strategy="steps" β WILL HANG
|
| 168 |
+
args=SFTConfig(eval_strategy="steps", ...),
|
| 169 |
+
)
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
### Option: Disable evaluation if no eval dataset
|
| 173 |
+
```python
|
| 174 |
+
config = SFTConfig(
|
| 175 |
+
eval_strategy="no", # β Explicitly disable evaluation
|
| 176 |
+
# ... other config
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
trainer = SFTTrainer(
|
| 180 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 181 |
+
train_dataset=dataset,
|
| 182 |
+
# No eval_dataset needed
|
| 183 |
+
args=config,
|
| 184 |
+
)
|
| 185 |
+
```
|
| 186 |
+
|
| 187 |
+
## Best Practices
|
| 188 |
+
|
| 189 |
+
1. **Use train/eval splits** - Create evaluation split for monitoring progress
|
| 190 |
+
2. **Enable Trackio** - Monitor progress in real-time
|
| 191 |
+
3. **Add 20-30% buffer to timeout** - Account for loading/saving overhead
|
| 192 |
+
4. **Test with TRL official scripts first** - Use maintained examples before custom code
|
| 193 |
+
5. **Always provide eval_dataset** - When using eval_strategy, or set to "no"
|
| 194 |
+
6. **Use multi-GPU for large models** - 7B+ models benefit significantly
|
| 195 |
+
|
| 196 |
+
## See Also
|
| 197 |
+
|
| 198 |
+
- `scripts/train_sft_example.py` - Complete SFT template with Trackio and eval split
|
| 199 |
+
- `scripts/train_dpo_example.py` - Complete DPO template
|
| 200 |
+
- `scripts/train_grpo_example.py` - Complete GRPO template
|
| 201 |
+
- `references/hardware_guide.md` - Detailed hardware specifications
|
| 202 |
+
- `references/training_methods.md` - Overview of all TRL training methods
|
| 203 |
+
- `references/troubleshooting.md` - Common issues and solutions
|
.agents/skills/huggingface-llm-trainer/references/troubleshooting.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Troubleshooting TRL Training Jobs
|
| 2 |
+
|
| 3 |
+
Common issues and solutions when training with TRL on Hugging Face Jobs.
|
| 4 |
+
|
| 5 |
+
## Training Hangs at "Starting training..." Step
|
| 6 |
+
|
| 7 |
+
**Problem:** Job starts but hangs at the training step - never progresses, never times out, just sits there.
|
| 8 |
+
|
| 9 |
+
**Root Cause:** Using `eval_strategy="steps"` or `eval_strategy="epoch"` without providing an `eval_dataset` to the trainer.
|
| 10 |
+
|
| 11 |
+
**Solution:**
|
| 12 |
+
|
| 13 |
+
**Option A: Provide eval_dataset (recommended)**
|
| 14 |
+
```python
|
| 15 |
+
# Create train/eval split
|
| 16 |
+
dataset_split = dataset.train_test_split(test_size=0.1, seed=42)
|
| 17 |
+
|
| 18 |
+
trainer = SFTTrainer(
|
| 19 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 20 |
+
train_dataset=dataset_split["train"],
|
| 21 |
+
eval_dataset=dataset_split["test"], # β MUST provide when eval_strategy is enabled
|
| 22 |
+
args=SFTConfig(
|
| 23 |
+
eval_strategy="steps",
|
| 24 |
+
eval_steps=50,
|
| 25 |
+
...
|
| 26 |
+
),
|
| 27 |
+
)
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
**Option B: Disable evaluation**
|
| 31 |
+
```python
|
| 32 |
+
trainer = SFTTrainer(
|
| 33 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 34 |
+
train_dataset=dataset,
|
| 35 |
+
# No eval_dataset
|
| 36 |
+
args=SFTConfig(
|
| 37 |
+
eval_strategy="no", # β Explicitly disable
|
| 38 |
+
...
|
| 39 |
+
),
|
| 40 |
+
)
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
**Prevention:**
|
| 44 |
+
- Always create train/eval split for better monitoring
|
| 45 |
+
- Use `dataset.train_test_split(test_size=0.1, seed=42)`
|
| 46 |
+
- Check example scripts: `scripts/train_sft_example.py` includes proper eval setup
|
| 47 |
+
|
| 48 |
+
## Job Times Out
|
| 49 |
+
|
| 50 |
+
**Problem:** Job terminates before training completes, all progress lost.
|
| 51 |
+
|
| 52 |
+
**Solutions:**
|
| 53 |
+
- Increase timeout parameter (e.g., `"timeout": "4h"`)
|
| 54 |
+
- Reduce `num_train_epochs` or use smaller dataset slice
|
| 55 |
+
- Use smaller model or enable LoRA/PEFT to speed up training
|
| 56 |
+
- Add 20-30% buffer to estimated time for loading/saving overhead
|
| 57 |
+
|
| 58 |
+
**Prevention:**
|
| 59 |
+
- Always start with a quick demo run to estimate timing
|
| 60 |
+
- Use `scripts/estimate_cost.py` to get time estimates
|
| 61 |
+
- Monitor first runs closely via Trackio or logs
|
| 62 |
+
|
| 63 |
+
## Model Not Saved to Hub
|
| 64 |
+
|
| 65 |
+
**Problem:** Training completes but model doesn't appear on Hub - all work lost.
|
| 66 |
+
|
| 67 |
+
**Check:**
|
| 68 |
+
- [ ] `push_to_hub=True` in training config
|
| 69 |
+
- [ ] `hub_model_id` specified with username (e.g., `"username/model-name"`)
|
| 70 |
+
- [ ] `secrets={"HF_TOKEN": "$HF_TOKEN"}` in job submission
|
| 71 |
+
- [ ] User has write access to target repo
|
| 72 |
+
- [ ] Token has write permissions (check at https://huggingface.co/settings/tokens)
|
| 73 |
+
- [ ] Training script calls `trainer.push_to_hub()` at the end
|
| 74 |
+
|
| 75 |
+
**See:** `references/hub_saving.md` for detailed Hub authentication troubleshooting
|
| 76 |
+
|
| 77 |
+
## Out of Memory (OOM)
|
| 78 |
+
|
| 79 |
+
**Problem:** Job fails with CUDA out of memory error.
|
| 80 |
+
|
| 81 |
+
**Solutions (in order of preference):**
|
| 82 |
+
1. **Reduce batch size:** Lower `per_device_train_batch_size` (try 4 β 2 β 1)
|
| 83 |
+
2. **Increase gradient accumulation:** Raise `gradient_accumulation_steps` to maintain effective batch size
|
| 84 |
+
3. **Disable evaluation:** Remove `eval_dataset` and `eval_strategy` (saves ~40% memory, good for demos)
|
| 85 |
+
4. **Enable LoRA/PEFT:** Use `peft_config=LoraConfig(r=8, lora_alpha=16)` to train adapters only (smaller rank = less memory)
|
| 86 |
+
5. **Use larger GPU:** Switch from `t4-small` β `l4x1` β `a10g-large` β `a100-large`
|
| 87 |
+
6. **Enable gradient checkpointing:** Set `gradient_checkpointing=True` in config (slower but saves memory)
|
| 88 |
+
7. **Use smaller model:** Try a smaller variant (e.g., 0.5B instead of 3B)
|
| 89 |
+
|
| 90 |
+
**Memory guidelines:**
|
| 91 |
+
- T4 (16GB): <1B models with LoRA
|
| 92 |
+
- A10G (24GB): 1-3B models with LoRA, <1B full fine-tune
|
| 93 |
+
- A100 (40GB/80GB): 7B+ models with LoRA, 3B full fine-tune
|
| 94 |
+
|
| 95 |
+
## Parameter Naming Issues
|
| 96 |
+
|
| 97 |
+
**Problem:** `TypeError: SFTConfig.__init__() got an unexpected keyword argument 'max_seq_length'`
|
| 98 |
+
|
| 99 |
+
**Cause:** TRL config classes use `max_length`, not `max_seq_length`.
|
| 100 |
+
|
| 101 |
+
**Solution:**
|
| 102 |
+
```python
|
| 103 |
+
# β
CORRECT - TRL uses max_length
|
| 104 |
+
SFTConfig(max_length=512)
|
| 105 |
+
DPOConfig(max_length=512)
|
| 106 |
+
|
| 107 |
+
# β WRONG - This will fail
|
| 108 |
+
SFTConfig(max_seq_length=512)
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
**Note:** Most TRL configs don't require explicit max_length - the default (1024) works well. Only set if you need a specific value.
|
| 112 |
+
|
| 113 |
+
## Dataset Format Error
|
| 114 |
+
|
| 115 |
+
**Problem:** Training fails with dataset format errors or missing fields.
|
| 116 |
+
|
| 117 |
+
**Solutions:**
|
| 118 |
+
1. **Check format documentation:**
|
| 119 |
+
```python
|
| 120 |
+
hf_doc_fetch("https://huggingface.co/docs/trl/dataset_formats")
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
2. **Validate dataset before training:**
|
| 124 |
+
```bash
|
| 125 |
+
uv run https://huggingface.co/datasets/mcp-tools/skills/raw/main/dataset_inspector.py \
|
| 126 |
+
--dataset <dataset-name> --split train
|
| 127 |
+
```
|
| 128 |
+
Or via hf_jobs:
|
| 129 |
+
```python
|
| 130 |
+
hf_jobs("uv", {
|
| 131 |
+
"script": "https://huggingface.co/datasets/mcp-tools/skills/raw/main/dataset_inspector.py",
|
| 132 |
+
"script_args": ["--dataset", "dataset-name", "--split", "train"]
|
| 133 |
+
})
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
3. **Verify field names:**
|
| 137 |
+
- **SFT:** Needs "messages" field (conversational), OR "text" field, OR "prompt"/"completion"
|
| 138 |
+
- **DPO:** Needs "chosen" and "rejected" fields
|
| 139 |
+
- **GRPO:** Needs prompt-only format
|
| 140 |
+
|
| 141 |
+
4. **Check dataset split:**
|
| 142 |
+
- Ensure split exists (e.g., `split="train"`)
|
| 143 |
+
- Preview dataset: `load_dataset("name", split="train[:5]")`
|
| 144 |
+
|
| 145 |
+
## Import/Module Errors
|
| 146 |
+
|
| 147 |
+
**Problem:** Job fails with "ModuleNotFoundError" or import errors.
|
| 148 |
+
|
| 149 |
+
**Solutions:**
|
| 150 |
+
1. **Add PEP 723 header with dependencies:**
|
| 151 |
+
```python
|
| 152 |
+
# /// script
|
| 153 |
+
# dependencies = [
|
| 154 |
+
# "trl>=0.12.0",
|
| 155 |
+
# "peft>=0.7.0",
|
| 156 |
+
# "transformers>=4.36.0",
|
| 157 |
+
# ]
|
| 158 |
+
# ///
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
2. **Verify exact format:**
|
| 162 |
+
- Must have `# ///` delimiters (with space after `#`)
|
| 163 |
+
- Dependencies must be valid PyPI package names
|
| 164 |
+
- Check spelling and version constraints
|
| 165 |
+
|
| 166 |
+
3. **Test locally first:**
|
| 167 |
+
```bash
|
| 168 |
+
uv run train.py # Tests if dependencies are correct
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
## Authentication Errors
|
| 172 |
+
|
| 173 |
+
**Problem:** Job fails with authentication or permission errors when pushing to Hub.
|
| 174 |
+
|
| 175 |
+
**Solutions:**
|
| 176 |
+
1. **Verify authentication:**
|
| 177 |
+
```python
|
| 178 |
+
mcp__huggingface__hf_whoami() # Check who's authenticated
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
2. **Check token permissions:**
|
| 182 |
+
- Go to https://huggingface.co/settings/tokens
|
| 183 |
+
- Ensure token has "write" permission
|
| 184 |
+
- Token must not be "read-only"
|
| 185 |
+
|
| 186 |
+
3. **Verify token in job:**
|
| 187 |
+
```python
|
| 188 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"} # Must be in job config
|
| 189 |
+
```
|
| 190 |
+
|
| 191 |
+
4. **Check repo permissions:**
|
| 192 |
+
- User must have write access to target repo
|
| 193 |
+
- If org repo, user must be member with write access
|
| 194 |
+
- Repo must exist or user must have permission to create
|
| 195 |
+
|
| 196 |
+
## Job Stuck or Not Starting
|
| 197 |
+
|
| 198 |
+
**Problem:** Job shows "pending" or "starting" for extended period.
|
| 199 |
+
|
| 200 |
+
**Solutions:**
|
| 201 |
+
- Check Jobs dashboard for status: https://huggingface.co/jobs
|
| 202 |
+
- Verify hardware availability (some GPU types may have queues)
|
| 203 |
+
- Try different hardware flavor if one is heavily utilized
|
| 204 |
+
- Check for account billing issues (Jobs requires paid plan)
|
| 205 |
+
|
| 206 |
+
**Typical startup times:**
|
| 207 |
+
- CPU jobs: 10-30 seconds
|
| 208 |
+
- GPU jobs: 30-90 seconds
|
| 209 |
+
- If >3 minutes: likely queued or stuck
|
| 210 |
+
|
| 211 |
+
## Training Loss Not Decreasing
|
| 212 |
+
|
| 213 |
+
**Problem:** Training runs but loss stays flat or doesn't improve.
|
| 214 |
+
|
| 215 |
+
**Solutions:**
|
| 216 |
+
1. **Check learning rate:** May be too low (try 2e-5 to 5e-5) or too high (try 1e-6)
|
| 217 |
+
2. **Verify dataset quality:** Inspect examples to ensure they're reasonable
|
| 218 |
+
3. **Check model size:** Very small models may not have capacity for task
|
| 219 |
+
4. **Increase training steps:** May need more epochs or larger dataset
|
| 220 |
+
5. **Verify dataset format:** Wrong format may cause degraded training
|
| 221 |
+
|
| 222 |
+
## Logs Not Appearing
|
| 223 |
+
|
| 224 |
+
**Problem:** Cannot see training logs or progress.
|
| 225 |
+
|
| 226 |
+
**Solutions:**
|
| 227 |
+
1. **Wait 30-60 seconds:** Initial logs can be delayed
|
| 228 |
+
2. **Check logs via MCP tool:**
|
| 229 |
+
```python
|
| 230 |
+
hf_jobs("logs", {"job_id": "your-job-id"})
|
| 231 |
+
```
|
| 232 |
+
3. **Use Trackio for real-time monitoring:** See `references/trackio_guide.md`
|
| 233 |
+
4. **Verify job is actually running:**
|
| 234 |
+
```python
|
| 235 |
+
hf_jobs("inspect", {"job_id": "your-job-id"})
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
## Checkpoint/Resume Issues
|
| 239 |
+
|
| 240 |
+
**Problem:** Cannot resume from checkpoint or checkpoint not saved.
|
| 241 |
+
|
| 242 |
+
**Solutions:**
|
| 243 |
+
1. **Enable checkpoint saving:**
|
| 244 |
+
```python
|
| 245 |
+
SFTConfig(
|
| 246 |
+
save_strategy="steps",
|
| 247 |
+
save_steps=100,
|
| 248 |
+
hub_strategy="every_save", # Push each checkpoint
|
| 249 |
+
)
|
| 250 |
+
```
|
| 251 |
+
|
| 252 |
+
2. **Verify checkpoints pushed to Hub:** Check model repo for checkpoint folders
|
| 253 |
+
|
| 254 |
+
3. **Resume from checkpoint:**
|
| 255 |
+
```python
|
| 256 |
+
trainer = SFTTrainer(
|
| 257 |
+
model="username/model-name", # Can be checkpoint path
|
| 258 |
+
resume_from_checkpoint="username/model-name/checkpoint-1000",
|
| 259 |
+
)
|
| 260 |
+
```
|
| 261 |
+
|
| 262 |
+
## Getting Help
|
| 263 |
+
|
| 264 |
+
If issues persist:
|
| 265 |
+
|
| 266 |
+
1. **Check TRL documentation:**
|
| 267 |
+
```python
|
| 268 |
+
hf_doc_search("your issue", product="trl")
|
| 269 |
+
```
|
| 270 |
+
|
| 271 |
+
2. **Check Jobs documentation:**
|
| 272 |
+
```python
|
| 273 |
+
hf_doc_fetch("https://huggingface.co/docs/huggingface_hub/guides/jobs")
|
| 274 |
+
```
|
| 275 |
+
|
| 276 |
+
3. **Review related guides:**
|
| 277 |
+
- `references/hub_saving.md` - Hub authentication issues
|
| 278 |
+
- `references/hardware_guide.md` - Hardware selection and specs
|
| 279 |
+
- `references/training_patterns.md` - Eval dataset requirements
|
| 280 |
+
- SKILL.md "Working with Scripts" section - Script format and URL issues
|
| 281 |
+
|
| 282 |
+
4. **Ask in HF forums:** https://discuss.huggingface.co/
|
.agents/skills/huggingface-llm-trainer/references/unsloth.md
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Unsloth: Fast Fine-Tuning with Memory Optimization
|
| 2 |
+
|
| 3 |
+
**Unsloth** is a fine-tuning library that provides ~2x faster training and ~60% less VRAM usage for LLM training. It's particularly useful when working with limited GPU memory or when speed is critical.
|
| 4 |
+
|
| 5 |
+
- **GitHub**: [unslothai/unsloth](https://github.com/unslothai/unsloth)
|
| 6 |
+
- **Docs**: [unsloth.ai/docs](https://unsloth.ai/docs)
|
| 7 |
+
|
| 8 |
+
## When to Use Unsloth
|
| 9 |
+
|
| 10 |
+
Use Unsloth if instructed to do so, or one of the following use cases applies:
|
| 11 |
+
|
| 12 |
+
| Use Case | Recommendation |
|
| 13 |
+
|----------|----------------|
|
| 14 |
+
| Standard text LLM fine-tuning | TRL is sufficient, but Unsloth is faster |
|
| 15 |
+
| Limited GPU memory | **Use Unsloth** - 60% less VRAM |
|
| 16 |
+
| Need maximum speed | **Use Unsloth** - 2x faster |
|
| 17 |
+
| Large models (>13B) | **Use Unsloth** - memory efficiency critical |
|
| 18 |
+
|
| 19 |
+
## Supported Models
|
| 20 |
+
|
| 21 |
+
Unsloth supports many popular models including:
|
| 22 |
+
- **Text LLMs**: Llama 3/3.1/3.2/3.3, Qwen 2.5/3, Mistral, Phi-4, Gemma 2/3, LFM2/2.5
|
| 23 |
+
- **Vision LLMs**: Qwen3-VL, Gemma 3, Llama 3.2 Vision, Pixtral
|
| 24 |
+
|
| 25 |
+
Use Unsloth's pre-optimized model variants when available:
|
| 26 |
+
```python
|
| 27 |
+
# Unsloth-optimized models load faster and use less memory
|
| 28 |
+
model_id = "unsloth/LFM2.5-1.2B-Instruct" # 4-bit quantized
|
| 29 |
+
model_id = "unsloth/gemma-3-4b-pt" # Vision model
|
| 30 |
+
model_id = "unsloth/Qwen3-VL-8B-Instruct" # Vision model
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
## Installation
|
| 34 |
+
|
| 35 |
+
```python
|
| 36 |
+
# /// script
|
| 37 |
+
# dependencies = [
|
| 38 |
+
# "unsloth",
|
| 39 |
+
# "trl",
|
| 40 |
+
# "datasets",
|
| 41 |
+
# "trackio",
|
| 42 |
+
# ]
|
| 43 |
+
# ///
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
## Basic Usage: Text LLM
|
| 47 |
+
|
| 48 |
+
```python
|
| 49 |
+
from unsloth import FastLanguageModel
|
| 50 |
+
from trl import SFTTrainer, SFTConfig
|
| 51 |
+
from datasets import load_dataset
|
| 52 |
+
|
| 53 |
+
# Load model with Unsloth optimizations
|
| 54 |
+
model, tokenizer = FastLanguageModel.from_pretrained(
|
| 55 |
+
model_name="LiquidAI/LFM2.5-1.2B-Instruct",
|
| 56 |
+
max_seq_length=4096,
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
# Add LoRA adapters
|
| 60 |
+
model = FastLanguageModel.get_peft_model(
|
| 61 |
+
model,
|
| 62 |
+
r=16,
|
| 63 |
+
lora_alpha=16,
|
| 64 |
+
target_modules=["q_proj", "k_proj", "v_proj", "out_proj", "in_proj", "w1", "w2", "w3"],
|
| 65 |
+
lora_dropout=0,
|
| 66 |
+
bias="none",
|
| 67 |
+
use_gradient_checkpointing="unsloth",
|
| 68 |
+
random_state=3407,
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
# Load dataset
|
| 72 |
+
dataset = load_dataset("trl-lib/Capybara", split="train")
|
| 73 |
+
|
| 74 |
+
# Train with TRL
|
| 75 |
+
trainer = SFTTrainer(
|
| 76 |
+
model=model,
|
| 77 |
+
tokenizer=tokenizer,
|
| 78 |
+
train_dataset=dataset,
|
| 79 |
+
args=SFTConfig(
|
| 80 |
+
output_dir="./output",
|
| 81 |
+
per_device_train_batch_size=2,
|
| 82 |
+
gradient_accumulation_steps=4,
|
| 83 |
+
max_steps=500,
|
| 84 |
+
learning_rate=2e-4,
|
| 85 |
+
report_to="trackio",
|
| 86 |
+
),
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
trainer.train()
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
## LFM2.5 Specific Settings
|
| 93 |
+
|
| 94 |
+
For LFM2.5 inference, use these recommended generation parameters:
|
| 95 |
+
|
| 96 |
+
**Instruct models:**
|
| 97 |
+
```python
|
| 98 |
+
temperature = 0.1
|
| 99 |
+
top_k = 50
|
| 100 |
+
top_p = 0.1
|
| 101 |
+
repetition_penalty = 1.05
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
**Thinking models:**
|
| 105 |
+
```python
|
| 106 |
+
temperature = 0.05
|
| 107 |
+
top_k = 50
|
| 108 |
+
repetition_penalty = 1.05
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
## Vision-Language Models (VLMs)
|
| 112 |
+
|
| 113 |
+
Unsloth provides specialized support for VLMs with `FastVisionModel`:
|
| 114 |
+
|
| 115 |
+
```python
|
| 116 |
+
from unsloth import FastVisionModel, get_chat_template
|
| 117 |
+
from unsloth.trainer import UnslothVisionDataCollator
|
| 118 |
+
from trl import SFTTrainer, SFTConfig
|
| 119 |
+
from datasets import load_dataset
|
| 120 |
+
|
| 121 |
+
# Load VLM with Unsloth
|
| 122 |
+
model, processor = FastVisionModel.from_pretrained(
|
| 123 |
+
"unsloth/gemma-3-4b-pt", # or "unsloth/Qwen3-VL-8B-Instruct"
|
| 124 |
+
load_in_4bit=True,
|
| 125 |
+
use_gradient_checkpointing="unsloth",
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
# Add LoRA for all modalities
|
| 129 |
+
model = FastVisionModel.get_peft_model(
|
| 130 |
+
model,
|
| 131 |
+
finetune_vision_layers=True, # Train vision encoder
|
| 132 |
+
finetune_language_layers=True, # Train language model
|
| 133 |
+
finetune_attention_modules=True, # Train attention
|
| 134 |
+
finetune_mlp_modules=True, # Train MLPs
|
| 135 |
+
r=16,
|
| 136 |
+
lora_alpha=32,
|
| 137 |
+
target_modules="all-linear",
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
# Apply chat template (required for base models)
|
| 141 |
+
processor = get_chat_template(processor, "gemma-3")
|
| 142 |
+
|
| 143 |
+
# Load VLM dataset (with images and messages)
|
| 144 |
+
dataset = load_dataset("your-vlm-dataset", split="train", streaming=True)
|
| 145 |
+
|
| 146 |
+
# Enable training mode
|
| 147 |
+
FastVisionModel.for_training(model)
|
| 148 |
+
|
| 149 |
+
# Train with VLM-specific collator
|
| 150 |
+
trainer = SFTTrainer(
|
| 151 |
+
model=model,
|
| 152 |
+
train_dataset=dataset,
|
| 153 |
+
processing_class=processor.tokenizer,
|
| 154 |
+
data_collator=UnslothVisionDataCollator(model, processor),
|
| 155 |
+
args=SFTConfig(
|
| 156 |
+
output_dir="./vlm-output",
|
| 157 |
+
per_device_train_batch_size=2,
|
| 158 |
+
gradient_accumulation_steps=4,
|
| 159 |
+
max_steps=500,
|
| 160 |
+
learning_rate=2e-4,
|
| 161 |
+
# VLM-specific settings
|
| 162 |
+
remove_unused_columns=False,
|
| 163 |
+
dataset_text_field="",
|
| 164 |
+
dataset_kwargs={"skip_prepare_dataset": True},
|
| 165 |
+
report_to="trackio",
|
| 166 |
+
),
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
trainer.train()
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
## Key Differences from Standard TRL
|
| 173 |
+
|
| 174 |
+
| Aspect | Standard TRL | Unsloth |
|
| 175 |
+
|--------|--------------|---------|
|
| 176 |
+
| Model loading | `AutoModelForCausalLM.from_pretrained()` | `FastLanguageModel.from_pretrained()` |
|
| 177 |
+
| LoRA setup | `PeftModel` / `LoraConfig` | `FastLanguageModel.get_peft_model()` |
|
| 178 |
+
| VLM loading | Limited support | `FastVisionModel.from_pretrained()` |
|
| 179 |
+
| VLM collator | Manual | `UnslothVisionDataCollator` |
|
| 180 |
+
| Memory usage | Standard | ~60% less |
|
| 181 |
+
| Training speed | Standard | ~2x faster |
|
| 182 |
+
|
| 183 |
+
## VLM Dataset Format
|
| 184 |
+
|
| 185 |
+
VLM datasets should have:
|
| 186 |
+
- `images`: List of PIL images or image paths
|
| 187 |
+
- `messages`: Conversation format with image references
|
| 188 |
+
|
| 189 |
+
```python
|
| 190 |
+
{
|
| 191 |
+
"images": [<PIL.Image>, ...],
|
| 192 |
+
"messages": [
|
| 193 |
+
{"role": "user", "content": [
|
| 194 |
+
{"type": "image"},
|
| 195 |
+
{"type": "text", "text": "Describe this image"}
|
| 196 |
+
]},
|
| 197 |
+
{"role": "assistant", "content": "This image shows..."}
|
| 198 |
+
]
|
| 199 |
+
}
|
| 200 |
+
```
|
| 201 |
+
|
| 202 |
+
## Streaming Datasets
|
| 203 |
+
|
| 204 |
+
For large VLM datasets, use streaming to avoid disk space issues:
|
| 205 |
+
|
| 206 |
+
```python
|
| 207 |
+
dataset = load_dataset(
|
| 208 |
+
"your-vlm-dataset",
|
| 209 |
+
split="train",
|
| 210 |
+
streaming=True, # Stream from Hub
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
# Must use max_steps with streaming (no epoch-based training)
|
| 214 |
+
SFTConfig(max_steps=500, ...)
|
| 215 |
+
```
|
| 216 |
+
|
| 217 |
+
## Saving Models
|
| 218 |
+
|
| 219 |
+
### Save LoRA Adapter
|
| 220 |
+
|
| 221 |
+
```python
|
| 222 |
+
model.save_pretrained("./adapter")
|
| 223 |
+
processor.save_pretrained("./adapter")
|
| 224 |
+
|
| 225 |
+
# Push to Hub
|
| 226 |
+
model.push_to_hub("username/my-vlm-adapter")
|
| 227 |
+
processor.push_to_hub("username/my-vlm-adapter")
|
| 228 |
+
```
|
| 229 |
+
|
| 230 |
+
### Merge and Save Full Model
|
| 231 |
+
|
| 232 |
+
```python
|
| 233 |
+
# Merge LoRA weights into base model
|
| 234 |
+
model = model.merge_and_unload()
|
| 235 |
+
|
| 236 |
+
# Save merged model
|
| 237 |
+
model.save_pretrained("./merged")
|
| 238 |
+
tokenizer.save_pretrained("./merged")
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
### Convert to GGUF
|
| 242 |
+
|
| 243 |
+
Unsloth models can be converted to GGUF for llama.cpp/Ollama:
|
| 244 |
+
|
| 245 |
+
```python
|
| 246 |
+
# Save in 16-bit for GGUF conversion
|
| 247 |
+
model.save_pretrained_gguf("./gguf", tokenizer, quantization_method="f16")
|
| 248 |
+
|
| 249 |
+
# Or directly quantize
|
| 250 |
+
model.save_pretrained_gguf("./gguf", tokenizer, quantization_method="q4_k_m")
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
## Qwen3-VL Specific Settings
|
| 254 |
+
|
| 255 |
+
For Qwen3-VL models, use these recommended settings:
|
| 256 |
+
|
| 257 |
+
**Instruct models:**
|
| 258 |
+
```python
|
| 259 |
+
temperature = 0.7
|
| 260 |
+
top_p = 0.8
|
| 261 |
+
presence_penalty = 1.5
|
| 262 |
+
```
|
| 263 |
+
|
| 264 |
+
**Thinking models:**
|
| 265 |
+
```python
|
| 266 |
+
temperature = 1.0
|
| 267 |
+
top_p = 0.95
|
| 268 |
+
presence_penalty = 0.0
|
| 269 |
+
```
|
| 270 |
+
|
| 271 |
+
## Hardware Requirements
|
| 272 |
+
|
| 273 |
+
| Model | Min VRAM (Unsloth 4-bit) | Recommended GPU |
|
| 274 |
+
|-------|--------------------------|-----------------|
|
| 275 |
+
| 2B-4B | 8GB | T4, L4 |
|
| 276 |
+
| 7B-8B | 16GB | A10G, L4x4 |
|
| 277 |
+
| 13B | 24GB | A10G-large |
|
| 278 |
+
| 30B+ | 48GB+ | A100 |
|
| 279 |
+
|
| 280 |
+
## Example: Full VLM Training Script
|
| 281 |
+
|
| 282 |
+
See `scripts/unsloth_sft_example.py` for a complete production-ready example that includes:
|
| 283 |
+
- Unsloth VLM setup
|
| 284 |
+
- Streaming dataset support
|
| 285 |
+
- Trackio monitoring
|
| 286 |
+
- Hub push
|
| 287 |
+
- CLI arguments
|
| 288 |
+
|
| 289 |
+
Run locally:
|
| 290 |
+
```bash
|
| 291 |
+
uv run scripts/unsloth_sft_example.py \
|
| 292 |
+
--dataset trl-lib/Capybara \
|
| 293 |
+
--max-steps 500 \
|
| 294 |
+
--output-repo username/my-model
|
| 295 |
+
```
|
| 296 |
+
|
| 297 |
+
Run on HF Jobs:
|
| 298 |
+
```python
|
| 299 |
+
hf_jobs("uv", {
|
| 300 |
+
"script": "<script content>",
|
| 301 |
+
"flavor": "a10g-large",
|
| 302 |
+
"timeout": "2h",
|
| 303 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"}
|
| 304 |
+
})
|
| 305 |
+
```
|
| 306 |
+
|
| 307 |
+
## See Also
|
| 308 |
+
|
| 309 |
+
- `scripts/unsloth_sft_example.py` - Complete text LLM training example
|
| 310 |
+
- [Unsloth Documentation](https://unsloth.ai/docs)
|
| 311 |
+
- [LFM2.5 Guide](https://unsloth.ai/docs/models/tutorials/lfm2.5)
|
| 312 |
+
- [Qwen3-VL Guide](https://unsloth.ai/docs/models/qwen3-vl-how-to-run-and-fine-tune)
|
| 313 |
+
- [Unsloth GitHub](https://github.com/unslothai/unsloth)
|
.agents/skills/huggingface-llm-trainer/scripts/convert_to_gguf.py
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# /// script
|
| 3 |
+
# requires-python = ">=3.10"
|
| 4 |
+
# dependencies = [
|
| 5 |
+
# "transformers>=4.36.0",
|
| 6 |
+
# "peft>=0.7.0",
|
| 7 |
+
# "torch>=2.0.0",
|
| 8 |
+
# "accelerate>=0.24.0",
|
| 9 |
+
# "huggingface_hub>=0.20.0",
|
| 10 |
+
# "sentencepiece>=0.1.99",
|
| 11 |
+
# "protobuf>=3.20.0",
|
| 12 |
+
# "numpy",
|
| 13 |
+
# "gguf",
|
| 14 |
+
# ]
|
| 15 |
+
# ///
|
| 16 |
+
|
| 17 |
+
"""
|
| 18 |
+
GGUF Conversion Script - Production Ready
|
| 19 |
+
|
| 20 |
+
This script converts a LoRA fine-tuned model to GGUF format for use with:
|
| 21 |
+
- llama.cpp
|
| 22 |
+
- Ollama
|
| 23 |
+
- LM Studio
|
| 24 |
+
- Other GGUF-compatible tools
|
| 25 |
+
|
| 26 |
+
PREREQUISITES (install these FIRST):
|
| 27 |
+
- Ubuntu/Debian: sudo apt-get update && sudo apt-get install -y build-essential cmake
|
| 28 |
+
- RHEL/CentOS: sudo yum groupinstall -y "Development Tools" && sudo yum install -y cmake
|
| 29 |
+
- macOS: xcode-select --install && brew install cmake
|
| 30 |
+
|
| 31 |
+
Usage:
|
| 32 |
+
Set environment variables:
|
| 33 |
+
- ADAPTER_MODEL: Your fine-tuned model (e.g., "username/my-finetuned-model")
|
| 34 |
+
- BASE_MODEL: Base model used for fine-tuning (e.g., "Qwen/Qwen2.5-0.5B")
|
| 35 |
+
- OUTPUT_REPO: Where to upload GGUF files (e.g., "username/my-model-gguf")
|
| 36 |
+
- HF_USERNAME: Your Hugging Face username (optional, for README)
|
| 37 |
+
|
| 38 |
+
Dependencies: All required packages are declared in PEP 723 header above.
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
import os
|
| 42 |
+
import sys
|
| 43 |
+
import torch
|
| 44 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 45 |
+
from peft import PeftModel
|
| 46 |
+
from huggingface_hub import HfApi
|
| 47 |
+
import subprocess
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def check_system_dependencies():
|
| 51 |
+
"""Check if required system packages are available."""
|
| 52 |
+
print("π Checking system dependencies...")
|
| 53 |
+
|
| 54 |
+
# Check for git
|
| 55 |
+
if subprocess.run(["which", "git"], capture_output=True).returncode != 0:
|
| 56 |
+
print(" β git is not installed. Please install it:")
|
| 57 |
+
print(" Ubuntu/Debian: sudo apt-get install git")
|
| 58 |
+
print(" RHEL/CentOS: sudo yum install git")
|
| 59 |
+
print(" macOS: brew install git")
|
| 60 |
+
return False
|
| 61 |
+
|
| 62 |
+
# Check for make or cmake
|
| 63 |
+
has_make = subprocess.run(["which", "make"], capture_output=True).returncode == 0
|
| 64 |
+
has_cmake = subprocess.run(["which", "cmake"], capture_output=True).returncode == 0
|
| 65 |
+
|
| 66 |
+
if not has_make and not has_cmake:
|
| 67 |
+
print(" β Neither make nor cmake found. Please install build tools:")
|
| 68 |
+
print(" Ubuntu/Debian: sudo apt-get install build-essential cmake")
|
| 69 |
+
print(" RHEL/CentOS: sudo yum groupinstall 'Development Tools' && sudo yum install cmake")
|
| 70 |
+
print(" macOS: xcode-select --install && brew install cmake")
|
| 71 |
+
return False
|
| 72 |
+
|
| 73 |
+
print(" β
System dependencies found")
|
| 74 |
+
return True
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def run_command(cmd, description):
|
| 78 |
+
"""Run a command with error handling."""
|
| 79 |
+
print(f" {description}...")
|
| 80 |
+
try:
|
| 81 |
+
result = subprocess.run(
|
| 82 |
+
cmd,
|
| 83 |
+
check=True,
|
| 84 |
+
capture_output=True,
|
| 85 |
+
text=True
|
| 86 |
+
)
|
| 87 |
+
if result.stdout:
|
| 88 |
+
print(f" {result.stdout[:200]}") # Show first 200 chars
|
| 89 |
+
return True
|
| 90 |
+
except subprocess.CalledProcessError as e:
|
| 91 |
+
print(f" β Command failed: {' '.join(cmd)}")
|
| 92 |
+
if e.stdout:
|
| 93 |
+
print(f" STDOUT: {e.stdout[:500]}")
|
| 94 |
+
if e.stderr:
|
| 95 |
+
print(f" STDERR: {e.stderr[:500]}")
|
| 96 |
+
return False
|
| 97 |
+
except FileNotFoundError:
|
| 98 |
+
print(f" β Command not found: {cmd[0]}")
|
| 99 |
+
return False
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
print("π GGUF Conversion Script")
|
| 103 |
+
print("=" * 60)
|
| 104 |
+
|
| 105 |
+
# Check system dependencies first
|
| 106 |
+
if not check_system_dependencies():
|
| 107 |
+
print("\nβ Please install the missing system dependencies and try again.")
|
| 108 |
+
sys.exit(1)
|
| 109 |
+
|
| 110 |
+
# Configuration from environment variables
|
| 111 |
+
ADAPTER_MODEL = os.environ.get("ADAPTER_MODEL", "evalstate/qwen-capybara-medium")
|
| 112 |
+
BASE_MODEL = os.environ.get("BASE_MODEL", "Qwen/Qwen2.5-0.5B")
|
| 113 |
+
OUTPUT_REPO = os.environ.get("OUTPUT_REPO", "evalstate/qwen-capybara-medium-gguf")
|
| 114 |
+
username = os.environ.get("HF_USERNAME", ADAPTER_MODEL.split('/')[0])
|
| 115 |
+
|
| 116 |
+
print(f"\nπ¦ Configuration:")
|
| 117 |
+
print(f" Base model: {BASE_MODEL}")
|
| 118 |
+
print(f" Adapter model: {ADAPTER_MODEL}")
|
| 119 |
+
print(f" Output repo: {OUTPUT_REPO}")
|
| 120 |
+
|
| 121 |
+
# Step 1: Load base model and adapter
|
| 122 |
+
print("\nπ§ Step 1: Loading base model and LoRA adapter...")
|
| 123 |
+
print(" (This may take a few minutes)")
|
| 124 |
+
|
| 125 |
+
try:
|
| 126 |
+
base_model = AutoModelForCausalLM.from_pretrained(
|
| 127 |
+
BASE_MODEL,
|
| 128 |
+
dtype=torch.float16,
|
| 129 |
+
device_map="auto",
|
| 130 |
+
trust_remote_code=True,
|
| 131 |
+
)
|
| 132 |
+
print(" β
Base model loaded")
|
| 133 |
+
except Exception as e:
|
| 134 |
+
print(f" β Failed to load base model: {e}")
|
| 135 |
+
sys.exit(1)
|
| 136 |
+
|
| 137 |
+
try:
|
| 138 |
+
# Load and merge adapter
|
| 139 |
+
print(" Loading LoRA adapter...")
|
| 140 |
+
model = PeftModel.from_pretrained(base_model, ADAPTER_MODEL)
|
| 141 |
+
print(" β
Adapter loaded")
|
| 142 |
+
|
| 143 |
+
print(" Merging adapter with base model...")
|
| 144 |
+
merged_model = model.merge_and_unload()
|
| 145 |
+
print(" β
Models merged!")
|
| 146 |
+
except Exception as e:
|
| 147 |
+
print(f" β Failed to merge models: {e}")
|
| 148 |
+
sys.exit(1)
|
| 149 |
+
|
| 150 |
+
try:
|
| 151 |
+
# Load tokenizer
|
| 152 |
+
tokenizer = AutoTokenizer.from_pretrained(ADAPTER_MODEL, trust_remote_code=True)
|
| 153 |
+
print(" β
Tokenizer loaded")
|
| 154 |
+
except Exception as e:
|
| 155 |
+
print(f" β Failed to load tokenizer: {e}")
|
| 156 |
+
sys.exit(1)
|
| 157 |
+
|
| 158 |
+
# Step 2: Save merged model temporarily
|
| 159 |
+
print("\nπΎ Step 2: Saving merged model...")
|
| 160 |
+
merged_dir = "/tmp/merged_model"
|
| 161 |
+
try:
|
| 162 |
+
merged_model.save_pretrained(merged_dir, safe_serialization=True)
|
| 163 |
+
tokenizer.save_pretrained(merged_dir)
|
| 164 |
+
print(f" β
Merged model saved to {merged_dir}")
|
| 165 |
+
except Exception as e:
|
| 166 |
+
print(f" β Failed to save merged model: {e}")
|
| 167 |
+
sys.exit(1)
|
| 168 |
+
|
| 169 |
+
# Step 3: Install llama.cpp for conversion
|
| 170 |
+
print("\nπ₯ Step 3: Setting up llama.cpp for GGUF conversion...")
|
| 171 |
+
|
| 172 |
+
# Clone llama.cpp repository
|
| 173 |
+
if not run_command(
|
| 174 |
+
["git", "clone", "https://github.com/ggerganov/llama.cpp.git", "/tmp/llama.cpp"],
|
| 175 |
+
"Cloning llama.cpp repository"
|
| 176 |
+
):
|
| 177 |
+
print(" Trying alternative clone method...")
|
| 178 |
+
# Try shallow clone
|
| 179 |
+
if not run_command(
|
| 180 |
+
["git", "clone", "--depth", "1", "https://github.com/ggerganov/llama.cpp.git", "/tmp/llama.cpp"],
|
| 181 |
+
"Cloning llama.cpp (shallow)"
|
| 182 |
+
):
|
| 183 |
+
sys.exit(1)
|
| 184 |
+
|
| 185 |
+
# Install Python dependencies
|
| 186 |
+
print(" Installing Python dependencies...")
|
| 187 |
+
if not run_command(
|
| 188 |
+
["pip", "install", "-r", "/tmp/llama.cpp/requirements.txt"],
|
| 189 |
+
"Installing llama.cpp requirements"
|
| 190 |
+
):
|
| 191 |
+
print(" β οΈ Some requirements may already be installed")
|
| 192 |
+
|
| 193 |
+
if not run_command(
|
| 194 |
+
["pip", "install", "sentencepiece", "protobuf"],
|
| 195 |
+
"Installing tokenizer dependencies"
|
| 196 |
+
):
|
| 197 |
+
print(" β οΈ Tokenizer dependencies may already be installed")
|
| 198 |
+
|
| 199 |
+
# Step 4: Convert to GGUF (FP16)
|
| 200 |
+
print("\nπ Step 4: Converting to GGUF format (FP16)...")
|
| 201 |
+
gguf_output_dir = "/tmp/gguf_output"
|
| 202 |
+
os.makedirs(gguf_output_dir, exist_ok=True)
|
| 203 |
+
|
| 204 |
+
convert_script = "/tmp/llama.cpp/convert_hf_to_gguf.py"
|
| 205 |
+
model_name = ADAPTER_MODEL.split('/')[-1]
|
| 206 |
+
gguf_file = f"{gguf_output_dir}/{model_name}-f16.gguf"
|
| 207 |
+
|
| 208 |
+
print(f" Running conversion...")
|
| 209 |
+
if not run_command(
|
| 210 |
+
[
|
| 211 |
+
sys.executable, convert_script,
|
| 212 |
+
merged_dir,
|
| 213 |
+
"--outfile", gguf_file,
|
| 214 |
+
"--outtype", "f16"
|
| 215 |
+
],
|
| 216 |
+
f"Converting to FP16"
|
| 217 |
+
):
|
| 218 |
+
print(" β Conversion failed!")
|
| 219 |
+
sys.exit(1)
|
| 220 |
+
|
| 221 |
+
print(f" β
FP16 GGUF created: {gguf_file}")
|
| 222 |
+
|
| 223 |
+
# Step 5: Quantize to different formats
|
| 224 |
+
print("\nβοΈ Step 5: Creating quantized versions...")
|
| 225 |
+
|
| 226 |
+
# Build quantize tool using CMake (more reliable than make)
|
| 227 |
+
print(" Building quantize tool with CMake...")
|
| 228 |
+
os.makedirs("/tmp/llama.cpp/build", exist_ok=True)
|
| 229 |
+
|
| 230 |
+
# Configure with CMake
|
| 231 |
+
if not run_command(
|
| 232 |
+
["cmake", "-B", "/tmp/llama.cpp/build", "-S", "/tmp/llama.cpp",
|
| 233 |
+
"-DGGML_CUDA=OFF"],
|
| 234 |
+
"Configuring with CMake"
|
| 235 |
+
):
|
| 236 |
+
print(" β CMake configuration failed")
|
| 237 |
+
sys.exit(1)
|
| 238 |
+
|
| 239 |
+
# Build just the quantize tool
|
| 240 |
+
if not run_command(
|
| 241 |
+
["cmake", "--build", "/tmp/llama.cpp/build", "--target", "llama-quantize", "-j", "4"],
|
| 242 |
+
"Building llama-quantize"
|
| 243 |
+
):
|
| 244 |
+
print(" β Build failed!")
|
| 245 |
+
sys.exit(1)
|
| 246 |
+
|
| 247 |
+
print(" β
Quantize tool built")
|
| 248 |
+
|
| 249 |
+
# Use the CMake build output path
|
| 250 |
+
quantize_bin = "/tmp/llama.cpp/build/bin/llama-quantize"
|
| 251 |
+
|
| 252 |
+
# Common quantization formats
|
| 253 |
+
quant_formats = [
|
| 254 |
+
("Q4_K_M", "4-bit, medium quality (recommended)"),
|
| 255 |
+
("Q5_K_M", "5-bit, higher quality"),
|
| 256 |
+
("Q8_0", "8-bit, very high quality"),
|
| 257 |
+
]
|
| 258 |
+
|
| 259 |
+
quantized_files = []
|
| 260 |
+
for quant_type, description in quant_formats:
|
| 261 |
+
print(f" Creating {quant_type} quantization ({description})...")
|
| 262 |
+
quant_file = f"{gguf_output_dir}/{model_name}-{quant_type.lower()}.gguf"
|
| 263 |
+
|
| 264 |
+
if not run_command(
|
| 265 |
+
[quantize_bin, gguf_file, quant_file, quant_type],
|
| 266 |
+
f"Quantizing to {quant_type}"
|
| 267 |
+
):
|
| 268 |
+
print(f" β οΈ Skipping {quant_type} due to error")
|
| 269 |
+
continue
|
| 270 |
+
|
| 271 |
+
quantized_files.append((quant_file, quant_type))
|
| 272 |
+
|
| 273 |
+
# Get file size
|
| 274 |
+
size_mb = os.path.getsize(quant_file) / (1024 * 1024)
|
| 275 |
+
print(f" β
{quant_type}: {size_mb:.1f} MB")
|
| 276 |
+
|
| 277 |
+
if not quantized_files:
|
| 278 |
+
print(" β No quantized versions were created successfully")
|
| 279 |
+
sys.exit(1)
|
| 280 |
+
|
| 281 |
+
# Step 6: Upload to Hub
|
| 282 |
+
print("\nβοΈ Step 6: Uploading to Hugging Face Hub...")
|
| 283 |
+
api = HfApi()
|
| 284 |
+
|
| 285 |
+
# Create repo
|
| 286 |
+
print(f" Creating repository: {OUTPUT_REPO}")
|
| 287 |
+
try:
|
| 288 |
+
api.create_repo(repo_id=OUTPUT_REPO, repo_type="model", exist_ok=True)
|
| 289 |
+
print(" β
Repository ready")
|
| 290 |
+
except Exception as e:
|
| 291 |
+
print(f" βΉοΈ Repository may already exist: {e}")
|
| 292 |
+
|
| 293 |
+
# Upload FP16 version
|
| 294 |
+
print(" Uploading FP16 GGUF...")
|
| 295 |
+
try:
|
| 296 |
+
api.upload_file(
|
| 297 |
+
path_or_fileobj=gguf_file,
|
| 298 |
+
path_in_repo=f"{model_name}-f16.gguf",
|
| 299 |
+
repo_id=OUTPUT_REPO,
|
| 300 |
+
)
|
| 301 |
+
print(" β
FP16 uploaded")
|
| 302 |
+
except Exception as e:
|
| 303 |
+
print(f" β Upload failed: {e}")
|
| 304 |
+
sys.exit(1)
|
| 305 |
+
|
| 306 |
+
# Upload quantized versions
|
| 307 |
+
for quant_file, quant_type in quantized_files:
|
| 308 |
+
print(f" Uploading {quant_type}...")
|
| 309 |
+
try:
|
| 310 |
+
api.upload_file(
|
| 311 |
+
path_or_fileobj=quant_file,
|
| 312 |
+
path_in_repo=f"{model_name}-{quant_type.lower()}.gguf",
|
| 313 |
+
repo_id=OUTPUT_REPO,
|
| 314 |
+
)
|
| 315 |
+
print(f" οΏ½οΏ½ {quant_type} uploaded")
|
| 316 |
+
except Exception as e:
|
| 317 |
+
print(f" β Upload failed for {quant_type}: {e}")
|
| 318 |
+
continue
|
| 319 |
+
|
| 320 |
+
# Create README
|
| 321 |
+
print("\nπ Creating README...")
|
| 322 |
+
readme_content = f"""---
|
| 323 |
+
base_model: {BASE_MODEL}
|
| 324 |
+
tags:
|
| 325 |
+
- gguf
|
| 326 |
+
- llama.cpp
|
| 327 |
+
- quantized
|
| 328 |
+
- trl
|
| 329 |
+
- sft
|
| 330 |
+
---
|
| 331 |
+
|
| 332 |
+
# {OUTPUT_REPO.split('/')[-1]}
|
| 333 |
+
|
| 334 |
+
This is a GGUF conversion of [{ADAPTER_MODEL}](https://huggingface.co/{ADAPTER_MODEL}), which is a LoRA fine-tuned version of [{BASE_MODEL}](https://huggingface.co/{BASE_MODEL}).
|
| 335 |
+
|
| 336 |
+
## Model Details
|
| 337 |
+
|
| 338 |
+
- **Base Model:** {BASE_MODEL}
|
| 339 |
+
- **Fine-tuned Model:** {ADAPTER_MODEL}
|
| 340 |
+
- **Training:** Supervised Fine-Tuning (SFT) with TRL
|
| 341 |
+
- **Format:** GGUF (for llama.cpp, Ollama, LM Studio, etc.)
|
| 342 |
+
|
| 343 |
+
## Available Quantizations
|
| 344 |
+
|
| 345 |
+
| File | Quant | Size | Description | Use Case |
|
| 346 |
+
|------|-------|------|-------------|----------|
|
| 347 |
+
| {model_name}-f16.gguf | F16 | ~1GB | Full precision | Best quality, slower |
|
| 348 |
+
| {model_name}-q8_0.gguf | Q8_0 | ~500MB | 8-bit | High quality |
|
| 349 |
+
| {model_name}-q5_k_m.gguf | Q5_K_M | ~350MB | 5-bit medium | Good quality, smaller |
|
| 350 |
+
| {model_name}-q4_k_m.gguf | Q4_K_M | ~300MB | 4-bit medium | Recommended - good balance |
|
| 351 |
+
|
| 352 |
+
## Usage
|
| 353 |
+
|
| 354 |
+
### With llama.cpp
|
| 355 |
+
|
| 356 |
+
```bash
|
| 357 |
+
# Download model
|
| 358 |
+
hf download {OUTPUT_REPO} {model_name}-q4_k_m.gguf
|
| 359 |
+
|
| 360 |
+
# Run with llama.cpp
|
| 361 |
+
./llama-cli -m {model_name}-q4_k_m.gguf -p "Your prompt here"
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
### With Ollama
|
| 365 |
+
|
| 366 |
+
1. Create a `Modelfile`:
|
| 367 |
+
```
|
| 368 |
+
FROM ./{model_name}-q4_k_m.gguf
|
| 369 |
+
```
|
| 370 |
+
|
| 371 |
+
2. Create the model:
|
| 372 |
+
```bash
|
| 373 |
+
ollama create my-model -f Modelfile
|
| 374 |
+
ollama run my-model
|
| 375 |
+
```
|
| 376 |
+
|
| 377 |
+
### With LM Studio
|
| 378 |
+
|
| 379 |
+
1. Download the `.gguf` file
|
| 380 |
+
2. Import into LM Studio
|
| 381 |
+
3. Start chatting!
|
| 382 |
+
|
| 383 |
+
## License
|
| 384 |
+
|
| 385 |
+
Inherits the license from the base model: {BASE_MODEL}
|
| 386 |
+
|
| 387 |
+
## Citation
|
| 388 |
+
|
| 389 |
+
```bibtex
|
| 390 |
+
@misc{{{OUTPUT_REPO.split('/')[-1].replace('-', '_')},
|
| 391 |
+
author = {{{username}}},
|
| 392 |
+
title = {{{OUTPUT_REPO.split('/')[-1]}}},
|
| 393 |
+
year = {{2025}},
|
| 394 |
+
publisher = {{Hugging Face}},
|
| 395 |
+
url = {{https://huggingface.co/{OUTPUT_REPO}}}
|
| 396 |
+
}}
|
| 397 |
+
```
|
| 398 |
+
|
| 399 |
+
---
|
| 400 |
+
|
| 401 |
+
*Converted to GGUF format using llama.cpp*
|
| 402 |
+
"""
|
| 403 |
+
|
| 404 |
+
try:
|
| 405 |
+
api.upload_file(
|
| 406 |
+
path_or_fileobj=readme_content.encode(),
|
| 407 |
+
path_in_repo="README.md",
|
| 408 |
+
repo_id=OUTPUT_REPO,
|
| 409 |
+
)
|
| 410 |
+
print(" β
README uploaded")
|
| 411 |
+
except Exception as e:
|
| 412 |
+
print(f" β README upload failed: {e}")
|
| 413 |
+
|
| 414 |
+
print("\n" + "=" * 60)
|
| 415 |
+
print("β
GGUF Conversion Complete!")
|
| 416 |
+
print(f"π¦ Repository: https://huggingface.co/{OUTPUT_REPO}")
|
| 417 |
+
print(f"\nπ₯ Download with:")
|
| 418 |
+
print(f" hf download {OUTPUT_REPO} {model_name}-q4_k_m.gguf")
|
| 419 |
+
print(f"\nπ Use with Ollama:")
|
| 420 |
+
print(" 1. Download the GGUF file")
|
| 421 |
+
print(f" 2. Create Modelfile: FROM ./{model_name}-q4_k_m.gguf")
|
| 422 |
+
print(" 3. ollama create my-model -f Modelfile")
|
| 423 |
+
print(" 4. ollama run my-model")
|
| 424 |
+
print("=" * 60)
|
.agents/skills/huggingface-llm-trainer/scripts/dataset_inspector.py
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# /// script
|
| 3 |
+
# requires-python = ">=3.10"
|
| 4 |
+
# dependencies = []
|
| 5 |
+
# ///
|
| 6 |
+
"""
|
| 7 |
+
Dataset Format Inspector for TRL Training (LLM-Optimized Output)
|
| 8 |
+
|
| 9 |
+
Inspects Hugging Face datasets to determine TRL training compatibility.
|
| 10 |
+
Uses Datasets Server API for instant results - no dataset download needed!
|
| 11 |
+
|
| 12 |
+
ULTRA-EFFICIENT: Uses HF Datasets Server API - completes in <2 seconds.
|
| 13 |
+
|
| 14 |
+
Usage with HF Jobs:
|
| 15 |
+
hf_jobs("uv", {
|
| 16 |
+
"script": "https://huggingface.co/datasets/evalstate/trl-helpers/raw/main/dataset_inspector.py",
|
| 17 |
+
"script_args": ["--dataset", "your/dataset", "--split", "train"]
|
| 18 |
+
})
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
import argparse
|
| 22 |
+
import sys
|
| 23 |
+
import json
|
| 24 |
+
import urllib.request
|
| 25 |
+
import urllib.parse
|
| 26 |
+
from typing import List, Dict, Any
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def parse_args():
|
| 30 |
+
parser = argparse.ArgumentParser(description="Inspect dataset format for TRL training")
|
| 31 |
+
parser.add_argument("--dataset", type=str, required=True, help="Dataset name")
|
| 32 |
+
parser.add_argument("--split", type=str, default="train", help="Dataset split (default: train)")
|
| 33 |
+
parser.add_argument("--config", type=str, default="default", help="Dataset config name (default: default)")
|
| 34 |
+
parser.add_argument("--preview", type=int, default=150, help="Max chars per field preview")
|
| 35 |
+
parser.add_argument("--samples", type=int, default=5, help="Number of samples to fetch (default: 5)")
|
| 36 |
+
parser.add_argument("--json-output", action="store_true", help="Output as JSON")
|
| 37 |
+
return parser.parse_args()
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def api_request(url: str) -> Dict:
|
| 41 |
+
"""Make API request to Datasets Server"""
|
| 42 |
+
try:
|
| 43 |
+
with urllib.request.urlopen(url, timeout=10) as response:
|
| 44 |
+
return json.loads(response.read().decode())
|
| 45 |
+
except urllib.error.HTTPError as e:
|
| 46 |
+
if e.code == 404:
|
| 47 |
+
return None
|
| 48 |
+
raise Exception(f"API request failed: {e.code} {e.reason}")
|
| 49 |
+
except Exception as e:
|
| 50 |
+
raise Exception(f"API request failed: {str(e)}")
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def get_splits(dataset: str) -> Dict:
|
| 54 |
+
"""Get available splits for dataset"""
|
| 55 |
+
url = f"https://datasets-server.huggingface.co/splits?dataset={urllib.parse.quote(dataset)}"
|
| 56 |
+
return api_request(url)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def get_rows(dataset: str, config: str, split: str, offset: int = 0, length: int = 5) -> Dict:
|
| 60 |
+
"""Get rows from dataset"""
|
| 61 |
+
url = f"https://datasets-server.huggingface.co/rows?dataset={urllib.parse.quote(dataset)}&config={config}&split={split}&offset={offset}&length={length}"
|
| 62 |
+
return api_request(url)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def find_columns(columns: List[str], patterns: List[str]) -> List[str]:
|
| 66 |
+
"""Find columns matching patterns"""
|
| 67 |
+
return [c for c in columns if any(p in c.lower() for p in patterns)]
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def check_sft_compatibility(columns: List[str]) -> Dict[str, Any]:
|
| 71 |
+
"""Check SFT compatibility"""
|
| 72 |
+
has_messages = "messages" in columns
|
| 73 |
+
has_text = "text" in columns
|
| 74 |
+
has_prompt_completion = "prompt" in columns and "completion" in columns
|
| 75 |
+
|
| 76 |
+
ready = has_messages or has_text or has_prompt_completion
|
| 77 |
+
|
| 78 |
+
possible_prompt = find_columns(columns, ["prompt", "instruction", "question", "input"])
|
| 79 |
+
possible_response = find_columns(columns, ["response", "completion", "output", "answer"])
|
| 80 |
+
|
| 81 |
+
return {
|
| 82 |
+
"ready": ready,
|
| 83 |
+
"reason": "messages" if has_messages else "text" if has_text else "prompt+completion" if has_prompt_completion else None,
|
| 84 |
+
"possible_prompt": possible_prompt[0] if possible_prompt else None,
|
| 85 |
+
"possible_response": possible_response[0] if possible_response else None,
|
| 86 |
+
"has_context": "context" in columns,
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def check_dpo_compatibility(columns: List[str]) -> Dict[str, Any]:
|
| 91 |
+
"""Check DPO compatibility"""
|
| 92 |
+
has_standard = "prompt" in columns and "chosen" in columns and "rejected" in columns
|
| 93 |
+
|
| 94 |
+
possible_prompt = find_columns(columns, ["prompt", "instruction", "question", "input"])
|
| 95 |
+
possible_chosen = find_columns(columns, ["chosen", "preferred", "winner"])
|
| 96 |
+
possible_rejected = find_columns(columns, ["rejected", "dispreferred", "loser"])
|
| 97 |
+
|
| 98 |
+
can_map = bool(possible_prompt and possible_chosen and possible_rejected)
|
| 99 |
+
|
| 100 |
+
return {
|
| 101 |
+
"ready": has_standard,
|
| 102 |
+
"can_map": can_map,
|
| 103 |
+
"prompt_col": possible_prompt[0] if possible_prompt else None,
|
| 104 |
+
"chosen_col": possible_chosen[0] if possible_chosen else None,
|
| 105 |
+
"rejected_col": possible_rejected[0] if possible_rejected else None,
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def check_grpo_compatibility(columns: List[str]) -> Dict[str, Any]:
|
| 110 |
+
"""Check GRPO compatibility"""
|
| 111 |
+
has_prompt = "prompt" in columns
|
| 112 |
+
has_no_responses = "chosen" not in columns and "rejected" not in columns
|
| 113 |
+
|
| 114 |
+
possible_prompt = find_columns(columns, ["prompt", "instruction", "question", "input"])
|
| 115 |
+
|
| 116 |
+
return {
|
| 117 |
+
"ready": has_prompt and has_no_responses,
|
| 118 |
+
"can_map": bool(possible_prompt) and has_no_responses,
|
| 119 |
+
"prompt_col": possible_prompt[0] if possible_prompt else None,
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def check_kto_compatibility(columns: List[str]) -> Dict[str, Any]:
|
| 124 |
+
"""Check KTO compatibility"""
|
| 125 |
+
return {"ready": "prompt" in columns and "completion" in columns and "label" in columns}
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def generate_mapping_code(method: str, info: Dict[str, Any]) -> str:
|
| 129 |
+
"""Generate mapping code for a training method"""
|
| 130 |
+
if method == "SFT":
|
| 131 |
+
if info["ready"]:
|
| 132 |
+
return None
|
| 133 |
+
|
| 134 |
+
prompt_col = info.get("possible_prompt")
|
| 135 |
+
response_col = info.get("possible_response")
|
| 136 |
+
has_context = info.get("has_context", False)
|
| 137 |
+
|
| 138 |
+
if not prompt_col:
|
| 139 |
+
return None
|
| 140 |
+
|
| 141 |
+
if has_context and response_col:
|
| 142 |
+
return f"""def format_for_sft(example):
|
| 143 |
+
text = f"Instruction: {{example['{prompt_col}']}}\n\n"
|
| 144 |
+
if example.get('context'):
|
| 145 |
+
text += f"Context: {{example['context']}}\n\n"
|
| 146 |
+
text += f"Response: {{example['{response_col}']}}"
|
| 147 |
+
return {{'text': text}}
|
| 148 |
+
|
| 149 |
+
dataset = dataset.map(format_for_sft, remove_columns=dataset.column_names)"""
|
| 150 |
+
elif response_col:
|
| 151 |
+
return f"""def format_for_sft(example):
|
| 152 |
+
return {{'text': f"{{example['{prompt_col}']}}\n\n{{example['{response_col}']}}}}
|
| 153 |
+
|
| 154 |
+
dataset = dataset.map(format_for_sft, remove_columns=dataset.column_names)"""
|
| 155 |
+
else:
|
| 156 |
+
return f"""def format_for_sft(example):
|
| 157 |
+
return {{'text': example['{prompt_col}']}}
|
| 158 |
+
|
| 159 |
+
dataset = dataset.map(format_for_sft, remove_columns=dataset.column_names)"""
|
| 160 |
+
|
| 161 |
+
elif method == "DPO":
|
| 162 |
+
if info["ready"] or not info["can_map"]:
|
| 163 |
+
return None
|
| 164 |
+
|
| 165 |
+
return f"""def format_for_dpo(example):
|
| 166 |
+
return {{
|
| 167 |
+
'prompt': example['{info['prompt_col']}'],
|
| 168 |
+
'chosen': example['{info['chosen_col']}'],
|
| 169 |
+
'rejected': example['{info['rejected_col']}'],
|
| 170 |
+
}}
|
| 171 |
+
|
| 172 |
+
dataset = dataset.map(format_for_dpo, remove_columns=dataset.column_names)"""
|
| 173 |
+
|
| 174 |
+
elif method == "GRPO":
|
| 175 |
+
if info["ready"] or not info["can_map"]:
|
| 176 |
+
return None
|
| 177 |
+
|
| 178 |
+
return f"""def format_for_grpo(example):
|
| 179 |
+
return {{'prompt': example['{info['prompt_col']}']}}
|
| 180 |
+
|
| 181 |
+
dataset = dataset.map(format_for_grpo, remove_columns=dataset.column_names)"""
|
| 182 |
+
|
| 183 |
+
return None
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def format_value_preview(value: Any, max_chars: int) -> str:
|
| 187 |
+
"""Format value for preview"""
|
| 188 |
+
if value is None:
|
| 189 |
+
return "None"
|
| 190 |
+
elif isinstance(value, str):
|
| 191 |
+
return value[:max_chars] + ("..." if len(value) > max_chars else "")
|
| 192 |
+
elif isinstance(value, list):
|
| 193 |
+
if len(value) > 0 and isinstance(value[0], dict):
|
| 194 |
+
return f"[{len(value)} items] Keys: {list(value[0].keys())}"
|
| 195 |
+
preview = str(value)
|
| 196 |
+
return preview[:max_chars] + ("..." if len(preview) > max_chars else "")
|
| 197 |
+
else:
|
| 198 |
+
preview = str(value)
|
| 199 |
+
return preview[:max_chars] + ("..." if len(preview) > max_chars else "")
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def main():
|
| 203 |
+
args = parse_args()
|
| 204 |
+
|
| 205 |
+
print(f"Fetching dataset info via Datasets Server API...")
|
| 206 |
+
|
| 207 |
+
try:
|
| 208 |
+
# Get splits info
|
| 209 |
+
splits_data = get_splits(args.dataset)
|
| 210 |
+
if not splits_data or "splits" not in splits_data:
|
| 211 |
+
print(f"ERROR: Could not fetch splits for dataset '{args.dataset}'")
|
| 212 |
+
print(f" Dataset may not exist or is not accessible via Datasets Server API")
|
| 213 |
+
sys.exit(1)
|
| 214 |
+
|
| 215 |
+
# Find the right config
|
| 216 |
+
available_configs = set()
|
| 217 |
+
split_found = False
|
| 218 |
+
config_to_use = args.config
|
| 219 |
+
|
| 220 |
+
for split_info in splits_data["splits"]:
|
| 221 |
+
available_configs.add(split_info["config"])
|
| 222 |
+
if split_info["config"] == args.config and split_info["split"] == args.split:
|
| 223 |
+
split_found = True
|
| 224 |
+
|
| 225 |
+
# If default config not found, try first available
|
| 226 |
+
if not split_found and available_configs:
|
| 227 |
+
config_to_use = list(available_configs)[0]
|
| 228 |
+
print(f"Config '{args.config}' not found, trying '{config_to_use}'...")
|
| 229 |
+
|
| 230 |
+
# Get rows
|
| 231 |
+
rows_data = get_rows(args.dataset, config_to_use, args.split, offset=0, length=args.samples)
|
| 232 |
+
|
| 233 |
+
if not rows_data or "rows" not in rows_data:
|
| 234 |
+
print(f"ERROR: Could not fetch rows for dataset '{args.dataset}'")
|
| 235 |
+
print(f" Split '{args.split}' may not exist")
|
| 236 |
+
print(f" Available configs: {', '.join(sorted(available_configs))}")
|
| 237 |
+
sys.exit(1)
|
| 238 |
+
|
| 239 |
+
rows = rows_data["rows"]
|
| 240 |
+
if not rows:
|
| 241 |
+
print(f"ERROR: No rows found in split '{args.split}'")
|
| 242 |
+
sys.exit(1)
|
| 243 |
+
|
| 244 |
+
# Extract column info from first row
|
| 245 |
+
first_row = rows[0]["row"]
|
| 246 |
+
columns = list(first_row.keys())
|
| 247 |
+
features = rows_data.get("features", [])
|
| 248 |
+
|
| 249 |
+
# Get total count if available
|
| 250 |
+
total_examples = "Unknown"
|
| 251 |
+
for split_info in splits_data["splits"]:
|
| 252 |
+
if split_info["config"] == config_to_use and split_info["split"] == args.split:
|
| 253 |
+
total_examples = f"{split_info.get('num_examples', 'Unknown'):,}" if isinstance(split_info.get('num_examples'), int) else "Unknown"
|
| 254 |
+
break
|
| 255 |
+
|
| 256 |
+
except Exception as e:
|
| 257 |
+
print(f"ERROR: {str(e)}")
|
| 258 |
+
sys.exit(1)
|
| 259 |
+
|
| 260 |
+
# Run compatibility checks
|
| 261 |
+
sft_info = check_sft_compatibility(columns)
|
| 262 |
+
dpo_info = check_dpo_compatibility(columns)
|
| 263 |
+
grpo_info = check_grpo_compatibility(columns)
|
| 264 |
+
kto_info = check_kto_compatibility(columns)
|
| 265 |
+
|
| 266 |
+
# Determine recommended methods
|
| 267 |
+
recommended = []
|
| 268 |
+
if sft_info["ready"]:
|
| 269 |
+
recommended.append("SFT")
|
| 270 |
+
elif sft_info["possible_prompt"]:
|
| 271 |
+
recommended.append("SFT (needs mapping)")
|
| 272 |
+
|
| 273 |
+
if dpo_info["ready"]:
|
| 274 |
+
recommended.append("DPO")
|
| 275 |
+
elif dpo_info["can_map"]:
|
| 276 |
+
recommended.append("DPO (needs mapping)")
|
| 277 |
+
|
| 278 |
+
if grpo_info["ready"]:
|
| 279 |
+
recommended.append("GRPO")
|
| 280 |
+
elif grpo_info["can_map"]:
|
| 281 |
+
recommended.append("GRPO (needs mapping)")
|
| 282 |
+
|
| 283 |
+
if kto_info["ready"]:
|
| 284 |
+
recommended.append("KTO")
|
| 285 |
+
|
| 286 |
+
# JSON output mode
|
| 287 |
+
if args.json_output:
|
| 288 |
+
result = {
|
| 289 |
+
"dataset": args.dataset,
|
| 290 |
+
"config": config_to_use,
|
| 291 |
+
"split": args.split,
|
| 292 |
+
"total_examples": total_examples,
|
| 293 |
+
"columns": columns,
|
| 294 |
+
"features": [{"name": f["name"], "type": f["type"]} for f in features] if features else [],
|
| 295 |
+
"compatibility": {
|
| 296 |
+
"SFT": sft_info,
|
| 297 |
+
"DPO": dpo_info,
|
| 298 |
+
"GRPO": grpo_info,
|
| 299 |
+
"KTO": kto_info,
|
| 300 |
+
},
|
| 301 |
+
"recommended_methods": recommended,
|
| 302 |
+
}
|
| 303 |
+
print(json.dumps(result, indent=2))
|
| 304 |
+
sys.exit(0)
|
| 305 |
+
|
| 306 |
+
# Human-readable output optimized for LLM parsing
|
| 307 |
+
print("=" * 80)
|
| 308 |
+
print(f"DATASET INSPECTION RESULTS")
|
| 309 |
+
print("=" * 80)
|
| 310 |
+
|
| 311 |
+
print(f"\nDataset: {args.dataset}")
|
| 312 |
+
print(f"Config: {config_to_use}")
|
| 313 |
+
print(f"Split: {args.split}")
|
| 314 |
+
print(f"Total examples: {total_examples}")
|
| 315 |
+
print(f"Samples fetched: {len(rows)}")
|
| 316 |
+
|
| 317 |
+
print(f"\n{'COLUMNS':-<80}")
|
| 318 |
+
if features:
|
| 319 |
+
for feature in features:
|
| 320 |
+
print(f" {feature['name']}: {feature['type']}")
|
| 321 |
+
else:
|
| 322 |
+
for col in columns:
|
| 323 |
+
print(f" {col}: (type info not available)")
|
| 324 |
+
|
| 325 |
+
print(f"\n{'EXAMPLE DATA':-<80}")
|
| 326 |
+
example = first_row
|
| 327 |
+
for col in columns:
|
| 328 |
+
value = example.get(col)
|
| 329 |
+
display = format_value_preview(value, args.preview)
|
| 330 |
+
print(f"\n{col}:")
|
| 331 |
+
print(f" {display}")
|
| 332 |
+
|
| 333 |
+
print(f"\n{'TRAINING METHOD COMPATIBILITY':-<80}")
|
| 334 |
+
|
| 335 |
+
# SFT
|
| 336 |
+
print(f"\n[SFT] {'β READY' if sft_info['ready'] else 'β NEEDS MAPPING'}")
|
| 337 |
+
if sft_info["ready"]:
|
| 338 |
+
print(f" Reason: Dataset has '{sft_info['reason']}' field")
|
| 339 |
+
print(f" Action: Use directly with SFTTrainer")
|
| 340 |
+
elif sft_info["possible_prompt"]:
|
| 341 |
+
print(f" Detected: prompt='{sft_info['possible_prompt']}' response='{sft_info['possible_response']}'")
|
| 342 |
+
print(f" Action: Apply mapping code (see below)")
|
| 343 |
+
else:
|
| 344 |
+
print(f" Status: Cannot determine mapping - manual inspection needed")
|
| 345 |
+
|
| 346 |
+
# DPO
|
| 347 |
+
print(f"\n[DPO] {'β READY' if dpo_info['ready'] else 'β NEEDS MAPPING' if dpo_info['can_map'] else 'β INCOMPATIBLE'}")
|
| 348 |
+
if dpo_info["ready"]:
|
| 349 |
+
print(f" Reason: Dataset has 'prompt', 'chosen', 'rejected' fields")
|
| 350 |
+
print(f" Action: Use directly with DPOTrainer")
|
| 351 |
+
elif dpo_info["can_map"]:
|
| 352 |
+
print(f" Detected: prompt='{dpo_info['prompt_col']}' chosen='{dpo_info['chosen_col']}' rejected='{dpo_info['rejected_col']}'")
|
| 353 |
+
print(f" Action: Apply mapping code (see below)")
|
| 354 |
+
else:
|
| 355 |
+
print(f" Status: Missing required fields (prompt + chosen + rejected)")
|
| 356 |
+
|
| 357 |
+
# GRPO
|
| 358 |
+
print(f"\n[GRPO] {'β READY' if grpo_info['ready'] else 'β NEEDS MAPPING' if grpo_info['can_map'] else 'β INCOMPATIBLE'}")
|
| 359 |
+
if grpo_info["ready"]:
|
| 360 |
+
print(f" Reason: Dataset has 'prompt' field")
|
| 361 |
+
print(f" Action: Use directly with GRPOTrainer")
|
| 362 |
+
elif grpo_info["can_map"]:
|
| 363 |
+
print(f" Detected: prompt='{grpo_info['prompt_col']}'")
|
| 364 |
+
print(f" Action: Apply mapping code (see below)")
|
| 365 |
+
else:
|
| 366 |
+
print(f" Status: Missing prompt field")
|
| 367 |
+
|
| 368 |
+
# KTO
|
| 369 |
+
print(f"\n[KTO] {'β READY' if kto_info['ready'] else 'β INCOMPATIBLE'}")
|
| 370 |
+
if kto_info["ready"]:
|
| 371 |
+
print(f" Reason: Dataset has 'prompt', 'completion', 'label' fields")
|
| 372 |
+
print(f" Action: Use directly with KTOTrainer")
|
| 373 |
+
else:
|
| 374 |
+
print(f" Status: Missing required fields (prompt + completion + label)")
|
| 375 |
+
|
| 376 |
+
# Mapping code
|
| 377 |
+
print(f"\n{'MAPPING CODE (if needed)':-<80}")
|
| 378 |
+
|
| 379 |
+
mapping_needed = False
|
| 380 |
+
|
| 381 |
+
sft_mapping = generate_mapping_code("SFT", sft_info)
|
| 382 |
+
if sft_mapping:
|
| 383 |
+
print(f"\n# For SFT Training:")
|
| 384 |
+
print(sft_mapping)
|
| 385 |
+
mapping_needed = True
|
| 386 |
+
|
| 387 |
+
dpo_mapping = generate_mapping_code("DPO", dpo_info)
|
| 388 |
+
if dpo_mapping:
|
| 389 |
+
print(f"\n# For DPO Training:")
|
| 390 |
+
print(dpo_mapping)
|
| 391 |
+
mapping_needed = True
|
| 392 |
+
|
| 393 |
+
grpo_mapping = generate_mapping_code("GRPO", grpo_info)
|
| 394 |
+
if grpo_mapping:
|
| 395 |
+
print(f"\n# For GRPO Training:")
|
| 396 |
+
print(grpo_mapping)
|
| 397 |
+
mapping_needed = True
|
| 398 |
+
|
| 399 |
+
if not mapping_needed:
|
| 400 |
+
print("\nNo mapping needed - dataset is ready for training!")
|
| 401 |
+
|
| 402 |
+
print(f"\n{'SUMMARY':-<80}")
|
| 403 |
+
print(f"Recommended training methods: {', '.join(recommended) if recommended else 'None (dataset needs formatting)'}")
|
| 404 |
+
print(f"\nNote: Used Datasets Server API (instant, no download required)")
|
| 405 |
+
|
| 406 |
+
print("\n" + "=" * 80)
|
| 407 |
+
sys.exit(0)
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
if __name__ == "__main__":
|
| 411 |
+
try:
|
| 412 |
+
main()
|
| 413 |
+
except KeyboardInterrupt:
|
| 414 |
+
sys.exit(0)
|
| 415 |
+
except Exception as e:
|
| 416 |
+
print(f"ERROR: {e}", file=sys.stderr)
|
| 417 |
+
sys.exit(1)
|
.agents/skills/huggingface-llm-trainer/scripts/estimate_cost.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# /// script
|
| 3 |
+
# requires-python = ">=3.10"
|
| 4 |
+
# dependencies = []
|
| 5 |
+
# ///
|
| 6 |
+
"""
|
| 7 |
+
Estimate training time and cost for TRL jobs.
|
| 8 |
+
|
| 9 |
+
Usage with uv:
|
| 10 |
+
uv run estimate_cost.py --model <model> --dataset <dataset> --hardware <flavor>
|
| 11 |
+
|
| 12 |
+
Example:
|
| 13 |
+
uv run estimate_cost.py --model Qwen/Qwen2.5-0.5B --dataset trl-lib/Capybara --hardware a10g-large
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
import argparse
|
| 17 |
+
|
| 18 |
+
# Hardware costs per hour (approximate)
|
| 19 |
+
HARDWARE_COSTS = {
|
| 20 |
+
"t4-small": 0.75,
|
| 21 |
+
"t4-medium": 1.50,
|
| 22 |
+
"l4x1": 2.50,
|
| 23 |
+
"a10g-small": 3.50,
|
| 24 |
+
"a10g-large": 5.00,
|
| 25 |
+
"a10g-largex2": 10.00,
|
| 26 |
+
"a10g-largex4": 20.00,
|
| 27 |
+
"a100-large": 10.00,
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
# Model sizes in billions of parameters
|
| 31 |
+
MODEL_SIZES = {
|
| 32 |
+
"0.5B": 0.5,
|
| 33 |
+
"1.5B": 1.5,
|
| 34 |
+
"3B": 3,
|
| 35 |
+
"7B": 7,
|
| 36 |
+
"13B": 13,
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
def estimate_training_time(model_params, dataset_size, epochs, hardware):
|
| 40 |
+
"""Estimate training time in hours."""
|
| 41 |
+
# Rough estimates based on empirical observations
|
| 42 |
+
# These are approximations and actual times will vary
|
| 43 |
+
|
| 44 |
+
base_time_per_1k_examples = 0.1 # hours for 1B model on a10g-large
|
| 45 |
+
|
| 46 |
+
# Adjust for model size
|
| 47 |
+
time = base_time_per_1k_examples * model_params * (dataset_size / 1000) * epochs
|
| 48 |
+
|
| 49 |
+
# Adjust for hardware (relative to a10g-large baseline)
|
| 50 |
+
hardware_multipliers = {
|
| 51 |
+
"t4-small": 2.0,
|
| 52 |
+
"t4-medium": 1.5,
|
| 53 |
+
"l4x1": 1.2,
|
| 54 |
+
"a10g-small": 1.3,
|
| 55 |
+
"a10g-large": 1.0,
|
| 56 |
+
"a10g-largex2": 0.6,
|
| 57 |
+
"a10g-largex4": 0.4,
|
| 58 |
+
"a100-large": 0.7,
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
multiplier = hardware_multipliers.get(hardware, 1.0)
|
| 62 |
+
time *= multiplier
|
| 63 |
+
|
| 64 |
+
return time
|
| 65 |
+
|
| 66 |
+
def parse_args():
|
| 67 |
+
parser = argparse.ArgumentParser(description="Estimate training cost for TRL jobs")
|
| 68 |
+
parser.add_argument("--model", required=True, help="Model name or size (e.g., 'Qwen/Qwen2.5-0.5B' or '0.5B')")
|
| 69 |
+
parser.add_argument("--dataset", required=True, help="Dataset name")
|
| 70 |
+
parser.add_argument("--hardware", required=True, choices=HARDWARE_COSTS.keys(), help="Hardware flavor")
|
| 71 |
+
parser.add_argument("--dataset-size", type=int, help="Override dataset size (number of examples)")
|
| 72 |
+
parser.add_argument("--epochs", type=int, default=3, help="Number of training epochs")
|
| 73 |
+
return parser.parse_args()
|
| 74 |
+
|
| 75 |
+
def extract_model_size(model_name):
|
| 76 |
+
"""Extract model size from name or return parsed value."""
|
| 77 |
+
for size_str, size_val in MODEL_SIZES.items():
|
| 78 |
+
if size_str in model_name:
|
| 79 |
+
return size_val
|
| 80 |
+
|
| 81 |
+
# Try to parse directly
|
| 82 |
+
try:
|
| 83 |
+
if "B" in model_name:
|
| 84 |
+
return float(model_name.replace("B", ""))
|
| 85 |
+
except:
|
| 86 |
+
pass
|
| 87 |
+
|
| 88 |
+
return 1.0 # Default to 1B if can't determine
|
| 89 |
+
|
| 90 |
+
def main():
|
| 91 |
+
args = parse_args()
|
| 92 |
+
|
| 93 |
+
# Extract model parameters
|
| 94 |
+
model_params = extract_model_size(args.model)
|
| 95 |
+
print(f"π Model: {args.model} (~{model_params}B parameters)")
|
| 96 |
+
|
| 97 |
+
# Estimate dataset size (would need to load to get real size)
|
| 98 |
+
if args.dataset_size:
|
| 99 |
+
dataset_size = args.dataset_size
|
| 100 |
+
else:
|
| 101 |
+
# Common dataset sizes (approximations)
|
| 102 |
+
dataset_sizes = {
|
| 103 |
+
"trl-lib/Capybara": 16000,
|
| 104 |
+
"Anthropic/hh-rlhf": 160000,
|
| 105 |
+
}
|
| 106 |
+
dataset_size = dataset_sizes.get(args.dataset, 10000)
|
| 107 |
+
|
| 108 |
+
print(f"π¦ Dataset: {args.dataset} (~{dataset_size} examples)")
|
| 109 |
+
print(f"π Epochs: {args.epochs}")
|
| 110 |
+
print(f"π» Hardware: {args.hardware}")
|
| 111 |
+
print()
|
| 112 |
+
|
| 113 |
+
# Estimate training time
|
| 114 |
+
estimated_hours = estimate_training_time(model_params, dataset_size, args.epochs, args.hardware)
|
| 115 |
+
estimated_cost = estimated_hours * HARDWARE_COSTS[args.hardware]
|
| 116 |
+
|
| 117 |
+
# Recommend timeout with buffer
|
| 118 |
+
recommended_timeout_hours = estimated_hours * 1.3 # 30% buffer
|
| 119 |
+
|
| 120 |
+
print(f"β±οΈ Estimated training time: {estimated_hours:.1f} hours")
|
| 121 |
+
print(f"π° Estimated cost: ${estimated_cost:.2f}")
|
| 122 |
+
print(f"β° Recommended timeout: {recommended_timeout_hours:.1f}h (with 30% buffer)")
|
| 123 |
+
print()
|
| 124 |
+
|
| 125 |
+
# Warnings and recommendations
|
| 126 |
+
if estimated_hours > 4:
|
| 127 |
+
print("β οΈ Long training time - consider:")
|
| 128 |
+
print(" - Using faster hardware")
|
| 129 |
+
print(" - Reducing epochs")
|
| 130 |
+
print(" - Using a smaller dataset subset for testing")
|
| 131 |
+
|
| 132 |
+
if model_params >= 7 and args.hardware not in ["a10g-largex2", "a10g-largex4", "a100-large"]:
|
| 133 |
+
print("β οΈ Large model - consider using:")
|
| 134 |
+
print(" - Larger GPU (a100-large)")
|
| 135 |
+
print(" - Multi-GPU setup (a10g-largex2 or a10g-largex4)")
|
| 136 |
+
print(" - LoRA/PEFT for memory efficiency")
|
| 137 |
+
|
| 138 |
+
print()
|
| 139 |
+
print("π Example job configuration:")
|
| 140 |
+
print(f"""
|
| 141 |
+
hf_jobs("uv", {{
|
| 142 |
+
"script": "your_training_script.py",
|
| 143 |
+
"flavor": "{args.hardware}",
|
| 144 |
+
"timeout": "{recommended_timeout_hours:.0f}h",
|
| 145 |
+
"secrets": {{"HF_TOKEN": "$HF_TOKEN"}}
|
| 146 |
+
}})
|
| 147 |
+
""")
|
| 148 |
+
|
| 149 |
+
if __name__ == "__main__":
|
| 150 |
+
main()
|
.agents/skills/huggingface-llm-trainer/scripts/hf_benchmarks.py
ADDED
|
@@ -0,0 +1,659 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env -S uv run --script
|
| 2 |
+
# /// script
|
| 3 |
+
# requires-python = ">=3.10"
|
| 4 |
+
# dependencies = []
|
| 5 |
+
# ///
|
| 6 |
+
"""
|
| 7 |
+
Search Hugging Face benchmark datasets and fetch leaderboard results.
|
| 8 |
+
|
| 9 |
+
This script is designed to be pipeline-friendly:
|
| 10 |
+
- search benchmark datasets by free text, alias, task, and modality
|
| 11 |
+
- fetch dataset leaderboards in normalized JSON / NDJSON / table form
|
| 12 |
+
- optionally read dataset ids from stdin for chaining
|
| 13 |
+
|
| 14 |
+
It uses HF_TOKEN automatically when present.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
from __future__ import annotations
|
| 18 |
+
|
| 19 |
+
import argparse
|
| 20 |
+
import json
|
| 21 |
+
import os
|
| 22 |
+
import re
|
| 23 |
+
import sys
|
| 24 |
+
import textwrap
|
| 25 |
+
import urllib.error
|
| 26 |
+
import urllib.parse
|
| 27 |
+
import urllib.request
|
| 28 |
+
from typing import Any, Iterable
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
BASE_URL = "https://huggingface.co"
|
| 32 |
+
DEFAULT_TIMEOUT = 30
|
| 33 |
+
|
| 34 |
+
ALIASES: dict[str, list[str]] = {
|
| 35 |
+
"ocr": [
|
| 36 |
+
"ocr",
|
| 37 |
+
"olmocr",
|
| 38 |
+
"pdf",
|
| 39 |
+
"image-to-text",
|
| 40 |
+
"screen",
|
| 41 |
+
"screenspot",
|
| 42 |
+
"markdown",
|
| 43 |
+
"text recognition",
|
| 44 |
+
],
|
| 45 |
+
"coding": [
|
| 46 |
+
"code",
|
| 47 |
+
"coding",
|
| 48 |
+
"software engineering",
|
| 49 |
+
"programming",
|
| 50 |
+
"swe",
|
| 51 |
+
"terminal",
|
| 52 |
+
"patch",
|
| 53 |
+
"bug",
|
| 54 |
+
"cuda",
|
| 55 |
+
],
|
| 56 |
+
"math": [
|
| 57 |
+
"math",
|
| 58 |
+
"reasoning",
|
| 59 |
+
"gsm8k",
|
| 60 |
+
"mmlu",
|
| 61 |
+
"gpqa",
|
| 62 |
+
"aime",
|
| 63 |
+
"hmmt",
|
| 64 |
+
],
|
| 65 |
+
"retrieval": [
|
| 66 |
+
"retrieval",
|
| 67 |
+
"search",
|
| 68 |
+
"mteb",
|
| 69 |
+
"arguana",
|
| 70 |
+
"bright",
|
| 71 |
+
],
|
| 72 |
+
"agents": [
|
| 73 |
+
"agent",
|
| 74 |
+
"agents",
|
| 75 |
+
"terminal",
|
| 76 |
+
"screen",
|
| 77 |
+
"computer use",
|
| 78 |
+
"tool use",
|
| 79 |
+
],
|
| 80 |
+
"asr": [
|
| 81 |
+
"asr",
|
| 82 |
+
"speech",
|
| 83 |
+
"audio",
|
| 84 |
+
"transcribe",
|
| 85 |
+
"transcription",
|
| 86 |
+
],
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class HfApiError(RuntimeError):
|
| 91 |
+
pass
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
class FullHelpArgumentParser(argparse.ArgumentParser):
|
| 95 |
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
| 96 |
+
super().__init__(*args, **kwargs)
|
| 97 |
+
self._search_parser: argparse.ArgumentParser | None = None
|
| 98 |
+
self._leaderboard_parser: argparse.ArgumentParser | None = None
|
| 99 |
+
|
| 100 |
+
def format_help(self) -> str:
|
| 101 |
+
text = super().format_help()
|
| 102 |
+
extra_sections: list[str] = []
|
| 103 |
+
|
| 104 |
+
if self._search_parser is not None:
|
| 105 |
+
extra_sections.append(
|
| 106 |
+
"\nsearch command options:\n"
|
| 107 |
+
+ textwrap.indent(self._search_parser.format_help().strip(), " ")
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
if self._leaderboard_parser is not None:
|
| 111 |
+
extra_sections.append(
|
| 112 |
+
"\nleaderboard command options:\n"
|
| 113 |
+
+ textwrap.indent(self._leaderboard_parser.format_help().strip(), " ")
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
if extra_sections:
|
| 117 |
+
text += "\n" + "\n".join(extra_sections) + "\n"
|
| 118 |
+
return text
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def auth_headers() -> dict[str, str]:
|
| 122 |
+
token = os.getenv("HF_TOKEN")
|
| 123 |
+
return {"Authorization": f"Bearer {token}"} if token else {}
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def http_get_json(path: str, params: dict[str, Any] | None = None) -> Any:
|
| 127 |
+
url = f"{BASE_URL}{path}"
|
| 128 |
+
if params:
|
| 129 |
+
pairs: list[tuple[str, str]] = []
|
| 130 |
+
for key, value in params.items():
|
| 131 |
+
if value is None:
|
| 132 |
+
continue
|
| 133 |
+
if isinstance(value, (list, tuple)):
|
| 134 |
+
for item in value:
|
| 135 |
+
pairs.append((key, str(item)))
|
| 136 |
+
else:
|
| 137 |
+
pairs.append((key, str(value)))
|
| 138 |
+
if pairs:
|
| 139 |
+
url = f"{url}?{urllib.parse.urlencode(pairs)}"
|
| 140 |
+
|
| 141 |
+
req = urllib.request.Request(url, headers=auth_headers())
|
| 142 |
+
try:
|
| 143 |
+
with urllib.request.urlopen(req, timeout=DEFAULT_TIMEOUT) as resp:
|
| 144 |
+
return json.loads(resp.read().decode("utf-8"))
|
| 145 |
+
except urllib.error.HTTPError as exc:
|
| 146 |
+
body = exc.read().decode("utf-8", errors="replace")
|
| 147 |
+
raise HfApiError(f"{exc.code} {exc.reason} for {url}: {body[:500]}") from exc
|
| 148 |
+
except urllib.error.URLError as exc:
|
| 149 |
+
raise HfApiError(f"Request failed for {url}: {exc}") from exc
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def shorten(text: str, width: int) -> str:
|
| 153 |
+
text = " ".join((text or "").split())
|
| 154 |
+
if len(text) <= width:
|
| 155 |
+
return text
|
| 156 |
+
return text[: max(0, width - 1)] + "β¦"
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def first_text(value: Any) -> str:
|
| 160 |
+
if value is None:
|
| 161 |
+
return ""
|
| 162 |
+
if isinstance(value, str):
|
| 163 |
+
return value
|
| 164 |
+
if isinstance(value, list):
|
| 165 |
+
return " ".join(first_text(v) for v in value)
|
| 166 |
+
if isinstance(value, dict):
|
| 167 |
+
return " ".join(first_text(v) for v in value.values())
|
| 168 |
+
return str(value)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def benchmark_catalog(limit: int = 500) -> list[dict[str, Any]]:
|
| 172 |
+
data = http_get_json(
|
| 173 |
+
"/api/datasets",
|
| 174 |
+
params={"filter": "benchmark:official", "limit": limit, "full": "true"},
|
| 175 |
+
)
|
| 176 |
+
if not isinstance(data, list):
|
| 177 |
+
raise HfApiError("Unexpected response while listing benchmark datasets")
|
| 178 |
+
return data
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def dataset_search_blob(dataset: dict[str, Any]) -> str:
|
| 182 |
+
card = dataset.get("cardData") or {}
|
| 183 |
+
parts = [
|
| 184 |
+
dataset.get("id", ""),
|
| 185 |
+
dataset.get("description", ""),
|
| 186 |
+
first_text(dataset.get("tags")),
|
| 187 |
+
first_text(card.get("pretty_name")),
|
| 188 |
+
first_text(card.get("tags")),
|
| 189 |
+
first_text(card.get("task_categories")),
|
| 190 |
+
first_text(card.get("task_ids")),
|
| 191 |
+
]
|
| 192 |
+
return " ".join(parts).lower()
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def dataset_search_fields(dataset: dict[str, Any]) -> dict[str, str]:
|
| 196 |
+
card = dataset.get("cardData") or {}
|
| 197 |
+
return {
|
| 198 |
+
"id": first_text(dataset.get("id")).lower(),
|
| 199 |
+
"pretty_name": first_text(card.get("pretty_name")).lower(),
|
| 200 |
+
"tags": " ".join(
|
| 201 |
+
[
|
| 202 |
+
first_text(dataset.get("tags")),
|
| 203 |
+
first_text(card.get("tags")),
|
| 204 |
+
first_text(card.get("task_categories")),
|
| 205 |
+
first_text(card.get("task_ids")),
|
| 206 |
+
first_text(card.get("modality")),
|
| 207 |
+
]
|
| 208 |
+
).lower(),
|
| 209 |
+
"description": first_text(dataset.get("description")).lower(),
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def collect_prefixed_tags(dataset: dict[str, Any], prefixes: Iterable[str]) -> list[str]:
|
| 214 |
+
prefixes = tuple(prefixes)
|
| 215 |
+
tags = dataset.get("tags") or []
|
| 216 |
+
card = dataset.get("cardData") or {}
|
| 217 |
+
|
| 218 |
+
out: list[str] = []
|
| 219 |
+
for tag in tags:
|
| 220 |
+
if isinstance(tag, str) and tag.startswith(prefixes):
|
| 221 |
+
out.append(tag)
|
| 222 |
+
|
| 223 |
+
for key, prefix in (
|
| 224 |
+
("task_categories", "task_categories:"),
|
| 225 |
+
("task_ids", "task_ids:"),
|
| 226 |
+
("modality", "modality:"),
|
| 227 |
+
):
|
| 228 |
+
values = card.get(key)
|
| 229 |
+
if isinstance(values, list):
|
| 230 |
+
for value in values:
|
| 231 |
+
full_tag = f"{prefix}{value}"
|
| 232 |
+
if full_tag.startswith(prefixes):
|
| 233 |
+
out.append(full_tag)
|
| 234 |
+
|
| 235 |
+
deduped: list[str] = []
|
| 236 |
+
seen: set[str] = set()
|
| 237 |
+
for tag in out:
|
| 238 |
+
if tag not in seen:
|
| 239 |
+
deduped.append(tag)
|
| 240 |
+
seen.add(tag)
|
| 241 |
+
return deduped
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
def expand_aliases(aliases: list[str]) -> dict[str, list[str]]:
|
| 245 |
+
expanded: dict[str, list[str]] = {}
|
| 246 |
+
for alias in aliases:
|
| 247 |
+
terms = ALIASES.get(alias.lower(), [alias])
|
| 248 |
+
expanded[alias] = terms
|
| 249 |
+
return expanded
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def matches_term(blob: str, term: str) -> bool:
|
| 253 |
+
candidate = term.lower().strip()
|
| 254 |
+
if not candidate:
|
| 255 |
+
return False
|
| 256 |
+
if re.fullmatch(r"[a-z0-9_]+", candidate):
|
| 257 |
+
return re.search(rf"(?<![a-z0-9_]){re.escape(candidate)}(?![a-z0-9_])", blob) is not None
|
| 258 |
+
return candidate in blob
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def score_dataset(
|
| 262 |
+
dataset: dict[str, Any],
|
| 263 |
+
queries: list[str],
|
| 264 |
+
aliases: dict[str, list[str]],
|
| 265 |
+
tasks: list[str],
|
| 266 |
+
modalities: list[str],
|
| 267 |
+
) -> dict[str, Any]:
|
| 268 |
+
blob = dataset_search_blob(dataset)
|
| 269 |
+
fields = dataset_search_fields(dataset)
|
| 270 |
+
task_tags = collect_prefixed_tags(dataset, ["task_categories:", "task_ids:"])
|
| 271 |
+
modality_tags = collect_prefixed_tags(dataset, ["modality:"])
|
| 272 |
+
|
| 273 |
+
score = 0
|
| 274 |
+
reasons: list[str] = []
|
| 275 |
+
|
| 276 |
+
for query in queries:
|
| 277 |
+
q = query.lower().strip()
|
| 278 |
+
if q and any(matches_term(value, q) for value in fields.values()):
|
| 279 |
+
score += 3
|
| 280 |
+
reasons.append(f"query:{query}")
|
| 281 |
+
|
| 282 |
+
for alias_name, terms in aliases.items():
|
| 283 |
+
matched_terms: list[str] = []
|
| 284 |
+
alias_score = 0
|
| 285 |
+
for term in terms:
|
| 286 |
+
strong_match = any(
|
| 287 |
+
matches_term(fields[field_name], term)
|
| 288 |
+
for field_name in ("id", "pretty_name", "tags")
|
| 289 |
+
)
|
| 290 |
+
desc_match = matches_term(fields["description"], term)
|
| 291 |
+
if strong_match:
|
| 292 |
+
alias_score += 2
|
| 293 |
+
matched_terms.append(term)
|
| 294 |
+
elif desc_match:
|
| 295 |
+
alias_score += 1
|
| 296 |
+
matched_terms.append(term)
|
| 297 |
+
if matched_terms:
|
| 298 |
+
score += alias_score
|
| 299 |
+
reasons.append(f"alias:{alias_name}=" + ",".join(matched_terms[:5]))
|
| 300 |
+
|
| 301 |
+
lower_task_tags = [t.lower() for t in task_tags]
|
| 302 |
+
for task in tasks:
|
| 303 |
+
task = task.lower().strip()
|
| 304 |
+
if not task:
|
| 305 |
+
continue
|
| 306 |
+
exact = [
|
| 307 |
+
tag
|
| 308 |
+
for tag in lower_task_tags
|
| 309 |
+
if tag == f"task_categories:{task}" or tag == f"task_ids:{task}"
|
| 310 |
+
]
|
| 311 |
+
fuzzy = matches_term(blob, task)
|
| 312 |
+
if exact:
|
| 313 |
+
score += 5
|
| 314 |
+
reasons.append(f"task:{task}")
|
| 315 |
+
elif fuzzy:
|
| 316 |
+
score += 2
|
| 317 |
+
reasons.append(f"task~:{task}")
|
| 318 |
+
|
| 319 |
+
lower_modality_tags = [m.lower() for m in modality_tags]
|
| 320 |
+
for modality in modalities:
|
| 321 |
+
modality = modality.lower().strip()
|
| 322 |
+
if not modality:
|
| 323 |
+
continue
|
| 324 |
+
if f"modality:{modality}" in lower_modality_tags:
|
| 325 |
+
score += 4
|
| 326 |
+
reasons.append(f"modality:{modality}")
|
| 327 |
+
|
| 328 |
+
return {
|
| 329 |
+
"dataset_id": dataset.get("id"),
|
| 330 |
+
"score": score,
|
| 331 |
+
"reasons": reasons,
|
| 332 |
+
"task_tags": task_tags,
|
| 333 |
+
"modality_tags": modality_tags,
|
| 334 |
+
"benchmark_tags": collect_prefixed_tags(dataset, ["benchmark:"]),
|
| 335 |
+
"pretty_name": (dataset.get("cardData") or {}).get("pretty_name"),
|
| 336 |
+
"downloads": dataset.get("downloads"),
|
| 337 |
+
"description": " ".join((dataset.get("description") or "").split()),
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
def search_benchmarks(
|
| 342 |
+
queries: list[str],
|
| 343 |
+
aliases: list[str],
|
| 344 |
+
tasks: list[str],
|
| 345 |
+
modalities: list[str],
|
| 346 |
+
limit: int,
|
| 347 |
+
) -> list[dict[str, Any]]:
|
| 348 |
+
datasets = benchmark_catalog(limit=500)
|
| 349 |
+
alias_map = expand_aliases(aliases)
|
| 350 |
+
|
| 351 |
+
results = [score_dataset(ds, queries, alias_map, tasks, modalities) for ds in datasets]
|
| 352 |
+
|
| 353 |
+
active_filters = bool(queries or aliases or tasks or modalities)
|
| 354 |
+
if active_filters:
|
| 355 |
+
results = [row for row in results if row["score"] >= 2]
|
| 356 |
+
|
| 357 |
+
results.sort(
|
| 358 |
+
key=lambda row: (
|
| 359 |
+
-row["score"],
|
| 360 |
+
-(row["downloads"] or 0),
|
| 361 |
+
row["dataset_id"] or "",
|
| 362 |
+
)
|
| 363 |
+
)
|
| 364 |
+
return results[:limit]
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def parse_repo_id(repo_id: str) -> tuple[str, str]:
|
| 368 |
+
if "/" not in repo_id:
|
| 369 |
+
raise ValueError(f"Expected <namespace>/<repo>, got: {repo_id}")
|
| 370 |
+
namespace, repo = repo_id.split("/", 1)
|
| 371 |
+
return namespace, repo
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def get_leaderboard(repo_id: str, task_id: str | None = None) -> list[dict[str, Any]]:
|
| 375 |
+
namespace, repo = parse_repo_id(repo_id)
|
| 376 |
+
params = {"task_id": task_id} if task_id else None
|
| 377 |
+
data = http_get_json(f"/api/datasets/{namespace}/{repo}/leaderboard", params=params)
|
| 378 |
+
if not isinstance(data, list):
|
| 379 |
+
raise HfApiError(f"Unexpected leaderboard response for {repo_id}")
|
| 380 |
+
|
| 381 |
+
normalized: list[dict[str, Any]] = []
|
| 382 |
+
for row in data:
|
| 383 |
+
source = row.get("source") or {}
|
| 384 |
+
normalized.append(
|
| 385 |
+
{
|
| 386 |
+
"dataset_id": repo_id,
|
| 387 |
+
"task_id": task_id,
|
| 388 |
+
"rank": row.get("rank"),
|
| 389 |
+
"model_id": row.get("modelId"),
|
| 390 |
+
"value": row.get("value"),
|
| 391 |
+
"verified": row.get("verified"),
|
| 392 |
+
"lower_is_better": row.get("lower_is_better"),
|
| 393 |
+
"filename": row.get("filename"),
|
| 394 |
+
"notes": row.get("notes"),
|
| 395 |
+
"pull_request": row.get("pullRequest"),
|
| 396 |
+
"source_name": source.get("name"),
|
| 397 |
+
"source_url": source.get("url"),
|
| 398 |
+
"source_is_external": source.get("isExternal"),
|
| 399 |
+
}
|
| 400 |
+
)
|
| 401 |
+
return normalized
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def read_repo_ids_from_stdin() -> list[str]:
|
| 405 |
+
if sys.stdin.isatty():
|
| 406 |
+
return []
|
| 407 |
+
|
| 408 |
+
repo_ids: list[str] = []
|
| 409 |
+
for raw_line in sys.stdin:
|
| 410 |
+
line = raw_line.strip()
|
| 411 |
+
if not line:
|
| 412 |
+
continue
|
| 413 |
+
if line.startswith("{"):
|
| 414 |
+
try:
|
| 415 |
+
obj = json.loads(line)
|
| 416 |
+
except json.JSONDecodeError:
|
| 417 |
+
continue
|
| 418 |
+
candidate = obj.get("dataset_id") or obj.get("id")
|
| 419 |
+
if isinstance(candidate, str) and "/" in candidate:
|
| 420 |
+
repo_ids.append(candidate)
|
| 421 |
+
continue
|
| 422 |
+
if "/" in line:
|
| 423 |
+
repo_ids.append(line)
|
| 424 |
+
return repo_ids
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
def print_json(data: Any) -> None:
|
| 428 |
+
json.dump(data, sys.stdout, indent=2, ensure_ascii=False)
|
| 429 |
+
sys.stdout.write("\n")
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
def print_ndjson(rows: list[dict[str, Any]]) -> None:
|
| 433 |
+
for row in rows:
|
| 434 |
+
sys.stdout.write(json.dumps(row, ensure_ascii=False) + "\n")
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
def print_search_table(rows: list[dict[str, Any]]) -> None:
|
| 438 |
+
if not rows:
|
| 439 |
+
print("No benchmark datasets matched.")
|
| 440 |
+
return
|
| 441 |
+
|
| 442 |
+
headers = ["dataset_id", "score", "modalities", "tasks", "reasons", "description"]
|
| 443 |
+
widths = [34, 5, 18, 24, 30, 68]
|
| 444 |
+
print(" ".join(h.ljust(w) for h, w in zip(headers, widths)))
|
| 445 |
+
print(" ".join("-" * w for w in widths))
|
| 446 |
+
for row in rows:
|
| 447 |
+
values = [
|
| 448 |
+
shorten(row.get("dataset_id") or "", widths[0]),
|
| 449 |
+
str(row.get("score", "")),
|
| 450 |
+
shorten(", ".join(row.get("modality_tags") or []), widths[2]),
|
| 451 |
+
shorten(", ".join(row.get("task_tags") or []), widths[3]),
|
| 452 |
+
shorten(", ".join(row.get("reasons") or []), widths[4]),
|
| 453 |
+
shorten(row.get("description") or "", widths[5]),
|
| 454 |
+
]
|
| 455 |
+
print(" ".join(v.ljust(w) for v, w in zip(values, widths)))
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def print_leaderboard_table(rows: list[dict[str, Any]]) -> None:
|
| 459 |
+
if not rows:
|
| 460 |
+
print("No leaderboard rows returned.")
|
| 461 |
+
return
|
| 462 |
+
|
| 463 |
+
headers = ["dataset_id", "rank", "model_id", "value", "verified", "source"]
|
| 464 |
+
widths = [30, 5, 38, 10, 8, 28]
|
| 465 |
+
print(" ".join(h.ljust(w) for h, w in zip(headers, widths)))
|
| 466 |
+
print(" ".join("-" * w for w in widths))
|
| 467 |
+
for row in rows:
|
| 468 |
+
values = [
|
| 469 |
+
shorten(str(row.get("dataset_id") or ""), widths[0]),
|
| 470 |
+
str(row.get("rank") or ""),
|
| 471 |
+
shorten(str(row.get("model_id") or ""), widths[2]),
|
| 472 |
+
shorten(str(row.get("value") or ""), widths[3]),
|
| 473 |
+
str(row.get("verified")),
|
| 474 |
+
shorten(str(row.get("source_name") or ""), widths[5]),
|
| 475 |
+
]
|
| 476 |
+
print(" ".join(v.ljust(w) for v, w in zip(values, widths)))
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
def build_parser() -> argparse.ArgumentParser:
|
| 480 |
+
parser = FullHelpArgumentParser(
|
| 481 |
+
prog="hf_benchmarks.py",
|
| 482 |
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
| 483 |
+
description=textwrap.dedent(
|
| 484 |
+
"""
|
| 485 |
+
Search benchmark datasets and fetch leaderboard results from the Hugging Face Hub.
|
| 486 |
+
|
| 487 |
+
Workflow ideas:
|
| 488 |
+
1) Discover candidate benchmarks:
|
| 489 |
+
hf_benchmarks.py search --alias ocr
|
| 490 |
+
hf_benchmarks.py search --alias coding
|
| 491 |
+
hf_benchmarks.py search --task image-to-text --modality document
|
| 492 |
+
|
| 493 |
+
2) Inspect a leaderboard:
|
| 494 |
+
hf_benchmarks.py leaderboard allenai/olmOCR-bench --top 10
|
| 495 |
+
|
| 496 |
+
3) Chain search -> leaderboard:
|
| 497 |
+
hf_benchmarks.py search --alias coding --format ndjson \\
|
| 498 |
+
| hf_benchmarks.py leaderboard --stdin --top 5 --format table
|
| 499 |
+
"""
|
| 500 |
+
),
|
| 501 |
+
)
|
| 502 |
+
|
| 503 |
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
| 504 |
+
|
| 505 |
+
search_parser = subparsers.add_parser(
|
| 506 |
+
"search",
|
| 507 |
+
help="Search benchmark datasets by query, alias, task, and modality",
|
| 508 |
+
)
|
| 509 |
+
search_parser.add_argument(
|
| 510 |
+
"--query",
|
| 511 |
+
action="append",
|
| 512 |
+
default=[],
|
| 513 |
+
help="Free-text query to match against benchmark dataset metadata. Repeatable.",
|
| 514 |
+
)
|
| 515 |
+
search_parser.add_argument(
|
| 516 |
+
"--alias",
|
| 517 |
+
action="append",
|
| 518 |
+
default=[],
|
| 519 |
+
help=(
|
| 520 |
+
"Convenience alias for common benchmark domains. Known aliases: "
|
| 521 |
+
+ ", ".join(sorted(ALIASES))
|
| 522 |
+
+ ". Repeatable."
|
| 523 |
+
),
|
| 524 |
+
)
|
| 525 |
+
search_parser.add_argument(
|
| 526 |
+
"--task",
|
| 527 |
+
action="append",
|
| 528 |
+
default=[],
|
| 529 |
+
help="Task to match, e.g. text-generation, image-to-text, question-answering. Repeatable.",
|
| 530 |
+
)
|
| 531 |
+
search_parser.add_argument(
|
| 532 |
+
"--modality",
|
| 533 |
+
action="append",
|
| 534 |
+
default=[],
|
| 535 |
+
help="Modality to match, e.g. text, image, document, audio. Repeatable.",
|
| 536 |
+
)
|
| 537 |
+
search_parser.add_argument(
|
| 538 |
+
"--limit",
|
| 539 |
+
type=int,
|
| 540 |
+
default=20,
|
| 541 |
+
help="Maximum number of rows to print (default: 20).",
|
| 542 |
+
)
|
| 543 |
+
search_parser.add_argument(
|
| 544 |
+
"--format",
|
| 545 |
+
choices=["table", "json", "ndjson"],
|
| 546 |
+
default="table",
|
| 547 |
+
help="Output format (default: table).",
|
| 548 |
+
)
|
| 549 |
+
|
| 550 |
+
leaderboard_parser = subparsers.add_parser(
|
| 551 |
+
"leaderboard",
|
| 552 |
+
help="Fetch normalized leaderboard rows for one or more benchmark datasets",
|
| 553 |
+
)
|
| 554 |
+
leaderboard_parser.add_argument(
|
| 555 |
+
"datasets",
|
| 556 |
+
nargs="*",
|
| 557 |
+
help="Dataset repo ids (<namespace>/<repo>). Can also be supplied via stdin with --stdin.",
|
| 558 |
+
)
|
| 559 |
+
leaderboard_parser.add_argument(
|
| 560 |
+
"--stdin",
|
| 561 |
+
action="store_true",
|
| 562 |
+
help="Read dataset ids from stdin. Accepts plain repo ids or NDJSON with dataset_id/id fields.",
|
| 563 |
+
)
|
| 564 |
+
leaderboard_parser.add_argument(
|
| 565 |
+
"--task-id",
|
| 566 |
+
default=None,
|
| 567 |
+
help="Optional leaderboard task_id query parameter.",
|
| 568 |
+
)
|
| 569 |
+
leaderboard_parser.add_argument(
|
| 570 |
+
"--top",
|
| 571 |
+
type=int,
|
| 572 |
+
default=None,
|
| 573 |
+
help="Only keep the top N results per leaderboard.",
|
| 574 |
+
)
|
| 575 |
+
leaderboard_parser.add_argument(
|
| 576 |
+
"--format",
|
| 577 |
+
choices=["table", "json", "ndjson"],
|
| 578 |
+
default="table",
|
| 579 |
+
help="Output format (default: table).",
|
| 580 |
+
)
|
| 581 |
+
|
| 582 |
+
parser._search_parser = search_parser
|
| 583 |
+
parser._leaderboard_parser = leaderboard_parser
|
| 584 |
+
|
| 585 |
+
return parser
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
def run_search(args: argparse.Namespace) -> int:
|
| 589 |
+
rows = search_benchmarks(
|
| 590 |
+
queries=args.query,
|
| 591 |
+
aliases=args.alias,
|
| 592 |
+
tasks=args.task,
|
| 593 |
+
modalities=args.modality,
|
| 594 |
+
limit=args.limit,
|
| 595 |
+
)
|
| 596 |
+
|
| 597 |
+
if args.format == "json":
|
| 598 |
+
print_json(rows)
|
| 599 |
+
elif args.format == "ndjson":
|
| 600 |
+
print_ndjson(rows)
|
| 601 |
+
else:
|
| 602 |
+
print_search_table(rows)
|
| 603 |
+
return 0
|
| 604 |
+
|
| 605 |
+
|
| 606 |
+
def run_leaderboard(args: argparse.Namespace) -> int:
|
| 607 |
+
repo_ids = list(args.datasets)
|
| 608 |
+
if args.stdin:
|
| 609 |
+
repo_ids.extend(read_repo_ids_from_stdin())
|
| 610 |
+
|
| 611 |
+
deduped: list[str] = []
|
| 612 |
+
seen: set[str] = set()
|
| 613 |
+
for repo_id in repo_ids:
|
| 614 |
+
if repo_id not in seen:
|
| 615 |
+
deduped.append(repo_id)
|
| 616 |
+
seen.add(repo_id)
|
| 617 |
+
repo_ids = deduped
|
| 618 |
+
|
| 619 |
+
if not repo_ids:
|
| 620 |
+
print("Error: provide dataset ids or use --stdin.", file=sys.stderr)
|
| 621 |
+
return 2
|
| 622 |
+
|
| 623 |
+
rows: list[dict[str, Any]] = []
|
| 624 |
+
for repo_id in repo_ids:
|
| 625 |
+
dataset_rows = get_leaderboard(repo_id, task_id=args.task_id)
|
| 626 |
+
if args.top is not None:
|
| 627 |
+
dataset_rows = dataset_rows[: args.top]
|
| 628 |
+
rows.extend(dataset_rows)
|
| 629 |
+
|
| 630 |
+
if args.format == "json":
|
| 631 |
+
print_json(rows)
|
| 632 |
+
elif args.format == "ndjson":
|
| 633 |
+
print_ndjson(rows)
|
| 634 |
+
else:
|
| 635 |
+
print_leaderboard_table(rows)
|
| 636 |
+
return 0
|
| 637 |
+
|
| 638 |
+
|
| 639 |
+
def main() -> int:
|
| 640 |
+
parser = build_parser()
|
| 641 |
+
args = parser.parse_args()
|
| 642 |
+
|
| 643 |
+
try:
|
| 644 |
+
if args.command == "search":
|
| 645 |
+
return run_search(args)
|
| 646 |
+
if args.command == "leaderboard":
|
| 647 |
+
return run_leaderboard(args)
|
| 648 |
+
parser.error(f"Unknown command: {args.command}")
|
| 649 |
+
return 2
|
| 650 |
+
except HfApiError as exc:
|
| 651 |
+
print(f"Error: {exc}", file=sys.stderr)
|
| 652 |
+
return 1
|
| 653 |
+
except ValueError as exc:
|
| 654 |
+
print(f"Error: {exc}", file=sys.stderr)
|
| 655 |
+
return 1
|
| 656 |
+
|
| 657 |
+
|
| 658 |
+
if __name__ == "__main__":
|
| 659 |
+
raise SystemExit(main())
|
.agents/skills/huggingface-llm-trainer/scripts/train_dpo_example.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# /// script
|
| 3 |
+
# requires-python = ">=3.10"
|
| 4 |
+
# dependencies = [
|
| 5 |
+
# "trl>=0.12.0",
|
| 6 |
+
# "transformers>=4.36.0",
|
| 7 |
+
# "accelerate>=0.24.0",
|
| 8 |
+
# "trackio",
|
| 9 |
+
# ]
|
| 10 |
+
# ///
|
| 11 |
+
|
| 12 |
+
"""
|
| 13 |
+
Production-ready DPO training example for preference learning.
|
| 14 |
+
|
| 15 |
+
DPO (Direct Preference Optimization) trains models on preference pairs
|
| 16 |
+
(chosen vs rejected responses) without requiring a reward model.
|
| 17 |
+
|
| 18 |
+
Usage with hf_jobs MCP tool:
|
| 19 |
+
hf_jobs("uv", {
|
| 20 |
+
"script": '''<paste this entire file>''',
|
| 21 |
+
"flavor": "a10g-large",
|
| 22 |
+
"timeout": "3h",
|
| 23 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"},
|
| 24 |
+
})
|
| 25 |
+
|
| 26 |
+
Or submit the script content directly inline without saving to a file.
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
import trackio
|
| 30 |
+
from datasets import load_dataset
|
| 31 |
+
from trl import DPOTrainer, DPOConfig
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# Load preference dataset
|
| 35 |
+
print("π¦ Loading dataset...")
|
| 36 |
+
dataset = load_dataset("trl-lib/ultrafeedback_binarized", split="train")
|
| 37 |
+
print(f"β
Dataset loaded: {len(dataset)} preference pairs")
|
| 38 |
+
|
| 39 |
+
# Create train/eval split
|
| 40 |
+
print("π Creating train/eval split...")
|
| 41 |
+
dataset_split = dataset.train_test_split(test_size=0.1, seed=42)
|
| 42 |
+
train_dataset = dataset_split["train"]
|
| 43 |
+
eval_dataset = dataset_split["test"]
|
| 44 |
+
print(f" Train: {len(train_dataset)} pairs")
|
| 45 |
+
print(f" Eval: {len(eval_dataset)} pairs")
|
| 46 |
+
|
| 47 |
+
# Training configuration
|
| 48 |
+
config = DPOConfig(
|
| 49 |
+
# CRITICAL: Hub settings
|
| 50 |
+
output_dir="qwen-dpo-aligned",
|
| 51 |
+
push_to_hub=True,
|
| 52 |
+
hub_model_id="username/qwen-dpo-aligned",
|
| 53 |
+
hub_strategy="every_save",
|
| 54 |
+
|
| 55 |
+
# DPO-specific parameters
|
| 56 |
+
beta=0.1, # KL penalty coefficient (higher = stay closer to reference)
|
| 57 |
+
|
| 58 |
+
# Training parameters
|
| 59 |
+
num_train_epochs=1, # DPO typically needs fewer epochs than SFT
|
| 60 |
+
per_device_train_batch_size=4,
|
| 61 |
+
gradient_accumulation_steps=4,
|
| 62 |
+
learning_rate=5e-7, # DPO uses much lower LR than SFT
|
| 63 |
+
# max_length=1024, # Default - only set if you need different sequence length
|
| 64 |
+
|
| 65 |
+
# Logging & checkpointing
|
| 66 |
+
logging_steps=10,
|
| 67 |
+
save_strategy="steps",
|
| 68 |
+
save_steps=100,
|
| 69 |
+
save_total_limit=2,
|
| 70 |
+
|
| 71 |
+
# Evaluation - IMPORTANT: Only enable if eval_dataset provided
|
| 72 |
+
eval_strategy="steps",
|
| 73 |
+
eval_steps=100,
|
| 74 |
+
|
| 75 |
+
# Optimization
|
| 76 |
+
warmup_ratio=0.1,
|
| 77 |
+
lr_scheduler_type="cosine",
|
| 78 |
+
|
| 79 |
+
# Monitoring
|
| 80 |
+
report_to="trackio", # Integrate with Trackio
|
| 81 |
+
project="meaningful_project_name", # project name for the training name (trackio)
|
| 82 |
+
run_name="baseline-run", #Descriptive name for this training run
|
| 83 |
+
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
# Initialize and train
|
| 87 |
+
# Note: DPO requires an instruct-tuned model as the base
|
| 88 |
+
print("π― Initializing trainer...")
|
| 89 |
+
trainer = DPOTrainer(
|
| 90 |
+
model="Qwen/Qwen2.5-0.5B-Instruct", # Use instruct model, not base model
|
| 91 |
+
train_dataset=train_dataset,
|
| 92 |
+
eval_dataset=eval_dataset, # CRITICAL: Must provide eval_dataset when eval_strategy is enabled
|
| 93 |
+
args=config,
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
print("π Starting DPO training...")
|
| 97 |
+
trainer.train()
|
| 98 |
+
|
| 99 |
+
print("πΎ Pushing to Hub...")
|
| 100 |
+
trainer.push_to_hub()
|
| 101 |
+
|
| 102 |
+
# Finish Trackio tracking
|
| 103 |
+
trackio.finish()
|
| 104 |
+
|
| 105 |
+
print("β
Complete! Model at: https://huggingface.co/username/qwen-dpo-aligned")
|
| 106 |
+
print("π View metrics at: https://huggingface.co/spaces/username/trackio")
|
.agents/skills/huggingface-llm-trainer/scripts/train_grpo_example.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# /// script
|
| 3 |
+
# requires-python = ">=3.10"
|
| 4 |
+
# dependencies = [
|
| 5 |
+
# "trl>=0.12.0",
|
| 6 |
+
# "transformers>=4.36.0",
|
| 7 |
+
# "accelerate>=0.24.0",
|
| 8 |
+
# "trackio",
|
| 9 |
+
# ]
|
| 10 |
+
# ///
|
| 11 |
+
|
| 12 |
+
"""
|
| 13 |
+
Production-ready GRPO training example for online RL.
|
| 14 |
+
|
| 15 |
+
GRPO (Group Relative Policy Optimization) is an online RL method that
|
| 16 |
+
optimizes relative to group performance. Best for tasks with automatic
|
| 17 |
+
reward signals like code execution or math verification.
|
| 18 |
+
|
| 19 |
+
Usage with hf_jobs MCP tool:
|
| 20 |
+
hf_jobs("uv", {
|
| 21 |
+
"script": '''<paste this entire file>''',
|
| 22 |
+
"flavor": "a10g-large",
|
| 23 |
+
"timeout": "4h",
|
| 24 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"},
|
| 25 |
+
})
|
| 26 |
+
|
| 27 |
+
Or submit the script content directly inline without saving to a file.
|
| 28 |
+
|
| 29 |
+
Note: For most GRPO use cases, the TRL maintained script is recommended:
|
| 30 |
+
https://raw.githubusercontent.com/huggingface/trl/main/examples/scripts/grpo.py
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
import trackio
|
| 34 |
+
from datasets import load_dataset
|
| 35 |
+
from trl import GRPOTrainer, GRPOConfig
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# Load dataset (GRPO uses prompt-only format)
|
| 39 |
+
dataset = load_dataset("trl-lib/math_shepherd", split="train")
|
| 40 |
+
print(f"β
Dataset loaded: {len(dataset)} prompts")
|
| 41 |
+
|
| 42 |
+
# Training configuration
|
| 43 |
+
config = GRPOConfig(
|
| 44 |
+
# CRITICAL: Hub settings
|
| 45 |
+
output_dir="qwen-grpo-math",
|
| 46 |
+
push_to_hub=True,
|
| 47 |
+
hub_model_id="username/qwen-grpo-math",
|
| 48 |
+
hub_strategy="every_save",
|
| 49 |
+
|
| 50 |
+
# Training parameters
|
| 51 |
+
num_train_epochs=1,
|
| 52 |
+
per_device_train_batch_size=4,
|
| 53 |
+
gradient_accumulation_steps=4,
|
| 54 |
+
learning_rate=1e-6,
|
| 55 |
+
|
| 56 |
+
# Logging & checkpointing
|
| 57 |
+
logging_steps=10,
|
| 58 |
+
save_strategy="steps",
|
| 59 |
+
save_steps=100,
|
| 60 |
+
save_total_limit=2,
|
| 61 |
+
|
| 62 |
+
# Optimization
|
| 63 |
+
warmup_ratio=0.1,
|
| 64 |
+
lr_scheduler_type="cosine",
|
| 65 |
+
|
| 66 |
+
# Monitoring
|
| 67 |
+
report_to="trackio", # Integrate with Trackio
|
| 68 |
+
project="meaningful_project_name", # project name for the training name (trackio)
|
| 69 |
+
run_name="baseline-run", #Descriptive name for this training run
|
| 70 |
+
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
# Initialize and train
|
| 74 |
+
# Note: GRPO requires an instruct-tuned model as the base
|
| 75 |
+
trainer = GRPOTrainer(
|
| 76 |
+
model="Qwen/Qwen2.5-0.5B-Instruct",
|
| 77 |
+
train_dataset=dataset,
|
| 78 |
+
args=config,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
print("π Starting GRPO training...")
|
| 82 |
+
trainer.train()
|
| 83 |
+
|
| 84 |
+
print("πΎ Pushing to Hub...")
|
| 85 |
+
trainer.push_to_hub()
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
print("β
Complete! Model at: https://huggingface.co/username/qwen-grpo-math")
|
| 89 |
+
print("π View metrics at: https://huggingface.co/spaces/username/trackio")
|
.agents/skills/huggingface-llm-trainer/scripts/train_sft_example.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# /// script
|
| 3 |
+
# requires-python = ">=3.10"
|
| 4 |
+
# dependencies = [
|
| 5 |
+
# "trl>=0.12.0",
|
| 6 |
+
# "peft>=0.7.0",
|
| 7 |
+
# "transformers>=4.36.0",
|
| 8 |
+
# "accelerate>=0.24.0",
|
| 9 |
+
# "trackio",
|
| 10 |
+
# ]
|
| 11 |
+
# ///
|
| 12 |
+
|
| 13 |
+
"""
|
| 14 |
+
Production-ready SFT training example with all best practices.
|
| 15 |
+
|
| 16 |
+
This script demonstrates:
|
| 17 |
+
- Trackio integration for real-time monitoring
|
| 18 |
+
- LoRA/PEFT for efficient training
|
| 19 |
+
- Proper Hub saving configuration
|
| 20 |
+
- Train/eval split for monitoring
|
| 21 |
+
- Checkpoint management
|
| 22 |
+
- Optimized training parameters
|
| 23 |
+
|
| 24 |
+
Usage with hf_jobs MCP tool:
|
| 25 |
+
hf_jobs("uv", {
|
| 26 |
+
"script": '''<paste this entire file>''',
|
| 27 |
+
"flavor": "a10g-large",
|
| 28 |
+
"timeout": "3h",
|
| 29 |
+
"secrets": {"HF_TOKEN": "$HF_TOKEN"},
|
| 30 |
+
})
|
| 31 |
+
|
| 32 |
+
Or submit the script content directly inline without saving to a file.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
import trackio
|
| 36 |
+
from datasets import load_dataset
|
| 37 |
+
from peft import LoraConfig
|
| 38 |
+
from trl import SFTTrainer, SFTConfig
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
# Load dataset
|
| 42 |
+
print("π¦ Loading dataset...")
|
| 43 |
+
dataset = load_dataset("trl-lib/Capybara", split="train")
|
| 44 |
+
print(f"β
Dataset loaded: {len(dataset)} examples")
|
| 45 |
+
|
| 46 |
+
# Create train/eval split
|
| 47 |
+
print("π Creating train/eval split...")
|
| 48 |
+
dataset_split = dataset.train_test_split(test_size=0.1, seed=42)
|
| 49 |
+
train_dataset = dataset_split["train"]
|
| 50 |
+
eval_dataset = dataset_split["test"]
|
| 51 |
+
print(f" Train: {len(train_dataset)} examples")
|
| 52 |
+
print(f" Eval: {len(eval_dataset)} examples")
|
| 53 |
+
|
| 54 |
+
# Note: For memory-constrained demos, skip eval by using full dataset as train_dataset
|
| 55 |
+
# and removing eval_dataset, eval_strategy, and eval_steps from config below
|
| 56 |
+
|
| 57 |
+
# Training configuration
|
| 58 |
+
config = SFTConfig(
|
| 59 |
+
# CRITICAL: Hub settings
|
| 60 |
+
output_dir="qwen-capybara-sft",
|
| 61 |
+
push_to_hub=True,
|
| 62 |
+
hub_model_id="username/qwen-capybara-sft",
|
| 63 |
+
hub_strategy="every_save", # Push checkpoints
|
| 64 |
+
|
| 65 |
+
# Training parameters
|
| 66 |
+
num_train_epochs=3,
|
| 67 |
+
per_device_train_batch_size=4,
|
| 68 |
+
gradient_accumulation_steps=4,
|
| 69 |
+
learning_rate=2e-5,
|
| 70 |
+
# max_length=1024, # Default - only set if you need different sequence length
|
| 71 |
+
|
| 72 |
+
# Logging & checkpointing
|
| 73 |
+
logging_steps=10,
|
| 74 |
+
save_strategy="steps",
|
| 75 |
+
save_steps=100,
|
| 76 |
+
save_total_limit=2,
|
| 77 |
+
|
| 78 |
+
# Evaluation - IMPORTANT: Only enable if eval_dataset provided
|
| 79 |
+
eval_strategy="steps",
|
| 80 |
+
eval_steps=100,
|
| 81 |
+
|
| 82 |
+
# Optimization
|
| 83 |
+
warmup_ratio=0.1,
|
| 84 |
+
lr_scheduler_type="cosine",
|
| 85 |
+
|
| 86 |
+
# Monitoring
|
| 87 |
+
report_to="trackio", # Integrate with Trackio
|
| 88 |
+
project="meaningful_project_name", # project name for the training name (trackio)
|
| 89 |
+
run_name="baseline-run", #Descriptive name for this training run
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
# LoRA configuration
|
| 93 |
+
peft_config = LoraConfig(
|
| 94 |
+
r=16,
|
| 95 |
+
lora_alpha=32,
|
| 96 |
+
lora_dropout=0.05,
|
| 97 |
+
bias="none",
|
| 98 |
+
task_type="CAUSAL_LM",
|
| 99 |
+
target_modules=["q_proj", "v_proj"],
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
# Initialize and train
|
| 103 |
+
print("π― Initializing trainer...")
|
| 104 |
+
trainer = SFTTrainer(
|
| 105 |
+
model="Qwen/Qwen2.5-0.5B",
|
| 106 |
+
train_dataset=train_dataset,
|
| 107 |
+
eval_dataset=eval_dataset, # CRITICAL: Must provide eval_dataset when eval_strategy is enabled
|
| 108 |
+
args=config,
|
| 109 |
+
peft_config=peft_config,
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
print("π Starting training...")
|
| 113 |
+
trainer.train()
|
| 114 |
+
|
| 115 |
+
print("πΎ Pushing to Hub...")
|
| 116 |
+
trainer.push_to_hub()
|
| 117 |
+
|
| 118 |
+
# Finish Trackio tracking
|
| 119 |
+
trackio.finish()
|
| 120 |
+
|
| 121 |
+
print("β
Complete! Model at: https://huggingface.co/username/qwen-capybara-sft")
|
| 122 |
+
print("π View metrics at: https://huggingface.co/spaces/username/trackio")
|
.agents/skills/huggingface-llm-trainer/scripts/unsloth_sft_example.py
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# /// script
|
| 2 |
+
# requires-python = ">=3.10"
|
| 3 |
+
# dependencies = [
|
| 4 |
+
# "unsloth",
|
| 5 |
+
# "datasets",
|
| 6 |
+
# "trl==0.22.2",
|
| 7 |
+
# "huggingface_hub[hf_transfer]",
|
| 8 |
+
# "trackio",
|
| 9 |
+
# "tensorboard",
|
| 10 |
+
# "transformers==4.57.3",
|
| 11 |
+
# ]
|
| 12 |
+
# ///
|
| 13 |
+
"""
|
| 14 |
+
Fine-tune LLMs using Unsloth optimizations for ~60% less VRAM and 2x faster training.
|
| 15 |
+
|
| 16 |
+
Supports epoch-based or step-based training with optional eval split.
|
| 17 |
+
Default model: LFM2.5-1.2B-Instruct (Liquid Foundation Model).
|
| 18 |
+
|
| 19 |
+
Epoch-based training (recommended for full datasets):
|
| 20 |
+
uv run unsloth_sft_example.py \
|
| 21 |
+
--dataset mlabonne/FineTome-100k \
|
| 22 |
+
--num-epochs 1 \
|
| 23 |
+
--eval-split 0.2 \
|
| 24 |
+
--output-repo your-username/model-finetuned
|
| 25 |
+
|
| 26 |
+
Run on HF Jobs (1 epoch with eval):
|
| 27 |
+
hf jobs uv run unsloth_sft_example.py \
|
| 28 |
+
--flavor a10g-small --secrets HF_TOKEN --timeout 4h \
|
| 29 |
+
-- --dataset mlabonne/FineTome-100k \
|
| 30 |
+
--num-epochs 1 \
|
| 31 |
+
--eval-split 0.2 \
|
| 32 |
+
--output-repo your-username/model-finetuned
|
| 33 |
+
|
| 34 |
+
Step-based training (for quick tests):
|
| 35 |
+
uv run unsloth_sft_example.py \
|
| 36 |
+
--dataset mlabonne/FineTome-100k \
|
| 37 |
+
--max-steps 500 \
|
| 38 |
+
--output-repo your-username/model-finetuned
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
import argparse
|
| 42 |
+
import logging
|
| 43 |
+
import os
|
| 44 |
+
import sys
|
| 45 |
+
import time
|
| 46 |
+
|
| 47 |
+
# Force unbuffered output for HF Jobs logs
|
| 48 |
+
sys.stdout.reconfigure(line_buffering=True)
|
| 49 |
+
sys.stderr.reconfigure(line_buffering=True)
|
| 50 |
+
|
| 51 |
+
logging.basicConfig(
|
| 52 |
+
level=logging.INFO,
|
| 53 |
+
format="%(asctime)s - %(levelname)s - %(message)s",
|
| 54 |
+
)
|
| 55 |
+
logger = logging.getLogger(__name__)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def check_cuda():
|
| 59 |
+
"""Check CUDA availability and exit if not available."""
|
| 60 |
+
import torch
|
| 61 |
+
|
| 62 |
+
if not torch.cuda.is_available():
|
| 63 |
+
logger.error("CUDA is not available. This script requires a GPU.")
|
| 64 |
+
logger.error("Run on a machine with a CUDA-capable GPU or use HF Jobs:")
|
| 65 |
+
logger.error(
|
| 66 |
+
" hf jobs uv run unsloth_sft_example.py --flavor a10g-small ..."
|
| 67 |
+
)
|
| 68 |
+
sys.exit(1)
|
| 69 |
+
logger.info(f"CUDA available: {torch.cuda.get_device_name(0)}")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def parse_args():
|
| 73 |
+
parser = argparse.ArgumentParser(
|
| 74 |
+
description="Fine-tune LLMs with Unsloth optimizations",
|
| 75 |
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
| 76 |
+
epilog="""
|
| 77 |
+
Examples:
|
| 78 |
+
# Quick test run
|
| 79 |
+
uv run unsloth_sft_example.py \\
|
| 80 |
+
--dataset mlabonne/FineTome-100k \\
|
| 81 |
+
--max-steps 50 \\
|
| 82 |
+
--output-repo username/model-test
|
| 83 |
+
|
| 84 |
+
# Full training with eval
|
| 85 |
+
uv run unsloth_sft_example.py \\
|
| 86 |
+
--dataset mlabonne/FineTome-100k \\
|
| 87 |
+
--num-epochs 1 \\
|
| 88 |
+
--eval-split 0.2 \\
|
| 89 |
+
--output-repo username/model-finetuned
|
| 90 |
+
|
| 91 |
+
# With Trackio monitoring
|
| 92 |
+
uv run unsloth_sft_example.py \\
|
| 93 |
+
--dataset mlabonne/FineTome-100k \\
|
| 94 |
+
--num-epochs 1 \\
|
| 95 |
+
--output-repo username/model-finetuned \\
|
| 96 |
+
--trackio-space username/trackio
|
| 97 |
+
""",
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
# Model and data
|
| 101 |
+
parser.add_argument(
|
| 102 |
+
"--base-model",
|
| 103 |
+
default="LiquidAI/LFM2.5-1.2B-Instruct",
|
| 104 |
+
help="Base model (default: LiquidAI/LFM2.5-1.2B-Instruct)",
|
| 105 |
+
)
|
| 106 |
+
parser.add_argument(
|
| 107 |
+
"--dataset",
|
| 108 |
+
required=True,
|
| 109 |
+
help="Dataset in ShareGPT/conversation format (e.g., mlabonne/FineTome-100k)",
|
| 110 |
+
)
|
| 111 |
+
parser.add_argument(
|
| 112 |
+
"--output-repo",
|
| 113 |
+
required=True,
|
| 114 |
+
help="HF Hub repo to push model to (e.g., 'username/model-finetuned')",
|
| 115 |
+
)
|
| 116 |
+
|
| 117 |
+
# Training config
|
| 118 |
+
parser.add_argument(
|
| 119 |
+
"--num-epochs",
|
| 120 |
+
type=float,
|
| 121 |
+
default=None,
|
| 122 |
+
help="Number of epochs (default: None). Use instead of --max-steps.",
|
| 123 |
+
)
|
| 124 |
+
parser.add_argument(
|
| 125 |
+
"--max-steps",
|
| 126 |
+
type=int,
|
| 127 |
+
default=None,
|
| 128 |
+
help="Training steps (default: None). Use for quick tests or streaming.",
|
| 129 |
+
)
|
| 130 |
+
parser.add_argument(
|
| 131 |
+
"--batch-size",
|
| 132 |
+
type=int,
|
| 133 |
+
default=2,
|
| 134 |
+
help="Per-device batch size (default: 2)",
|
| 135 |
+
)
|
| 136 |
+
parser.add_argument(
|
| 137 |
+
"--gradient-accumulation",
|
| 138 |
+
type=int,
|
| 139 |
+
default=4,
|
| 140 |
+
help="Gradient accumulation steps (default: 4). Effective batch = batch-size * this",
|
| 141 |
+
)
|
| 142 |
+
parser.add_argument(
|
| 143 |
+
"--learning-rate",
|
| 144 |
+
type=float,
|
| 145 |
+
default=2e-4,
|
| 146 |
+
help="Learning rate (default: 2e-4)",
|
| 147 |
+
)
|
| 148 |
+
parser.add_argument(
|
| 149 |
+
"--max-seq-length",
|
| 150 |
+
type=int,
|
| 151 |
+
default=2048,
|
| 152 |
+
help="Maximum sequence length (default: 2048)",
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
# LoRA config
|
| 156 |
+
parser.add_argument(
|
| 157 |
+
"--lora-r",
|
| 158 |
+
type=int,
|
| 159 |
+
default=16,
|
| 160 |
+
help="LoRA rank (default: 16). Higher = more capacity but more VRAM",
|
| 161 |
+
)
|
| 162 |
+
parser.add_argument(
|
| 163 |
+
"--lora-alpha",
|
| 164 |
+
type=int,
|
| 165 |
+
default=16,
|
| 166 |
+
help="LoRA alpha (default: 16). Same as r per Unsloth recommendation",
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
# Logging
|
| 170 |
+
parser.add_argument(
|
| 171 |
+
"--trackio-space",
|
| 172 |
+
default=None,
|
| 173 |
+
help="HF Space for Trackio dashboard (e.g., 'username/trackio')",
|
| 174 |
+
)
|
| 175 |
+
parser.add_argument(
|
| 176 |
+
"--run-name",
|
| 177 |
+
default=None,
|
| 178 |
+
help="Custom run name for Trackio (default: auto-generated)",
|
| 179 |
+
)
|
| 180 |
+
parser.add_argument(
|
| 181 |
+
"--save-local",
|
| 182 |
+
default="unsloth-output",
|
| 183 |
+
help="Local directory to save model (default: unsloth-output)",
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
# Evaluation and data control
|
| 187 |
+
parser.add_argument(
|
| 188 |
+
"--eval-split",
|
| 189 |
+
type=float,
|
| 190 |
+
default=0.0,
|
| 191 |
+
help="Fraction of data for evaluation (0.0-0.5). Default: 0.0 (no eval)",
|
| 192 |
+
)
|
| 193 |
+
parser.add_argument(
|
| 194 |
+
"--num-samples",
|
| 195 |
+
type=int,
|
| 196 |
+
default=None,
|
| 197 |
+
help="Limit samples (default: None = use all)",
|
| 198 |
+
)
|
| 199 |
+
parser.add_argument(
|
| 200 |
+
"--seed",
|
| 201 |
+
type=int,
|
| 202 |
+
default=3407,
|
| 203 |
+
help="Random seed for reproducibility (default: 3407)",
|
| 204 |
+
)
|
| 205 |
+
parser.add_argument(
|
| 206 |
+
"--merge-model",
|
| 207 |
+
action="store_true",
|
| 208 |
+
default=False,
|
| 209 |
+
help="Merge LoRA weights into base model before uploading (larger file, easier to use)",
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
return parser.parse_args()
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def main():
|
| 216 |
+
args = parse_args()
|
| 217 |
+
|
| 218 |
+
# Validate epochs/steps configuration
|
| 219 |
+
if not args.num_epochs and not args.max_steps:
|
| 220 |
+
args.num_epochs = 1
|
| 221 |
+
logger.info("Using default --num-epochs=1")
|
| 222 |
+
|
| 223 |
+
# Determine training duration display
|
| 224 |
+
if args.num_epochs:
|
| 225 |
+
duration_str = f"{args.num_epochs} epoch(s)"
|
| 226 |
+
else:
|
| 227 |
+
duration_str = f"{args.max_steps} steps"
|
| 228 |
+
|
| 229 |
+
print("=" * 70)
|
| 230 |
+
print("LLM Fine-tuning with Unsloth")
|
| 231 |
+
print("=" * 70)
|
| 232 |
+
print("\nConfiguration:")
|
| 233 |
+
print(f" Base model: {args.base_model}")
|
| 234 |
+
print(f" Dataset: {args.dataset}")
|
| 235 |
+
print(f" Num samples: {args.num_samples or 'all'}")
|
| 236 |
+
print(f" Eval split: {args.eval_split if args.eval_split > 0 else '(disabled)'}")
|
| 237 |
+
print(f" Seed: {args.seed}")
|
| 238 |
+
print(f" Training: {duration_str}")
|
| 239 |
+
print(f" Batch size: {args.batch_size} x {args.gradient_accumulation} = {args.batch_size * args.gradient_accumulation}")
|
| 240 |
+
print(f" Learning rate: {args.learning_rate}")
|
| 241 |
+
print(f" LoRA rank: {args.lora_r}")
|
| 242 |
+
print(f" Max seq length: {args.max_seq_length}")
|
| 243 |
+
print(f" Output repo: {args.output_repo}")
|
| 244 |
+
print(f" Trackio space: {args.trackio_space or '(not configured)'}")
|
| 245 |
+
print()
|
| 246 |
+
|
| 247 |
+
# Check CUDA before heavy imports
|
| 248 |
+
check_cuda()
|
| 249 |
+
|
| 250 |
+
# Enable fast transfers
|
| 251 |
+
os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
|
| 252 |
+
|
| 253 |
+
# Set Trackio space if provided
|
| 254 |
+
if args.trackio_space:
|
| 255 |
+
os.environ["TRACKIO_SPACE_ID"] = args.trackio_space
|
| 256 |
+
logger.info(f"Trackio dashboard: https://huggingface.co/spaces/{args.trackio_space}")
|
| 257 |
+
|
| 258 |
+
# Import heavy dependencies
|
| 259 |
+
from unsloth import FastLanguageModel
|
| 260 |
+
from unsloth.chat_templates import standardize_data_formats, train_on_responses_only
|
| 261 |
+
from datasets import load_dataset
|
| 262 |
+
from trl import SFTTrainer, SFTConfig
|
| 263 |
+
from huggingface_hub import login
|
| 264 |
+
|
| 265 |
+
# Login to Hub
|
| 266 |
+
token = os.environ.get("HF_TOKEN") or os.environ.get("hfjob")
|
| 267 |
+
if token:
|
| 268 |
+
login(token=token)
|
| 269 |
+
logger.info("Logged in to Hugging Face Hub")
|
| 270 |
+
else:
|
| 271 |
+
logger.warning("HF_TOKEN not set - model upload may fail")
|
| 272 |
+
|
| 273 |
+
# 1. Load model
|
| 274 |
+
print("\n[1/5] Loading model...")
|
| 275 |
+
start = time.time()
|
| 276 |
+
|
| 277 |
+
model, tokenizer = FastLanguageModel.from_pretrained(
|
| 278 |
+
model_name=args.base_model,
|
| 279 |
+
max_seq_length=args.max_seq_length,
|
| 280 |
+
load_in_4bit=False,
|
| 281 |
+
load_in_8bit=False,
|
| 282 |
+
load_in_16bit=True,
|
| 283 |
+
full_finetuning=False,
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
# Add LoRA adapters
|
| 287 |
+
model = FastLanguageModel.get_peft_model(
|
| 288 |
+
model,
|
| 289 |
+
r=args.lora_r,
|
| 290 |
+
target_modules=["q_proj", "k_proj", "v_proj", "out_proj", "in_proj", "w1", "w2", "w3"],
|
| 291 |
+
lora_alpha=args.lora_alpha,
|
| 292 |
+
lora_dropout=0,
|
| 293 |
+
bias="none",
|
| 294 |
+
use_gradient_checkpointing="unsloth",
|
| 295 |
+
random_state=args.seed,
|
| 296 |
+
use_rslora=False,
|
| 297 |
+
loftq_config=None,
|
| 298 |
+
)
|
| 299 |
+
print(f"Model loaded in {time.time() - start:.1f}s")
|
| 300 |
+
|
| 301 |
+
# 2. Load and prepare dataset
|
| 302 |
+
print("\n[2/5] Loading dataset...")
|
| 303 |
+
start = time.time()
|
| 304 |
+
|
| 305 |
+
dataset = load_dataset(args.dataset, split="train")
|
| 306 |
+
print(f" Dataset has {len(dataset)} total samples")
|
| 307 |
+
|
| 308 |
+
if args.num_samples:
|
| 309 |
+
dataset = dataset.select(range(min(args.num_samples, len(dataset))))
|
| 310 |
+
print(f" Limited to {len(dataset)} samples")
|
| 311 |
+
|
| 312 |
+
# Auto-detect and normalize conversation column
|
| 313 |
+
for col in ["messages", "conversations", "conversation"]:
|
| 314 |
+
if col in dataset.column_names and isinstance(dataset[0][col], list):
|
| 315 |
+
if col != "conversations":
|
| 316 |
+
dataset = dataset.rename_column(col, "conversations")
|
| 317 |
+
break
|
| 318 |
+
dataset = standardize_data_formats(dataset)
|
| 319 |
+
|
| 320 |
+
# Apply chat template
|
| 321 |
+
def formatting_prompts_func(examples):
|
| 322 |
+
texts = tokenizer.apply_chat_template(
|
| 323 |
+
examples["conversations"],
|
| 324 |
+
tokenize=False,
|
| 325 |
+
add_generation_prompt=False,
|
| 326 |
+
)
|
| 327 |
+
# Remove BOS token to avoid duplicates
|
| 328 |
+
return {"text": [x.removeprefix(tokenizer.bos_token) for x in texts]}
|
| 329 |
+
|
| 330 |
+
dataset = dataset.map(formatting_prompts_func, batched=True)
|
| 331 |
+
|
| 332 |
+
# Split for evaluation if requested
|
| 333 |
+
if args.eval_split > 0:
|
| 334 |
+
split = dataset.train_test_split(test_size=args.eval_split, seed=args.seed)
|
| 335 |
+
train_data = split["train"]
|
| 336 |
+
eval_data = split["test"]
|
| 337 |
+
print(f" Train: {len(train_data)} samples, Eval: {len(eval_data)} samples")
|
| 338 |
+
else:
|
| 339 |
+
train_data = dataset
|
| 340 |
+
eval_data = None
|
| 341 |
+
|
| 342 |
+
print(f" Dataset ready in {time.time() - start:.1f}s")
|
| 343 |
+
|
| 344 |
+
# 3. Configure trainer
|
| 345 |
+
print("\n[3/5] Configuring trainer...")
|
| 346 |
+
|
| 347 |
+
# Calculate steps per epoch for logging/eval intervals
|
| 348 |
+
effective_batch = args.batch_size * args.gradient_accumulation
|
| 349 |
+
num_samples = len(train_data)
|
| 350 |
+
steps_per_epoch = num_samples // effective_batch
|
| 351 |
+
|
| 352 |
+
# Determine run name and logging steps
|
| 353 |
+
if args.run_name:
|
| 354 |
+
run_name = args.run_name
|
| 355 |
+
elif args.num_epochs:
|
| 356 |
+
run_name = f"unsloth-sft-{args.num_epochs}ep"
|
| 357 |
+
else:
|
| 358 |
+
run_name = f"unsloth-sft-{args.max_steps}steps"
|
| 359 |
+
|
| 360 |
+
if args.num_epochs:
|
| 361 |
+
logging_steps = max(1, steps_per_epoch // 10)
|
| 362 |
+
save_steps = max(1, steps_per_epoch // 4)
|
| 363 |
+
else:
|
| 364 |
+
logging_steps = max(1, args.max_steps // 20)
|
| 365 |
+
save_steps = max(1, args.max_steps // 4)
|
| 366 |
+
|
| 367 |
+
# Determine reporting backend
|
| 368 |
+
if args.trackio_space:
|
| 369 |
+
report_to = ["tensorboard", "trackio"]
|
| 370 |
+
else:
|
| 371 |
+
report_to = ["tensorboard"]
|
| 372 |
+
|
| 373 |
+
training_config = SFTConfig(
|
| 374 |
+
output_dir=args.save_local,
|
| 375 |
+
dataset_text_field="text",
|
| 376 |
+
per_device_train_batch_size=args.batch_size,
|
| 377 |
+
gradient_accumulation_steps=args.gradient_accumulation,
|
| 378 |
+
warmup_steps=5,
|
| 379 |
+
num_train_epochs=args.num_epochs if args.num_epochs else 1,
|
| 380 |
+
max_steps=args.max_steps if args.max_steps else -1,
|
| 381 |
+
learning_rate=args.learning_rate,
|
| 382 |
+
logging_steps=logging_steps,
|
| 383 |
+
optim="adamw_8bit",
|
| 384 |
+
weight_decay=0.01,
|
| 385 |
+
lr_scheduler_type="linear",
|
| 386 |
+
seed=args.seed,
|
| 387 |
+
max_length=args.max_seq_length,
|
| 388 |
+
report_to=report_to,
|
| 389 |
+
run_name=run_name,
|
| 390 |
+
push_to_hub=True,
|
| 391 |
+
hub_model_id=args.output_repo,
|
| 392 |
+
save_steps=save_steps,
|
| 393 |
+
save_total_limit=3,
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
# Add evaluation config if eval is enabled
|
| 397 |
+
if eval_data:
|
| 398 |
+
if args.num_epochs:
|
| 399 |
+
training_config.eval_strategy = "epoch"
|
| 400 |
+
print(" Evaluation enabled: every epoch")
|
| 401 |
+
else:
|
| 402 |
+
training_config.eval_strategy = "steps"
|
| 403 |
+
training_config.eval_steps = max(1, args.max_steps // 5)
|
| 404 |
+
print(f" Evaluation enabled: every {training_config.eval_steps} steps")
|
| 405 |
+
|
| 406 |
+
trainer = SFTTrainer(
|
| 407 |
+
model=model,
|
| 408 |
+
tokenizer=tokenizer,
|
| 409 |
+
train_dataset=train_data,
|
| 410 |
+
eval_dataset=eval_data,
|
| 411 |
+
args=training_config,
|
| 412 |
+
)
|
| 413 |
+
|
| 414 |
+
# Train on responses only (mask user inputs)
|
| 415 |
+
trainer = train_on_responses_only(
|
| 416 |
+
trainer,
|
| 417 |
+
instruction_part="<|im_start|>user\n",
|
| 418 |
+
response_part="<|im_start|>assistant\n",
|
| 419 |
+
)
|
| 420 |
+
|
| 421 |
+
# 4. Train
|
| 422 |
+
print(f"\n[4/5] Training for {duration_str}...")
|
| 423 |
+
if args.num_epochs:
|
| 424 |
+
print(f" (~{steps_per_epoch} steps/epoch, {int(steps_per_epoch * args.num_epochs)} total steps)")
|
| 425 |
+
start = time.time()
|
| 426 |
+
|
| 427 |
+
train_result = trainer.train()
|
| 428 |
+
|
| 429 |
+
train_time = time.time() - start
|
| 430 |
+
total_steps = train_result.metrics.get("train_steps", args.max_steps or steps_per_epoch * args.num_epochs)
|
| 431 |
+
print(f"\nTraining completed in {train_time / 60:.1f} minutes")
|
| 432 |
+
print(f" Speed: {total_steps / train_time:.2f} steps/s")
|
| 433 |
+
|
| 434 |
+
# Print training metrics
|
| 435 |
+
train_loss = train_result.metrics.get("train_loss")
|
| 436 |
+
if train_loss:
|
| 437 |
+
print(f" Final train loss: {train_loss:.4f}")
|
| 438 |
+
|
| 439 |
+
# Print eval results if eval was enabled
|
| 440 |
+
if eval_data:
|
| 441 |
+
print("\nRunning final evaluation...")
|
| 442 |
+
try:
|
| 443 |
+
eval_results = trainer.evaluate()
|
| 444 |
+
eval_loss = eval_results.get("eval_loss")
|
| 445 |
+
if eval_loss:
|
| 446 |
+
print(f" Final eval loss: {eval_loss:.4f}")
|
| 447 |
+
if train_loss:
|
| 448 |
+
ratio = eval_loss / train_loss
|
| 449 |
+
if ratio > 1.5:
|
| 450 |
+
print(f" Warning: Eval loss is {ratio:.1f}x train loss - possible overfitting")
|
| 451 |
+
else:
|
| 452 |
+
print(f" Eval/train ratio: {ratio:.2f} - model generalizes well")
|
| 453 |
+
except Exception as e:
|
| 454 |
+
print(f" Warning: Final evaluation failed: {e}")
|
| 455 |
+
print(" Continuing to save model...")
|
| 456 |
+
|
| 457 |
+
# 5. Save and push
|
| 458 |
+
print("\n[5/5] Saving model...")
|
| 459 |
+
|
| 460 |
+
if args.merge_model:
|
| 461 |
+
print("Merging LoRA weights into base model...")
|
| 462 |
+
print(f"\nPushing merged model to {args.output_repo}...")
|
| 463 |
+
model.push_to_hub_merged(
|
| 464 |
+
args.output_repo,
|
| 465 |
+
tokenizer=tokenizer,
|
| 466 |
+
save_method="merged_16bit",
|
| 467 |
+
)
|
| 468 |
+
print(f"Merged model available at: https://huggingface.co/{args.output_repo}")
|
| 469 |
+
else:
|
| 470 |
+
model.save_pretrained(args.save_local)
|
| 471 |
+
tokenizer.save_pretrained(args.save_local)
|
| 472 |
+
print(f"Saved locally to {args.save_local}/")
|
| 473 |
+
|
| 474 |
+
print(f"\nPushing adapter to {args.output_repo}...")
|
| 475 |
+
model.push_to_hub(args.output_repo, tokenizer=tokenizer)
|
| 476 |
+
print(f"Adapter available at: https://huggingface.co/{args.output_repo}")
|
| 477 |
+
|
| 478 |
+
print("\n" + "=" * 70)
|
| 479 |
+
print("Done!")
|
| 480 |
+
print("=" * 70)
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
if __name__ == "__main__":
|
| 484 |
+
if len(sys.argv) == 1:
|
| 485 |
+
print("=" * 70)
|
| 486 |
+
print("LLM Fine-tuning with Unsloth")
|
| 487 |
+
print("=" * 70)
|
| 488 |
+
print("\nFine-tune language models with optional train/eval split.")
|
| 489 |
+
print("\nFeatures:")
|
| 490 |
+
print(" - ~60% less VRAM with Unsloth optimizations")
|
| 491 |
+
print(" - 2x faster training vs standard methods")
|
| 492 |
+
print(" - Epoch-based or step-based training")
|
| 493 |
+
print(" - Optional evaluation to detect overfitting")
|
| 494 |
+
print(" - Trains only on assistant responses (masked user inputs)")
|
| 495 |
+
print("\nEpoch-based training:")
|
| 496 |
+
print("\n uv run unsloth_sft_example.py \\")
|
| 497 |
+
print(" --dataset mlabonne/FineTome-100k \\")
|
| 498 |
+
print(" --num-epochs 1 \\")
|
| 499 |
+
print(" --eval-split 0.2 \\")
|
| 500 |
+
print(" --output-repo your-username/model-finetuned")
|
| 501 |
+
print("\nHF Jobs example:")
|
| 502 |
+
print("\n hf jobs uv run unsloth_sft_example.py \\")
|
| 503 |
+
print(" --flavor a10g-small --secrets HF_TOKEN --timeout 4h \\")
|
| 504 |
+
print(" -- --dataset mlabonne/FineTome-100k \\")
|
| 505 |
+
print(" --num-epochs 1 \\")
|
| 506 |
+
print(" --eval-split 0.2 \\")
|
| 507 |
+
print(" --output-repo your-username/model-finetuned")
|
| 508 |
+
print("\nFor full help: uv run unsloth_sft_example.py --help")
|
| 509 |
+
print("=" * 70)
|
| 510 |
+
sys.exit(0)
|
| 511 |
+
|
| 512 |
+
main()
|
.agents/skills/huggingface-local-models/SKILL.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: huggingface-local-models
|
| 3 |
+
description: "Use to select models to run locally with llama.cpp and GGUF on CPU, Mac Metal, CUDA, or ROCm. Covers finding GGUFs, quant selection, running servers, exact GGUF file lookup, conversion, and OpenAI-compatible local serving."
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Hugging Face Local Models
|
| 7 |
+
|
| 8 |
+
Search the Hugging Face Hub for llama.cpp-compatible GGUF repos, choose the right quant, and launch the model with `llama-cli` or `llama-server`.
|
| 9 |
+
|
| 10 |
+
## Default Workflow
|
| 11 |
+
|
| 12 |
+
1. Search the Hub with `apps=llama.cpp`.
|
| 13 |
+
2. Open `https://huggingface.co/<repo>?local-app=llama.cpp`.
|
| 14 |
+
3. Prefer the exact HF local-app snippet and quant recommendation when it is visible.
|
| 15 |
+
4. Confirm exact `.gguf` filenames with `https://huggingface.co/api/models/<repo>/tree/main?recursive=true`.
|
| 16 |
+
5. Launch with `llama-cli -hf <repo>:<QUANT>` or `llama-server -hf <repo>:<QUANT>`.
|
| 17 |
+
6. Fall back to `--hf-repo` plus `--hf-file` when the repo uses custom file naming.
|
| 18 |
+
7. Convert from Transformers weights only if the repo does not already expose GGUF files.
|
| 19 |
+
|
| 20 |
+
## Quick Start
|
| 21 |
+
|
| 22 |
+
### Install llama.cpp
|
| 23 |
+
|
| 24 |
+
```bash
|
| 25 |
+
brew install llama.cpp
|
| 26 |
+
winget install llama.cpp
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
```bash
|
| 30 |
+
git clone https://github.com/ggml-org/llama.cpp
|
| 31 |
+
cd llama.cpp
|
| 32 |
+
make
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
### Authenticate for gated repos
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
hf auth login
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
### Search the Hub
|
| 42 |
+
|
| 43 |
+
```text
|
| 44 |
+
https://huggingface.co/models?apps=llama.cpp&sort=trending
|
| 45 |
+
https://huggingface.co/models?search=Qwen3.6&apps=llama.cpp&sort=trending
|
| 46 |
+
https://huggingface.co/models?search=<term>&apps=llama.cpp&num_parameters=min:0,max:24B&sort=trending
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
### Run directly from the Hub
|
| 50 |
+
|
| 51 |
+
```bash
|
| 52 |
+
llama-cli -hf unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M
|
| 53 |
+
llama-server -hf unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
### Run an exact GGUF file
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
llama-server \
|
| 60 |
+
--hf-repo unsloth/Qwen3.6-35B-A3B-GGUF \
|
| 61 |
+
--hf-file Qwen3.6-35B-A3B-UD-Q4_K_M.gguf \
|
| 62 |
+
-c 4096
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
### Convert only when no GGUF is available
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
hf download <repo-without-gguf> --local-dir ./model-src
|
| 69 |
+
python convert_hf_to_gguf.py ./model-src \
|
| 70 |
+
--outfile model-f16.gguf \
|
| 71 |
+
--outtype f16
|
| 72 |
+
llama-quantize model-f16.gguf model-q4_k_m.gguf Q4_K_M
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
### Smoke test a local server
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
llama-server -hf unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
```bash
|
| 82 |
+
curl http://localhost:8080/v1/chat/completions \
|
| 83 |
+
-H "Content-Type: application/json" \
|
| 84 |
+
-H "Authorization: Bearer no-key" \
|
| 85 |
+
-d '{
|
| 86 |
+
"messages": [
|
| 87 |
+
{"role": "user", "content": "Write a limerick about exception handling"}
|
| 88 |
+
]
|
| 89 |
+
}'
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
## Quant Choice
|
| 93 |
+
|
| 94 |
+
- Prefer the exact quant that HF marks as compatible on the `?local-app=llama.cpp` page.
|
| 95 |
+
- Keep repo-native labels such as `UD-Q4_K_M` instead of normalizing them.
|
| 96 |
+
- Default to `Q4_K_M` unless the repo page or hardware profile suggests otherwise.
|
| 97 |
+
- Prefer `Q5_K_M` or `Q6_K` for code or technical workloads when memory allows.
|
| 98 |
+
- Consider `Q3_K_M`, `Q4_K_S`, or repo-specific `IQ` / `UD-*` variants for tighter RAM or VRAM budgets.
|
| 99 |
+
- Treat `mmproj-*.gguf` files as projector weights, not the main checkpoint.
|
| 100 |
+
|
| 101 |
+
## Load References
|
| 102 |
+
|
| 103 |
+
- Read [hub-discovery.md](references/hub-discovery.md) for URL-first workflows, model search, tree API extraction, and command reconstruction.
|
| 104 |
+
- Read [quantization.md](references/quantization.md) for format tables, model scaling, quality tradeoffs, and `imatrix`.
|
| 105 |
+
- Read [hardware.md](references/hardware.md) for Metal, CUDA, ROCm, or CPU build and acceleration details.
|
| 106 |
+
|
| 107 |
+
## Resources
|
| 108 |
+
|
| 109 |
+
- llama.cpp: `https://github.com/ggml-org/llama.cpp`
|
| 110 |
+
- Hugging Face GGUF + llama.cpp docs: `https://huggingface.co/docs/hub/gguf-llamacpp`
|
| 111 |
+
- Hugging Face Local Apps docs: `https://huggingface.co/docs/hub/main/local-apps`
|
| 112 |
+
- Hugging Face Local Agents docs: `https://huggingface.co/docs/hub/agents-local`
|
| 113 |
+
- GGUF converter Space: `https://huggingface.co/spaces/ggml-org/gguf-my-repo`
|
.agents/skills/huggingface-local-models/references/hardware.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Hardware Acceleration
|
| 2 |
+
|
| 3 |
+
### Apple Silicon (Metal)
|
| 4 |
+
|
| 5 |
+
```bash
|
| 6 |
+
make clean && make GGML_METAL=1
|
| 7 |
+
llama-cli -m model.gguf -ngl 99 -p "Hello"
|
| 8 |
+
```
|
| 9 |
+
|
| 10 |
+
### NVIDIA (CUDA)
|
| 11 |
+
|
| 12 |
+
```bash
|
| 13 |
+
make clean && make GGML_CUDA=1
|
| 14 |
+
llama-cli -m model.gguf -ngl 35 -p "Hello"
|
| 15 |
+
|
| 16 |
+
# Hybrid for large models
|
| 17 |
+
llama-cli -m llama-70b.Q4_K_M.gguf -ngl 20
|
| 18 |
+
|
| 19 |
+
# Multi-GPU split
|
| 20 |
+
llama-cli -m large-model.gguf --tensor-split 0.5,0.5 -ngl 60
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
### AMD (ROCm)
|
| 24 |
+
|
| 25 |
+
```bash
|
| 26 |
+
make LLAMA_HIP=1
|
| 27 |
+
llama-cli -m model.gguf -ngl 999
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
### CPU
|
| 31 |
+
|
| 32 |
+
```bash
|
| 33 |
+
# Match physical cores, not logical threads
|
| 34 |
+
llama-cli -m model.gguf -t 8 -p "Hello"
|
| 35 |
+
|
| 36 |
+
# BLAS acceleration
|
| 37 |
+
make LLAMA_OPENBLAS=1
|
| 38 |
+
```
|
.agents/skills/huggingface-local-models/references/hub-discovery.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face URL Workflows for llama.cpp
|
| 2 |
+
|
| 3 |
+
Use URL-only workflows first. Do not require `hf` or API clients just to find GGUF files, choose a quant, or build a `llama-server` command.
|
| 4 |
+
|
| 5 |
+
## Contents
|
| 6 |
+
|
| 7 |
+
- Core URLs
|
| 8 |
+
- Search for llama.cpp-compatible models
|
| 9 |
+
- Use the local-app page for the recommended quant
|
| 10 |
+
- Confirm exact files from the tree API
|
| 11 |
+
- Build the command
|
| 12 |
+
- Example: `unsloth/Qwen3.6-35B-A3B-GGUF`
|
| 13 |
+
- Notes
|
| 14 |
+
|
| 15 |
+
## Core URLs
|
| 16 |
+
|
| 17 |
+
```text
|
| 18 |
+
Search:
|
| 19 |
+
https://huggingface.co/models?apps=llama.cpp&sort=trending
|
| 20 |
+
|
| 21 |
+
Search with text:
|
| 22 |
+
https://huggingface.co/models?search=<term>&apps=llama.cpp&sort=trending
|
| 23 |
+
|
| 24 |
+
Search with size bounds:
|
| 25 |
+
https://huggingface.co/models?search=<term>&apps=llama.cpp&num_parameters=min:0,max:24B&sort=trending
|
| 26 |
+
|
| 27 |
+
Repo local-app view:
|
| 28 |
+
https://huggingface.co/<repo>?local-app=llama.cpp
|
| 29 |
+
|
| 30 |
+
Repo tree API:
|
| 31 |
+
https://huggingface.co/api/models/<repo>/tree/main?recursive=true
|
| 32 |
+
|
| 33 |
+
Repo file tree:
|
| 34 |
+
https://huggingface.co/<repo>/tree/main
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
## 1. Search for llama.cpp-compatible models
|
| 38 |
+
|
| 39 |
+
Start from the models page with `apps=llama.cpp`.
|
| 40 |
+
|
| 41 |
+
Use:
|
| 42 |
+
|
| 43 |
+
- `search=<term>` for model family names such as `Qwen`, `Gemma`, `Phi`, or `Mistral`
|
| 44 |
+
- `num_parameters=min:0,max:24B` or similar if the user has hardware limits
|
| 45 |
+
- `sort=trending` when the user wants popular repos right now
|
| 46 |
+
|
| 47 |
+
Do not start with random GGUF repos if the user has not chosen a model family yet. Search first, shortlist second.
|
| 48 |
+
|
| 49 |
+
Example: https://huggingface.co/models?search=Qwen&apps=llama.cpp&num_parameters=min:0,max:24B&sort=trending
|
| 50 |
+
|
| 51 |
+
## 2. Use the local-app page for the recommended quant
|
| 52 |
+
|
| 53 |
+
Open:
|
| 54 |
+
|
| 55 |
+
```text
|
| 56 |
+
https://huggingface.co/<repo>?local-app=llama.cpp
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
Extract, in order:
|
| 60 |
+
|
| 61 |
+
1. The exact `Use this model` snippet, if it is visible as text
|
| 62 |
+
2. The `Hardware compatibility` section from the fetched page text or HTML:
|
| 63 |
+
- quant label
|
| 64 |
+
- file size
|
| 65 |
+
- bit-depth grouping
|
| 66 |
+
3. Any extra launch flags shown in the snippet, such as `--jinja`
|
| 67 |
+
|
| 68 |
+
Treat the HF local-app snippet as the source of truth when it is visible.
|
| 69 |
+
|
| 70 |
+
Do this by reading the URL itself, not by assuming the UI rendered in a browser. If the fetched page source does not expose `Hardware compatibility`, say that the section was not text-visible and fall back to the tree API plus generic guidance from `quantization.md`.
|
| 71 |
+
|
| 72 |
+
## 3. Confirm exact files from the tree API
|
| 73 |
+
|
| 74 |
+
Open:
|
| 75 |
+
|
| 76 |
+
```text
|
| 77 |
+
https://huggingface.co/api/models/<repo>/tree/main?recursive=true
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
Treat the JSON response as the source of truth for repo inventory.
|
| 81 |
+
|
| 82 |
+
Keep entries where:
|
| 83 |
+
|
| 84 |
+
- `type` is `file`
|
| 85 |
+
- `path` ends with `.gguf`
|
| 86 |
+
|
| 87 |
+
Use these fields:
|
| 88 |
+
|
| 89 |
+
- `path` for the filename and subdirectory
|
| 90 |
+
- `size` for the byte size
|
| 91 |
+
- optionally `lfs.size` to confirm the LFS payload size
|
| 92 |
+
|
| 93 |
+
Separate files into:
|
| 94 |
+
|
| 95 |
+
- quantized single-file checkpoints, for example `Qwen3.6-35B-A3B-UD-Q4_K_M.gguf`
|
| 96 |
+
- projector weights, usually `mmproj-*.gguf`
|
| 97 |
+
- BF16 shard files, usually under `BF16/`
|
| 98 |
+
- everything else
|
| 99 |
+
|
| 100 |
+
Ignore unless the user asks:
|
| 101 |
+
|
| 102 |
+
- `README.md`
|
| 103 |
+
- imatrix or calibration blobs
|
| 104 |
+
|
| 105 |
+
Use `https://huggingface.co/<repo>/tree/main` only as a human fallback if the API endpoint fails or the user wants the web view.
|
| 106 |
+
|
| 107 |
+
## 4. Build the command
|
| 108 |
+
|
| 109 |
+
Preferred order:
|
| 110 |
+
|
| 111 |
+
1. Copy the exact HF snippet from the local-app page
|
| 112 |
+
2. If the page gives a clean quant label, use shorthand selection:
|
| 113 |
+
|
| 114 |
+
```bash
|
| 115 |
+
llama-server -hf <repo>:<QUANT>
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
3. If you need an exact file from the tree API, use the file-specific form:
|
| 119 |
+
|
| 120 |
+
```bash
|
| 121 |
+
llama-server --hf-repo <repo> --hf-file <filename.gguf>
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
4. For CLI usage instead of a server, use:
|
| 125 |
+
|
| 126 |
+
```bash
|
| 127 |
+
llama-cli -hf <repo>:<QUANT>
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
Use the exact-file form when the repo uses custom labels or nonstandard naming that could make `:<QUANT>` ambiguous.
|
| 131 |
+
|
| 132 |
+
## 5. Example: `unsloth/Qwen3.6-35B-A3B-GGUF`
|
| 133 |
+
|
| 134 |
+
Use these URLs:
|
| 135 |
+
|
| 136 |
+
```text
|
| 137 |
+
https://huggingface.co/unsloth/Qwen3.6-35B-A3B-GGUF?local-app=llama.cpp
|
| 138 |
+
https://huggingface.co/api/models/unsloth/Qwen3.6-35B-A3B-GGUF/tree/main?recursive=true
|
| 139 |
+
https://huggingface.co/unsloth/Qwen3.6-35B-A3B-GGUF/tree/main
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
On the local-app page, the hardware compatibility section can expose entries such as:
|
| 143 |
+
|
| 144 |
+
- `UD-IQ4_XS` - 17.7 GB
|
| 145 |
+
- `UD-Q4_K_S` - 20.9 GB
|
| 146 |
+
- `UD-Q4_K_M` - 22.1 GB
|
| 147 |
+
- `UD-Q5_K_M` - 26.5 GB
|
| 148 |
+
- `UD-Q6_K` - 29.3 GB
|
| 149 |
+
- `Q8_0` - 36.9 GB
|
| 150 |
+
|
| 151 |
+
On the tree API, you can confirm exact filenames such as:
|
| 152 |
+
|
| 153 |
+
- `Qwen3.6-35B-A3B-UD-Q4_K_M.gguf`
|
| 154 |
+
- `Qwen3.6-35B-A3B-UD-Q5_K_M.gguf`
|
| 155 |
+
- `Qwen3.6-35B-A3B-UD-Q6_K.gguf`
|
| 156 |
+
- `Qwen3.6-35B-A3B-Q8_0.gguf`
|
| 157 |
+
- `mmproj-F16.gguf`
|
| 158 |
+
|
| 159 |
+
Good final output for this repo:
|
| 160 |
+
|
| 161 |
+
```text
|
| 162 |
+
Repo: unsloth/Qwen3.6-35B-A3B-GGUF
|
| 163 |
+
Recommended quant from HF: UD-Q4_K_M (22.1 GB)
|
| 164 |
+
llama-server: llama-server --hf-repo unsloth/Qwen3.6-35B-A3B-GGUF --hf-file Qwen3.6-35B-A3B-UD-Q4_K_M.gguf
|
| 165 |
+
Other GGUFs:
|
| 166 |
+
- Qwen3.6-35B-A3B-UD-Q5_K_M.gguf - 26.5 GB
|
| 167 |
+
- Qwen3.6-35B-A3B-UD-Q6_K.gguf - 29.3 GB
|
| 168 |
+
- Qwen3.6-35B-A3B-Q8_0.gguf - 36.9 GB
|
| 169 |
+
Projector:
|
| 170 |
+
- mmproj-F16.gguf - 899 MB
|
| 171 |
+
```
|
| 172 |
+
|
| 173 |
+
## Notes
|
| 174 |
+
|
| 175 |
+
- Repo-specific quant labels matter. Do not rewrite `UD-Q4_K_M` to `Q4_K_M` unless the page itself does.
|
| 176 |
+
- `mmproj` files are projector weights for multimodal models, not the main language model checkpoint.
|
| 177 |
+
- If the HF hardware compatibility panel is missing because the user has no hardware profile configured, or because the fetched page source did not expose it, still use the tree API plus generic quant guidance from `quantization.md`.
|
| 178 |
+
- If the repo already has GGUFs, do not jump straight to conversion workflows.
|
.agents/skills/huggingface-local-models/references/quantization.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GGUF Quantization Guide
|
| 2 |
+
|
| 3 |
+
Complete guide to GGUF quantization formats and model conversion.
|
| 4 |
+
|
| 5 |
+
## Contents
|
| 6 |
+
|
| 7 |
+
- Hub-first quant selection
|
| 8 |
+
- Quantization Formats
|
| 9 |
+
- Converting Models
|
| 10 |
+
- K-Quantization Methods
|
| 11 |
+
- Quality Testing
|
| 12 |
+
- Use Case Guide
|
| 13 |
+
- Model Size Scaling
|
| 14 |
+
- Finding Pre-Quantized Models
|
| 15 |
+
- Importance Matrices (`imatrix`)
|
| 16 |
+
- Troubleshooting
|
| 17 |
+
|
| 18 |
+
## Hub-first quant selection
|
| 19 |
+
|
| 20 |
+
Before using generic tables, open the model repo with:
|
| 21 |
+
|
| 22 |
+
```text
|
| 23 |
+
https://huggingface.co/<repo>?local-app=llama.cpp
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
Prefer the exact quant labels and sizes shown in the `Hardware compatibility` section of the fetched `?local-app=llama.cpp` page text or HTML. Then confirm the matching filenames in:
|
| 27 |
+
|
| 28 |
+
```text
|
| 29 |
+
https://huggingface.co/api/models/<repo>/tree/main?recursive=true
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
Use the Hub page first, and only fall back to the generic heuristics below when the repo page does not expose a clear recommendation.
|
| 33 |
+
|
| 34 |
+
## Quantization Formats
|
| 35 |
+
|
| 36 |
+
**GGUF** (GPT-Generated Unified Format) - Standard format for llama.cpp models.
|
| 37 |
+
|
| 38 |
+
### Format Comparison
|
| 39 |
+
|
| 40 |
+
| Format | Perplexity | Size (7B) | Tokens/sec | Notes |
|
| 41 |
+
|--------|------------|-----------|------------|-------|
|
| 42 |
+
| FP16 | 5.9565 (baseline) | 13.0 GB | 15 tok/s | Original quality |
|
| 43 |
+
| Q8_0 | 5.9584 (+0.03%) | 7.0 GB | 25 tok/s | Nearly lossless |
|
| 44 |
+
| **Q6_K** | 5.9642 (+0.13%) | 5.5 GB | 30 tok/s | Best quality/size |
|
| 45 |
+
| **Q5_K_M** | 5.9796 (+0.39%) | 4.8 GB | 35 tok/s | Balanced |
|
| 46 |
+
| **Q4_K_M** | 6.0565 (+1.68%) | 4.1 GB | 40 tok/s | **Recommended** |
|
| 47 |
+
| Q4_K_S | 6.1125 (+2.62%) | 3.9 GB | 42 tok/s | Faster, lower quality |
|
| 48 |
+
| Q3_K_M | 6.3184 (+6.07%) | 3.3 GB | 45 tok/s | Small models only |
|
| 49 |
+
| Q2_K | 6.8673 (+15.3%) | 2.7 GB | 50 tok/s | Not recommended |
|
| 50 |
+
|
| 51 |
+
**Recommendation**: Use **Q4_K_M** for best balance of quality and speed.
|
| 52 |
+
|
| 53 |
+
## Converting Models
|
| 54 |
+
|
| 55 |
+
### Hugging Face to GGUF
|
| 56 |
+
|
| 57 |
+
```bash
|
| 58 |
+
# 1. Download Hugging Face model
|
| 59 |
+
hf download meta-llama/Llama-2-7b-chat-hf \
|
| 60 |
+
--local-dir models/llama-2-7b-chat/
|
| 61 |
+
|
| 62 |
+
# 2. Convert to FP16 GGUF
|
| 63 |
+
python convert_hf_to_gguf.py \
|
| 64 |
+
models/llama-2-7b-chat/ \
|
| 65 |
+
--outtype f16 \
|
| 66 |
+
--outfile models/llama-2-7b-chat-f16.gguf
|
| 67 |
+
|
| 68 |
+
# 3. Quantize to Q4_K_M
|
| 69 |
+
./llama-quantize \
|
| 70 |
+
models/llama-2-7b-chat-f16.gguf \
|
| 71 |
+
models/llama-2-7b-chat-Q4_K_M.gguf \
|
| 72 |
+
Q4_K_M
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
### Batch quantization
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
# Quantize to multiple formats
|
| 79 |
+
for quant in Q4_K_M Q5_K_M Q6_K Q8_0; do
|
| 80 |
+
./llama-quantize \
|
| 81 |
+
model-f16.gguf \
|
| 82 |
+
model-${quant}.gguf \
|
| 83 |
+
$quant
|
| 84 |
+
done
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
## K-Quantization Methods
|
| 88 |
+
|
| 89 |
+
**K-quants** use mixed precision for better quality:
|
| 90 |
+
- Attention weights: Higher precision
|
| 91 |
+
- Feed-forward weights: Lower precision
|
| 92 |
+
|
| 93 |
+
**Variants**:
|
| 94 |
+
- `_S` (Small): Faster, lower quality
|
| 95 |
+
- `_M` (Medium): Balanced (recommended)
|
| 96 |
+
- `_L` (Large): Better quality, larger size
|
| 97 |
+
|
| 98 |
+
**Example**: `Q4_K_M`
|
| 99 |
+
- `Q4`: 4-bit quantization
|
| 100 |
+
- `K`: Mixed precision method
|
| 101 |
+
- `M`: Medium quality
|
| 102 |
+
|
| 103 |
+
## Quality Testing
|
| 104 |
+
|
| 105 |
+
```bash
|
| 106 |
+
# Calculate perplexity (quality metric)
|
| 107 |
+
./llama-perplexity \
|
| 108 |
+
-m model.gguf \
|
| 109 |
+
-f wikitext-2-raw/wiki.test.raw \
|
| 110 |
+
-c 512
|
| 111 |
+
|
| 112 |
+
# Lower perplexity = better quality
|
| 113 |
+
# Baseline (FP16): ~5.96
|
| 114 |
+
# Q4_K_M: ~6.06 (+1.7%)
|
| 115 |
+
# Q2_K: ~6.87 (+15.3% - too much degradation)
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
## Use Case Guide
|
| 119 |
+
|
| 120 |
+
### General purpose (chatbots, assistants)
|
| 121 |
+
```
|
| 122 |
+
Q4_K_M - Best balance
|
| 123 |
+
Q5_K_M - If you have extra RAM
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
### Code generation
|
| 127 |
+
```
|
| 128 |
+
Q5_K_M or Q6_K - Higher precision helps with code
|
| 129 |
+
```
|
| 130 |
+
|
| 131 |
+
### Creative writing
|
| 132 |
+
```
|
| 133 |
+
Q4_K_M - Sufficient quality
|
| 134 |
+
Q3_K_M - Acceptable for draft generation
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
### Technical/medical
|
| 138 |
+
```
|
| 139 |
+
Q6_K or Q8_0 - Maximum accuracy
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
### Edge devices (Raspberry Pi)
|
| 143 |
+
```
|
| 144 |
+
Q2_K or Q3_K_S - Fit in limited RAM
|
| 145 |
+
```
|
| 146 |
+
|
| 147 |
+
## Model Size Scaling
|
| 148 |
+
|
| 149 |
+
### 7B parameter models
|
| 150 |
+
|
| 151 |
+
| Format | Size | RAM needed |
|
| 152 |
+
|--------|------|------------|
|
| 153 |
+
| Q2_K | 2.7 GB | 5 GB |
|
| 154 |
+
| Q3_K_M | 3.3 GB | 6 GB |
|
| 155 |
+
| Q4_K_M | 4.1 GB | 7 GB |
|
| 156 |
+
| Q5_K_M | 4.8 GB | 8 GB |
|
| 157 |
+
| Q6_K | 5.5 GB | 9 GB |
|
| 158 |
+
| Q8_0 | 7.0 GB | 11 GB |
|
| 159 |
+
|
| 160 |
+
### 13B parameter models
|
| 161 |
+
|
| 162 |
+
| Format | Size | RAM needed |
|
| 163 |
+
|--------|------|------------|
|
| 164 |
+
| Q2_K | 5.1 GB | 8 GB |
|
| 165 |
+
| Q3_K_M | 6.2 GB | 10 GB |
|
| 166 |
+
| Q4_K_M | 7.9 GB | 12 GB |
|
| 167 |
+
| Q5_K_M | 9.2 GB | 14 GB |
|
| 168 |
+
| Q6_K | 10.7 GB | 16 GB |
|
| 169 |
+
|
| 170 |
+
### 70B parameter models
|
| 171 |
+
|
| 172 |
+
| Format | Size | RAM needed |
|
| 173 |
+
|--------|------|------------|
|
| 174 |
+
| Q2_K | 26 GB | 32 GB |
|
| 175 |
+
| Q3_K_M | 32 GB | 40 GB |
|
| 176 |
+
| Q4_K_M | 41 GB | 48 GB |
|
| 177 |
+
| Q4_K_S | 39 GB | 46 GB |
|
| 178 |
+
| Q5_K_M | 48 GB | 56 GB |
|
| 179 |
+
|
| 180 |
+
**Recommendation for 70B**: Use Q3_K_M or Q4_K_S to fit in consumer hardware.
|
| 181 |
+
|
| 182 |
+
## Finding Pre-Quantized Models
|
| 183 |
+
|
| 184 |
+
Use the Hub search with the llama.cpp app filter:
|
| 185 |
+
|
| 186 |
+
```text
|
| 187 |
+
https://huggingface.co/models?apps=llama.cpp&sort=trending
|
| 188 |
+
https://huggingface.co/models?search=<term>&apps=llama.cpp&sort=trending
|
| 189 |
+
https://huggingface.co/models?search=<term>&apps=llama.cpp&num_parameters=min:0,max:24B&sort=trending
|
| 190 |
+
```
|
| 191 |
+
|
| 192 |
+
For a specific repo, open:
|
| 193 |
+
|
| 194 |
+
```text
|
| 195 |
+
https://huggingface.co/<repo>?local-app=llama.cpp
|
| 196 |
+
https://huggingface.co/api/models/<repo>/tree/main?recursive=true
|
| 197 |
+
```
|
| 198 |
+
|
| 199 |
+
Then launch directly from the Hub without extra Hub tooling:
|
| 200 |
+
|
| 201 |
+
```bash
|
| 202 |
+
llama-cli -hf <repo>:Q4_K_M
|
| 203 |
+
llama-server -hf <repo>:Q4_K_M
|
| 204 |
+
```
|
| 205 |
+
|
| 206 |
+
If you need the exact file name from the tree API:
|
| 207 |
+
|
| 208 |
+
```bash
|
| 209 |
+
llama-server --hf-repo <repo> --hf-file <filename.gguf>
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
## Importance Matrices (imatrix)
|
| 213 |
+
|
| 214 |
+
**What**: Calibration data to improve quantization quality.
|
| 215 |
+
|
| 216 |
+
**Benefits**:
|
| 217 |
+
- 10-20% perplexity improvement with Q4
|
| 218 |
+
- Essential for Q3 and below
|
| 219 |
+
|
| 220 |
+
**Usage**:
|
| 221 |
+
```bash
|
| 222 |
+
# 1. Generate importance matrix
|
| 223 |
+
./llama-imatrix \
|
| 224 |
+
-m model-f16.gguf \
|
| 225 |
+
-f calibration-data.txt \
|
| 226 |
+
-o model.imatrix
|
| 227 |
+
|
| 228 |
+
# 2. Quantize with imatrix
|
| 229 |
+
./llama-quantize \
|
| 230 |
+
--imatrix model.imatrix \
|
| 231 |
+
model-f16.gguf \
|
| 232 |
+
model-Q4_K_M.gguf \
|
| 233 |
+
Q4_K_M
|
| 234 |
+
```
|
| 235 |
+
|
| 236 |
+
**Calibration data**:
|
| 237 |
+
- Use domain-specific text (e.g., code for code models)
|
| 238 |
+
- ~100MB of representative text
|
| 239 |
+
- Higher quality data = better quantization
|
| 240 |
+
|
| 241 |
+
## Troubleshooting
|
| 242 |
+
|
| 243 |
+
**Model outputs gibberish**:
|
| 244 |
+
- Quantization too aggressive (Q2_K)
|
| 245 |
+
- Try Q4_K_M or Q5_K_M
|
| 246 |
+
- Verify model converted correctly
|
| 247 |
+
|
| 248 |
+
**Out of memory**:
|
| 249 |
+
- Use lower quantization (Q4_K_S instead of Q5_K_M)
|
| 250 |
+
- Offload fewer layers to GPU (`-ngl`)
|
| 251 |
+
- Use smaller context (`-c 2048`)
|
| 252 |
+
|
| 253 |
+
**Slow inference**:
|
| 254 |
+
- Higher quantization uses more compute
|
| 255 |
+
- Q8_0 much slower than Q4_K_M
|
| 256 |
+
- Consider speed vs quality trade-off
|
.agents/skills/huggingface-lora-space-builder/SKILL.md
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: huggingface-lora-space-builder
|
| 3 |
+
description: Build and publish a Gradio demo on Hugging Face Spaces for a user-provided LoRA. Use when someone asks to create, generate, ship, or publish a Space, demo, Gradio app, or playground for a LoRA β including LoRAs for Qwen-Image, Qwen-Image-Edit, LTX-Video, Wan, FLUX, SDXL, or other diffusion base models. Also triggers when someone describes a LoRA they trained or hosts on the Hub and wants to share it. Covers picking the right base pipeline and `diffusers` inference recipe, designing a UI tailored to the LoRA's task and inputs (Union/multi-task control, edit, video, image, etc.), respecting model-card recommendations (trigger words, steps, guidance, LoRA scale, example inputs), and shipping to ZeroGPU hardware as a private Space by default.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Gradio LoRA Space Builder
|
| 7 |
+
|
| 8 |
+
Build and publish a Gradio demo on Hugging Face Spaces that runs inference with a user-provided LoRA. Use whenever someone asks to create, generate, ship, or publish "a Space", "a demo", "a Gradio app", or "a playground" for a LoRA β whether the base model is Qwen-Image, Qwen-Image-Edit, LTX, or another diffusion model. Also use when someone describes a LoRA they trained or hosts on the Hub and wants to share it. The default target is ZeroGPU hardware and the default inference library is `diffusers` when the base model supports it.
|
| 9 |
+
|
| 10 |
+
The output is a real, published Space (private by default) that the user can try in the browser, not a local script.
|
| 11 |
+
|
| 12 |
+
## What "good" looks like for these demos
|
| 13 |
+
|
| 14 |
+
The demo should feel handcrafted for this specific LoRA, not a generic template with the LoRA bolted on. Two LoRAs that share a task can still need different demos: a pose-control video LoRA and an outpainting video LoRA both take video in and produce video out, but the inputs the user provides, the preprocessing, and the controls are completely different. Recognizing that is the central job here.
|
| 15 |
+
|
| 16 |
+
Concretely, a good demo:
|
| 17 |
+
|
| 18 |
+
- Loads fast and runs fast β minimal model loading, sensible step count, no wasted computation per call.
|
| 19 |
+
- Has a UI with exactly the controls this LoRA needs and nothing else. Excess sliders are a cost, not a feature.
|
| 20 |
+
- Shows the user what's happening β progress, intermediate outputs where useful, the seed used, a clear error when input is missing.
|
| 21 |
+
- Honors the LoRA's own recommendations from its model card: trigger words, recommended step count, recommended guidance scale, recommended LoRA scale, example inputs.
|
| 22 |
+
- Is creative where creativity helps β interactive canvases, before/after sliders, side-by-side previews of intermediate processing β and plain where plainness is right.
|
| 23 |
+
|
| 24 |
+
## Workflow
|
| 25 |
+
|
| 26 |
+
Work through these phases in order. Information gathered in one phase decides the next.
|
| 27 |
+
|
| 28 |
+
1. Gather the LoRA info needed to pick a pipeline and design a UI.
|
| 29 |
+
2. Pick the base pipeline and inference recipe.
|
| 30 |
+
3. Design the UI for this specific LoRA's task and inputs.
|
| 31 |
+
4. Write `app.py`, `requirements.txt`, and `README.md` together; show all three to the user for one batched approval.
|
| 32 |
+
5. Publish the Space (private).
|
| 33 |
+
|
| 34 |
+
Don't drip-feed questions across multiple turns. Batch them.
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
## Phase 1 β Gather LoRA info
|
| 39 |
+
|
| 40 |
+
Required: a LoRA repo on the Hub (e.g. `username/my-lora`).
|
| 41 |
+
|
| 42 |
+
**First, try to read the repo without a token.** If it succeeds, the repo is public β proceed. If it fails with 401/403, the repo is private/gated and you need an authenticated session to read it. **Don't immediately ask for a token.** Check first whether the user is already authenticated.
|
| 43 |
+
|
| 44 |
+
```python
|
| 45 |
+
from huggingface_hub import HfApi, get_token
|
| 46 |
+
|
| 47 |
+
cached_token = get_token() # picks up HF_TOKEN env var or cached CLI login
|
| 48 |
+
if cached_token:
|
| 49 |
+
try:
|
| 50 |
+
info = HfApi().whoami(token=cached_token)
|
| 51 |
+
username = info["name"]
|
| 52 |
+
# info also has fine-grained token scope info if applicable
|
| 53 |
+
except Exception:
|
| 54 |
+
cached_token = None # token exists but is invalid/expired
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
Then:
|
| 58 |
+
|
| 59 |
+
- If a valid cached token exists *and* it can read the repo, use it. No prompt needed.
|
| 60 |
+
- If no cached token, or the cached token can't read this private repo, ask the user for a token β once, with the explanation below.
|
| 61 |
+
|
| 62 |
+
When asking for a token (and only when you actually need to ask):
|
| 63 |
+
|
| 64 |
+
> I need a Hugging Face access token with **write** scope (to read the LoRA if it's private/gated, and to publish the Space). Create one at https://huggingface.co/settings/tokens. Paste it here.
|
| 65 |
+
|
| 66 |
+
The same token will be reused for publishing in the final phase, so this is a one-time ask.
|
| 67 |
+
|
| 68 |
+
**Then read what's in the repo:**
|
| 69 |
+
|
| 70 |
+
- List the repo files (`huggingface_hub.HfApi().list_repo_files(repo_id)`). Look for `.safetensors`, `README.md`, example images/videos, multiple checkpoints.
|
| 71 |
+
- Fetch the model card (`huggingface_hub.ModelCard.load(repo_id)`). The `data` dict has structured fields; the `text` has the README body.
|
| 72 |
+
- If multiple `.safetensors` files exist, pick the right one β see "Picking the LoRA weights file" in `references/zerogpu-and-publishing.md`. Briefly: README-recommended file wins, then `pytorch_lora_weights.safetensors`, then latest training checkpoint, otherwise ask.
|
| 73 |
+
|
| 74 |
+
**From the model card, try to determine:**
|
| 75 |
+
|
| 76 |
+
- **Base model** β the `base_model` field, or text mentions in the README. Usually present. Use it to pick the pipeline reference file (see Phase 2).
|
| 77 |
+
- **Task** β `pipeline_tag` if set, otherwise inferred from the base model and README text. The five tasks this skill handles: `text-to-image`, `image-to-image`, `text-to-video`, `image-to-video`, `video-to-video`.
|
| 78 |
+
- **Trigger words** β often called "trigger word", "instance prompt", "activation word"; sometimes embedded in example prompts.
|
| 79 |
+
- **Recommended inference recipe** β step count, guidance scale, true CFG scale, LoRA scale, resolution. Many LoRA cards include a Python snippet; trust its *parameters* (steps, guidance, CFG, LoRA scale, dtype). For *loading mechanics*, see `adapting-to-the-lora.md` β prefer `pipe.load_lora_weights(...)` over whatever loading approach the snippet uses.
|
| 80 |
+
- **Example prompts and example media** β use these as Gradio examples in the UI.
|
| 81 |
+
- **Sub-task / specific use case** β for image edits and video LoRAs, "what does this LoRA actually do" matters as much as the task category. A relighting LoRA, a face-swap LoRA, and a style LoRA all might be image-to-image, but the UI for each is different.
|
| 82 |
+
|
| 83 |
+
**When something can't be inferred, ask the user β once, in a single batched message.** Format the question to make answering trivial. For task category, list the five options as a numbered choice. For sub-task, give a one-line description ("what does this LoRA do? e.g. 'relight portraits', 'apply manga style', 'extend videos to wider aspect ratios'"). Don't ask if you can already infer it confidently from the base model or README.
|
| 84 |
+
|
| 85 |
+
If the model card has nothing helpful at all β no base model, no task, no example β surface that clearly: "The model card has no usable info. I'll need you to tell me: (1) base model, (2) what this LoRA does, (3) recommended step count and guidance scale if you know them."
|
| 86 |
+
|
| 87 |
+
---
|
| 88 |
+
|
| 89 |
+
## Phase 2 β Pick the base pipeline
|
| 90 |
+
|
| 91 |
+
Two things to decide here: which reference file to load, and which pipeline class to use. They're not the same question β a base-model family file (e.g. `qwen-image.md`) covers multiple variants, and variants in the same family don't always share a pipeline class. Get this wrong and the Space loads but produces wrong output, or fails at startup.
|
| 92 |
+
|
| 93 |
+
**Step 1 β Load the reference file for this base model family.**
|
| 94 |
+
|
| 95 |
+
- `references/base-models/qwen-image.md` β covers Qwen-Image and Qwen-Image-Edit family (text-to-image and image-to-image).
|
| 96 |
+
- `references/base-models/ltx.md` β covers LTX family (text-to-video, image-to-video, video-to-video, including IC-LoRAs).
|
| 97 |
+
|
| 98 |
+
If the base model isn't in one of these files, this skill doesn't have first-class support yet. Tell the user, and ask whether they want to proceed by analogy (use the closest model's recipe and adjust) or stop. Don't guess silently.
|
| 99 |
+
|
| 100 |
+
**Step 2 β Verify the pipeline class against the base model's own card. This step is mandatory, not optional.**
|
| 101 |
+
|
| 102 |
+
A new base model variant might use the same pipeline class with a different repo path, or a new pipeline class entirely. Don't trust the reference file's table alone β it's best-effort and can lag a recent release. Verify before committing:
|
| 103 |
+
|
| 104 |
+
```python
|
| 105 |
+
from huggingface_hub import ModelCard
|
| 106 |
+
base_card = ModelCard.load(base_model_id)
|
| 107 |
+
# Read base_card.text β find the diffusers inference snippet, note the pipeline class it imports.
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
The class imported in the base model card's diffusers snippet is the source of truth. Real examples where this matters:
|
| 111 |
+
|
| 112 |
+
- `Qwen-Image-Edit` uses `QwenImageEditPipeline`. `Qwen-Image-Edit-2509` and `Qwen-Image-Edit-2511` use `QwenImageEditPlusPipeline` β different class, different default parameters, takes a list of images instead of one. A LoRA targeting 2511 loaded onto `QwenImageEditPipeline` produces broken output.
|
| 113 |
+
- LTX-Video uses `LTXPipeline`/`LTXImageToVideoPipeline`/`LTXConditionPipeline`. LTX-2 uses `LTX2Pipeline` from a different module path. LTX-2.3 sometimes needs a native pipeline outside diffusers.
|
| 114 |
+
|
| 115 |
+
If the base model card has no diffusers snippet at all, fall back to the reference file's table β and tell the user you're falling back, in case they know something the table doesn't.
|
| 116 |
+
|
| 117 |
+
The cost of this verification is one Hub fetch and a few seconds of reading. The cost of skipping it is the failure mode the previous bullet describes β a "working" Space that's quietly using the wrong class.
|
| 118 |
+
|
| 119 |
+
**Step 3 β Diffusers vs native pipeline.** Default to `diffusers` when the base model has a diffusers pipeline class. That's the case for Qwen-Image and Qwen-Image-Edit and most of LTX. Some LTX variants (notably LTX-2.3 with certain IC-LoRAs) need a native pipeline; the LTX reference says when. Diffusers gives standard `load_lora_weights` / `set_adapters` semantics; the native path needs LoRA-specific glue.
|
| 120 |
+
|
| 121 |
+
---
|
| 122 |
+
|
| 123 |
+
## Phase 3 β Design the UI for this LoRA
|
| 124 |
+
|
| 125 |
+
Don't reach for a template. Reason from the LoRA's task and inputs to a UI.
|
| 126 |
+
|
| 127 |
+
Read `references/tasks.md` for the per-task baseline UI patterns (what the standard inputs/outputs look like for T2I, I2I, T2V, I2V, V2V).
|
| 128 |
+
|
| 129 |
+
Then read `references/adapting-to-the-lora.md`, which is about *thinking through what this specific LoRA needs* β beyond the task category. That file is the most important one in this skill. The same task can need very different UIs: a pose-control LTX LoRA needs a video input and a pose-extraction preview; an outpaint LTX LoRA needs an aspect-ratio picker and a black-margin preview; a relighting Flux LoRA needs an image and a brush canvas for indicating where to add light. None of those reduce to "the V2V template" or "the I2I template".
|
| 130 |
+
|
| 131 |
+
**Self-check before writing the UI.** Write one sentence describing what a user does with this Space in 10 seconds. If that sentence doesn't distinguish this LoRA from any other LoRA of the same task, the UI isn't shaped enough yet.
|
| 132 |
+
|
| 133 |
+
Examples that pass the self-check:
|
| 134 |
+
|
| 135 |
+
- "Upload a video, pick a target aspect ratio, click Generate; the model fills the empty margins."
|
| 136 |
+
- "Draw colored brush strokes where you want light, pick an illumination style, click Generate; the model relights the photo."
|
| 137 |
+
- "Upload a video of someone moving and an image of a different character; the model produces a video of the character doing the motion."
|
| 138 |
+
|
| 139 |
+
Examples that fail:
|
| 140 |
+
|
| 141 |
+
- "Type a prompt and click generate." (Generic T2I β say more.)
|
| 142 |
+
- "Upload an image and an instruction." (Generic edit β what kind of edit?)
|
| 143 |
+
|
| 144 |
+
**Gradio component freshness.** Gradio's component set evolves. Before defaulting to plain components, consider whether something newer fits better β for example `gr.ImageSlider` for before/after on edit LoRAs, `gr.BrowserState` for persistent prefs, `@gr.render` for UIs that change based on input. If you're unsure whether a component exists or what its signature is, web-fetch the current Gradio docs at https://www.gradio.app/docs rather than guessing.
|
| 145 |
+
|
| 146 |
+
**When stock and Hub custom components aren't enough β creative mode.** If the LoRA's natural input is a shape no Gradio component (built-in or on the Hub) expresses well β point sets, strokes, trajectories, multi-region annotations with metadata, 3D rotation gizmos, timeline scrubbers, anything where the user manipulates a thing on top of media β drop down to custom HTML/JS via `gr.HTML`. See `references/creative-mode.md` for the Gradio primitives (`gr.HTML`, `head=` injection, `elem_id` addressing, the two JSβPython state-sync approaches), the discipline around defining a JSON wire format, and the pitfalls. Don't reach for creative mode just because it would be cool β reach for it when the LoRA's input shape demands it. And don't skip the Hub custom components rung above (e.g. `gradio_image_annotation`) before going fully bespoke.
|
| 147 |
+
|
| 148 |
+
**`gr.Examples` for media-input Spaces.** When no fitting example media is available from the model's own repo, pull from the shared input pools β split by modality so the HF dataset viewer can render proper thumbnails: images at [`linoyts/repo-to-space-example-inputs`](https://huggingface.co/datasets/linoyts/repo-to-space-example-inputs), videos at [`linoyts/repo-to-space-example-videos`](https://huggingface.co/datasets/linoyts/repo-to-space-example-videos). Both are CC0 with `categories` + natural-language `caption` metadata and the same filter/rank recipe in each dataset README. Pick 2β3 that fit the task, preprocess to the shapes the model expects, and bake the copies into the Space. Set `cache_examples=True, cache_mode="lazy"` so the first click caches without running examples at build time (see `references/zerogpu-and-publishing.md`).
|
| 149 |
+
|
| 150 |
+
---
|
| 151 |
+
|
| 152 |
+
## Phase 4 β Write the Space files
|
| 153 |
+
|
| 154 |
+
Before writing, tell the user concretely what's about to happen β name the actual files. Not "I'll write the three files" but something like:
|
| 155 |
+
|
| 156 |
+
> "Now I'll write the three files needed to publish a Space: **`app.py`** (the Gradio demo and inference code), **`requirements.txt`** (Python dependencies), and **`README.md`** (Space configuration including ZeroGPU hardware setting). Then I'll show all three for your review before publishing."
|
| 157 |
+
|
| 158 |
+
This anchors the user in what's being produced. Don't say "three files" without naming them β it's vague and signals lack of commitment to the deliverable.
|
| 159 |
+
|
| 160 |
+
The three files are tightly coupled: `requirements.txt` is determined by what `app.py` imports, and the `README.md` YAML frontmatter sets the SDK version, hardware, and Space title that have to match. Write them together, then show all three to the user for approval in **one batched message** before publishing.
|
| 161 |
+
|
| 162 |
+
Read `references/zerogpu-and-publishing.md` for the ZeroGPU rules. The non-obvious ones:
|
| 163 |
+
|
| 164 |
+
- Models go on `cuda` at module level (not lazy-loaded inside the GPU function). ZeroGPU has a CUDA emulation that makes this work pre-allocation, and module-level placement is significantly faster than deferred placement.
|
| 165 |
+
- The function that runs inference is decorated with `@spaces.GPU(duration=...)`. Pick a duration appropriate for the task β short for image generation, longer for video.
|
| 166 |
+
- Don't use `torch.compile` β it's incompatible with ZeroGPU's process model.
|
| 167 |
+
|
| 168 |
+
### `app.py`
|
| 169 |
+
|
| 170 |
+
Compose from the pieces decided in Phases 1β3. Don't paste from a template. Each section should be there because it's needed:
|
| 171 |
+
|
| 172 |
+
- Imports β `gradio as gr`, `torch`, `spaces`, the pipeline class, anything the preprocessing needs.
|
| 173 |
+
- Constants β `LORA_REPO`, `BASE_MODEL`, recommended step count, guidance, LoRA scale, trigger word.
|
| 174 |
+
- Module-level model load β pipeline `from_pretrained`, `.to("cuda")`, `load_lora_weights`. If the LoRA repo is private, pass `token=os.environ["HF_TOKEN"]`.
|
| 175 |
+
- Preprocessing functions (if any) β pose extraction, padding, mask building, etc. CPU code can run at module level; GPU code needs to be inside a `@spaces.GPU` function.
|
| 176 |
+
- The inference function β decorated with `@spaces.GPU(duration=...)`. Validates inputs, applies trigger word, builds the pipeline kwargs, returns outputs.
|
| 177 |
+
- The Gradio Blocks β the UI from Phase 3, wired to the inference function.
|
| 178 |
+
|
| 179 |
+
Common things to get right:
|
| 180 |
+
|
| 181 |
+
- Return the actually-used seed alongside the result so the user can reproduce.
|
| 182 |
+
- `gr.Progress(track_tqdm=True)` on the inference function surfaces diffusers' internal progress bar.
|
| 183 |
+
- Validate inputs β raise `gr.Error("Please upload an image first.")` when a required input is missing, rather than letting the pipeline fail with a cryptic error.
|
| 184 |
+
- On `gr.Examples`, use `cache_examples=True, cache_mode="lazy"` β plain `cache_examples=True` runs examples at build time and fails on ZeroGPU; lazy mode defers caching to the first user click.
|
| 185 |
+
|
| 186 |
+
### `requirements.txt`
|
| 187 |
+
|
| 188 |
+
Don't ship a fixed minimal list and hope for the best. The "minimal" list works for plain T2I LoRAs and breaks the moment the base model has a vision-language text encoder, video output, or any non-trivial preprocessing. **Derive `requirements.txt` from what the Space actually needs**, in this order:
|
| 189 |
+
|
| 190 |
+
1. **Every top-level non-stdlib import in `app.py`.** If `app.py` does `import cv2`, `requirements.txt` has `opencv-python`. If it does `from controlnet_aux import OpenposeDetector`, `requirements.txt` has `controlnet-aux`. Walk the imports mechanically. (Note the exclusions in the next paragraph β some imports are runtime built-ins and don't need to be listed.)
|
| 191 |
+
2. **What the base-model reference's "Required dependencies" subsection says.** Each base-model file lists the non-obvious extras the pipeline pulls in β `torchvision` for Qwen-Image (Qwen 2.5-VL text encoder), `imageio[ffmpeg]` for LTX (video export), etc. Include all of them. These are the deps that aren't picked up from imports because the pipeline's components import them transitively at load time.
|
| 192 |
+
3. **What the LoRA's own model card explicitly mentions installing.** If the LoRA README has its own `pip install` block, lift the deps from there.
|
| 193 |
+
4. **The diffusers/ML stack:** `diffusers`, `transformers`, `accelerate`, `peft`, `safetensors`. Default to plain (unpinned). Switch `diffusers` to `git+https://github.com/huggingface/diffusers` if the base-model reference says the model needs it (recent releases often do β Qwen-Image-Edit-2511 is a current example).
|
| 194 |
+
|
| 195 |
+
**What *not* to list in `requirements.txt`:**
|
| 196 |
+
|
| 197 |
+
- **`gradio`** β controlled by the `sdk_version:` field in `README.md`'s YAML frontmatter, not by `requirements.txt`. Listing it in requirements is at best ignored, at worst causes a version conflict with the SDK. Set the version in the README only.
|
| 198 |
+
- **`torch`** β provided by the Space runtime. Only add if you need a specific version pinned (rare, and usually a sign something else is wrong).
|
| 199 |
+
- **`spaces`** β provided by the Space runtime. Only add if you need a specific version pinned.
|
| 200 |
+
- **`huggingface_hub`** β provided by the Space runtime. Only add if you need a specific version pinned.
|
| 201 |
+
|
| 202 |
+
These four come pre-installed in the ZeroGPU container. Listing them anyway is the kind of "include rather than skip" instinct that's right for non-baseline deps but wrong for baseline ones, because pinning conflicts with the runtime's managed versions.
|
| 203 |
+
|
| 204 |
+
**Bias for everything else: include rather than skip when uncertain.** A package the Space doesn't actually use causes a slightly slower build. A missing required package causes a startup-time crash that's much harder for the user to diagnose. These costs aren't symmetric β the test failure that prompted this rule was exactly the second kind.
|
| 205 |
+
|
| 206 |
+
**But two specific deps are *not* safe to add reflexively** because they routinely cause more problems than they solve on ZeroGPU:
|
| 207 |
+
|
| 208 |
+
- `xformers` β pinned to specific torch versions, frequent source of conflicts. The ZeroGPU runtime ships torch 2.8+, so any pinned `xformers` version must support that. Additional gotcha on Blackwell: xformers' FA3 dispatch mis-gates the hardware (FA3 kernels are Hopper-only at `sm_90a`, but the dispatcher gates on `device_capability >= (9, 0)`, which also matches Blackwell) and crashes at kernel launch with `CUDA invalid argument`. If a Space using xformers attention hits this, disable FA3 dispatch at module load:
|
| 209 |
+
|
| 210 |
+
```python
|
| 211 |
+
try:
|
| 212 |
+
from xformers.ops.fmha import _set_use_fa3
|
| 213 |
+
_set_use_fa3(False)
|
| 214 |
+
except Exception:
|
| 215 |
+
pass
|
| 216 |
+
```
|
| 217 |
+
|
| 218 |
+
Only include `xformers` if `app.py` actually uses it.
|
| 219 |
+
- `flash-attn` β needs a build step, often fails to install. Same torch 2.8+ alignment caveat as `xformers`. Only include if `app.py` actually uses it.
|
| 220 |
+
|
| 221 |
+
**Pin other versions only when you have a reason** (e.g. a known incompatibility, or matching a recipe from the model card).
|
| 222 |
+
|
| 223 |
+
### `README.md`
|
| 224 |
+
|
| 225 |
+
Spaces are configured by the YAML frontmatter at the top of `README.md`. This frontmatter is what selects ZeroGPU.
|
| 226 |
+
|
| 227 |
+
```
|
| 228 |
+
---
|
| 229 |
+
title: <human-readable title>
|
| 230 |
+
emoji: π¨
|
| 231 |
+
colorFrom: pink
|
| 232 |
+
colorTo: purple
|
| 233 |
+
sdk: gradio
|
| 234 |
+
sdk_version: <current Gradio version>
|
| 235 |
+
app_file: app.py
|
| 236 |
+
pinned: false
|
| 237 |
+
hardware: zero-a10g
|
| 238 |
+
short_description: <one short line for the Space tile, ~60 chars max>
|
| 239 |
+
models:
|
| 240 |
+
- <base model repo>
|
| 241 |
+
- <lora repo>
|
| 242 |
+
---
|
| 243 |
+
|
| 244 |
+
# <title>
|
| 245 |
+
|
| 246 |
+
A short description with links to the LoRA and base model.
|
| 247 |
+
```
|
| 248 |
+
|
| 249 |
+
Key fields:
|
| 250 |
+
|
| 251 |
+
- `sdk: gradio` β required for ZeroGPU.
|
| 252 |
+
- `sdk_version` β match the Gradio version you wrote against. Look up the current version (`pip index versions gradio`, or check https://www.gradio.app) rather than guessing.
|
| 253 |
+
- `hardware: zero-a10g` β the legacy string for ZeroGPU. The actual hardware is NVIDIA RTX Pro 6000 Blackwell, but the identifier is `zero-a10g`. ZeroGPU is available to PRO, Team, and Enterprise accounts; if the user isn't subscribed, the Space will fall back to CPU. Mention this if you suspect they aren't on PRO.
|
| 254 |
+
- `models:` β list base and LoRA repos. This enables Hub caching and discovery.
|
| 255 |
+
- `short_description` β appears on the Space tile. **Keep it short (~60 characters or less).** The Hub's YAML validator rejects long values with a 400 from `https://huggingface.co/api/validate-yaml`, which surfaces as an `HfHubHTTPError` during `create_repo` or `upload_file`. The exact server-side limit isn't documented and may change, so target the visible-tile-length range rather than pushing right up to a cap. If you do hit the 400, the fix is almost always to shorten this field. One sentence describing what the Space does is plenty β the README body below the YAML is where you put longer prose.
|
| 256 |
+
|
| 257 |
+
### Single batched approval β order of operations matters
|
| 258 |
+
|
| 259 |
+
The discipline here is **write all three files first, then show them all together in one message**. Not "write app.py β talk about it β write requirements β talk about it β write README β talk about it." That rhythm produces three approval moments even if you don't explicitly ask for approval, because the user is being asked to react after each file.
|
| 260 |
+
|
| 261 |
+
Concretely:
|
| 262 |
+
|
| 263 |
+
1. **Write `app.py`, `requirements.txt`, and `README.md` in succession with no intervening prose.** No commentary between files. No "Now I'll write the next one." No description of what each file does as you produce it. Just the three files, back to back.
|
| 264 |
+
2. **Then, in a single message, ask for approval covering all three at once.** Something like: "Here's the Space β `app.py` (N lines), `requirements.txt`, and `README.md`. Review and confirm to publish, or tell me what to change."
|
| 265 |
+
3. The user responds once, covering whatever they want changed across any of the three files.
|
| 266 |
+
|
| 267 |
+
What to avoid:
|
| 268 |
+
|
| 269 |
+
- Walking through `app.py`'s structure or design choices after writing it but before writing the others. Save commentary for either the pre-writing announcement (Phase 4 opening) or the single approval message after all three exist.
|
| 270 |
+
- Asking "ready for the next one?" or "want me to continue with requirements?" β those are implicit per-file approvals.
|
| 271 |
+
- Showing one file inline and offering to "show the next when you're ready" β same trap.
|
| 272 |
+
- Treating any of the three files as optional or as a follow-up. They are produced together as one deliverable.
|
| 273 |
+
|
| 274 |
+
If the user interrupts after seeing the first file with feedback or a question, that's fine β engage with it β but the rule still applies: the next time you produce code, produce all remaining files together, not one at a time.
|
| 275 |
+
|
| 276 |
+
---
|
| 277 |
+
|
| 278 |
+
## Phase 5 β Publish the Space
|
| 279 |
+
|
| 280 |
+
Use the authenticated session from Phase 1. Default to **private**, so the user can vet the Space before flipping it public. Confirm the target username with the user before creating: "I'll publish to `{username}/{space_name}` β confirm?"
|
| 281 |
+
|
| 282 |
+
```python
|
| 283 |
+
from huggingface_hub import HfApi, SpaceHardware
|
| 284 |
+
|
| 285 |
+
api = HfApi(token=hf_token)
|
| 286 |
+
username = api.whoami()["name"]
|
| 287 |
+
repo_id = f"{username}/{space_name}"
|
| 288 |
+
|
| 289 |
+
api.create_repo(
|
| 290 |
+
repo_id=repo_id,
|
| 291 |
+
repo_type="space",
|
| 292 |
+
space_sdk="gradio",
|
| 293 |
+
space_hardware=SpaceHardware.ZERO_A10G,
|
| 294 |
+
private=True,
|
| 295 |
+
exist_ok=True,
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
# Upload files
|
| 299 |
+
for path in ["app.py", "requirements.txt", "README.md"]:
|
| 300 |
+
api.upload_file(path_or_fileobj=path, path_in_repo=path,
|
| 301 |
+
repo_id=repo_id, repo_type="space")
|
| 302 |
+
```
|
| 303 |
+
|
| 304 |
+
If the LoRA repo itself is private/gated, the Space needs the token at runtime to download the LoRA. Set it as a Space secret:
|
| 305 |
+
|
| 306 |
+
```python
|
| 307 |
+
api.add_space_secret(repo_id=repo_id, key="HF_TOKEN", value=HF_TOKEN)
|
| 308 |
+
```
|
| 309 |
+
|
| 310 |
+
β¦and in `app.py`, load the LoRA with `token=os.environ["HF_TOKEN"]`.
|
| 311 |
+
|
| 312 |
+
**After upload**, run the smoke-test below before sharing β the build runs asynchronously and silent failures (wrong `weight_name`, missing dep, wrong pipeline class) only surface at first inference. **Once the smoke-test passes**, share the Space URL (`https://huggingface.co/spaces/{repo_id}`) and tell the user the Space is private β they'll need to be logged in to view it. Note that the build takes a few minutes; the logs are at `https://huggingface.co/spaces/{repo_id}/logs/container` if anything fails.
|
| 313 |
+
|
| 314 |
+
**Publish-time failures (before the build starts):**
|
| 315 |
+
|
| 316 |
+
- **`HfHubHTTPError: 400 Bad Request` from `https://huggingface.co/api/validate-yaml`** during `create_repo` or `upload_file`. The README YAML failed server-side validation. By far the most common cause is a `short_description` that's too long; sometimes a stray field or malformed value. Fix: shorten `short_description` to ~60 characters and retry. If shortening doesn't fix it, look for typos in field names or invalid values (e.g. unsupported colors in `colorFrom`/`colorTo`, an invalid `hardware` string).
|
| 317 |
+
- **403 on `create_repo`** with `space_hardware="zero-a10g"`: user isn't on PRO/Team/Enterprise, so they can't request ZeroGPU at creation time. Fix: retry `create_repo` without `space_hardware`, leave `hardware: zero-a10g` in the README YAML β the Space gets created on CPU. The user can then either upgrade to PRO (auto-promotes to ZeroGPU) or apply for a [community GPU grant](https://huggingface.co/docs/hub/spaces-gpus#community-gpu-grants) (request via the Space's hardware settings).
|
| 318 |
+
- **401/403 on `upload_file`**: token doesn't have write scope. Fix: ask the user for a write-scoped token.
|
| 319 |
+
|
| 320 |
+
**Common build failures (after the build starts):**
|
| 321 |
+
|
| 322 |
+
- LoRA `weight_name` mismatch in `load_lora_weights` β check the actual filename via `list_repo_files`.
|
| 323 |
+
- Base model is gated and the token wasn't set as a Space secret.
|
| 324 |
+
- ZeroGPU not allocated (user not on PRO) β Space falls back to CPU and is unusably slow.
|
| 325 |
+
- Diffusers version doesn't recognize the pipeline class β pin to git diffusers in `requirements.txt`.
|
| 326 |
+
- Missing dependency at module load β see `requirements.txt` derivation rules above; the most common case is a transitive dep like `torchvision` for Qwen-Image's text encoder.
|
| 327 |
+
|
| 328 |
+
If a build fails, offer to read the logs and propose a fix.
|
| 329 |
+
|
| 330 |
+
---
|
| 331 |
+
|
| 332 |
+
## Phase 6 β Smoke-test the Space
|
| 333 |
+
|
| 334 |
+
Before declaring the Space done and handing the URL to the user, exercise it once end-to-end. Several failure modes (wrong `weight_name`, wrong pipeline class, missing transitive dep, gated-base-model token issue) build cleanly and only surface at first inference. The `gradio` Python package ships a CLI that does exactly this β `gradio info` returns the endpoint signature, `gradio predict` runs an actual inference. Both ship with the `gradio` pip dependency the Space already needs, so they're available in any environment where this skill ran.
|
| 335 |
+
|
| 336 |
+
**Step 1 β Wait for the build.** `create_repo` returns immediately, but the container image is still building. Poll `HfApi().get_space_runtime(repo_id).stage` until it reaches `RUNNING`:
|
| 337 |
+
|
| 338 |
+
```python
|
| 339 |
+
import time
|
| 340 |
+
from huggingface_hub import HfApi
|
| 341 |
+
api = HfApi(token=hf_token)
|
| 342 |
+
while True:
|
| 343 |
+
stage = api.get_space_runtime(repo_id).stage
|
| 344 |
+
if stage == "RUNNING": break
|
| 345 |
+
if stage in {"BUILD_ERROR", "RUNTIME_ERROR", "CONFIG_ERROR"}:
|
| 346 |
+
raise RuntimeError(f"Build failed: {stage}. Logs: https://huggingface.co/spaces/{repo_id}/logs/container")
|
| 347 |
+
time.sleep(15)
|
| 348 |
+
```
|
| 349 |
+
|
| 350 |
+
If the build fails, fetch the container logs (`https://huggingface.co/spaces/{repo_id}/logs/container`), read the traceback, and propose a fix. Don't run `gradio info` against a Space that isn't running β it'll hang or 503.
|
| 351 |
+
|
| 352 |
+
**Step 2 β Verify the endpoint signature.** `gradio info {repo_id} --token {hf_token}` returns the exposed endpoints and their parameter types. Read the output and confirm: (a) the endpoint exists (default is `/predict`, but Blocks Spaces often have a custom name from the Python function name), (b) the parameters in order match what `app.py` declares, (c) file-typed params show `"type": "filepath"` as expected. If any of this is off, the user-facing UI may still appear correct but API calls will fail β fix and re-upload.
|
| 353 |
+
|
| 354 |
+
**Step 3 β Run one real inference.** Pick the lightest viable input β the simplest example from the LoRA card, or one of the `gr.Examples` entries. Pass `--token` for private Spaces. For file inputs, the payload uses `{"path": "...", "meta": {"_type": "gradio.FileData"}}`.
|
| 355 |
+
|
| 356 |
+
```bash
|
| 357 |
+
# Text-to-image:
|
| 358 |
+
gradio predict {repo_id} /predict '{"prompt": "...", "aspect_ratio": "1:1", ...}' --token $HF_TOKEN
|
| 359 |
+
|
| 360 |
+
# Image-to-image (file input):
|
| 361 |
+
gradio predict {repo_id} /predict '{"input_image": {"path": "/tmp/sample.jpg", "meta": {"_type": "gradio.FileData"}}, "prompt": "..."}' --token $HF_TOKEN
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
If you don't have a local sample image for I2I, lift one from the LoRA repo (`hf_hub_download(repo_id, filename="example.png")`) or the base model card.
|
| 365 |
+
|
| 366 |
+
**Caveat for creative-mode Spaces.** `gradio info` and `gradio predict` only exercise the Python endpoint β they tell you nothing about whether custom JS in a `gr.HTML` widget works. If the Space uses creative mode (see `references/creative-mode.md`), after the API smoke-test passes, **open the Space URL in a browser and verify the interaction once** before sharing. Server-side green plus broken JS is the most common failure mode for these.
|
| 367 |
+
|
| 368 |
+
**Step 4 β Interpret the result.**
|
| 369 |
+
|
| 370 |
+
- **Returns successfully and the output looks plausible** β done. Share the URL.
|
| 371 |
+
- **HTTPError 503 / "Space is sleeping"** β the Space spun down between steps 1 and 3. Wake it (`api.restart_space(repo_id)`) and retry.
|
| 372 |
+
- **Inference error mentioning `weight_name` / `safetensors`** β the LoRA filename in `app.py` doesn't match the actual file in the LoRA repo. Re-check `list_repo_files`, fix `weight_name=`, re-upload `app.py`.
|
| 373 |
+
- **Inference error mentioning a missing pipeline class or attribute** β diffusers version too old. Switch `requirements.txt` to `git+https://github.com/huggingface/diffusers` and re-upload.
|
| 374 |
+
- **`ImportError` at module load** β missing dep. Add it to `requirements.txt` and re-upload. The runtime logs (`/logs/run`) name the missing package.
|
| 375 |
+
- **OOM** β reduce default resolution or step count, or pick a smaller base variant.
|
| 376 |
+
- **Timeout / hangs** β bump `@spaces.GPU(duration=...)` and re-upload.
|
| 377 |
+
|
| 378 |
+
The smoke-test exists to convert these from "user discovers it and reports back" to "you discover it and fix it before sharing." Don't skip it because the build went green β green-build-broken-inference is the most common failure mode for Spaces with a non-trivial pipeline.
|
| 379 |
+
|
| 380 |
+
---
|
| 381 |
+
|
| 382 |
+
## What to avoid
|
| 383 |
+
|
| 384 |
+
- A generic "one demo for all LoRAs" template. The whole point of this skill is to tailor.
|
| 385 |
+
- Lazy-loading the model inside the GPU function. Slow on ZeroGPU, and hides startup errors until first request.
|
| 386 |
+
- `torch.compile`. Not supported on ZeroGPU.
|
| 387 |
+
- `cache_examples=True` without `cache_mode="lazy"` on ZeroGPU.
|
| 388 |
+
- Uploading the LoRA weights into the Space repo. Pull from the LoRA's own Hub repo at runtime.
|
| 389 |
+
- Asking for the HF token only at the end, then discovering the LoRA was private all along and you couldn't read the model card.
|
| 390 |
+
- Exposing every diffusers knob. Pick the 1β3 controls that matter for this LoRA.
|
| 391 |
+
- Long preambles in the chat reply once the Space is published. The Space URL is the deliverable; keep the wrap-up brief.
|