Girgie commited on
Commit
7eb55e2
Β·
verified Β·
1 Parent(s): c770a42

Deploy Token Holdem final validation Space

Browse files
This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .agents/skills/art-asset-generator/SKILL.md +111 -0
  2. .agents/skills/art-asset-generator/agents/openai.yaml +4 -0
  3. .agents/skills/cozy-game-polish-qa/SKILL.md +70 -0
  4. .agents/skills/cozy-game-polish-qa/agents/openai.yaml +4 -0
  5. .agents/skills/gradio-game-scene-composer/SKILL.md +155 -0
  6. .agents/skills/gradio-game-scene-composer/agents/openai.yaml +4 -0
  7. .agents/skills/gradio/SKILL.md +84 -0
  8. .agents/skills/gradio/references/api-signatures.md +115 -0
  9. .agents/skills/gradio/references/event-listeners.md +132 -0
  10. .agents/skills/gradio/references/examples.md +613 -0
  11. .agents/skills/hf-cli/.hf-skill-manifest.json +4 -0
  12. .agents/skills/hf-cli/SKILL.md +218 -0
  13. .agents/skills/hf-gradio/SKILL.md +83 -0
  14. .agents/skills/hf-mem/SKILL.md +79 -0
  15. .agents/skills/huggingface-best/SKILL.md +134 -0
  16. .agents/skills/huggingface-community-evals/SKILL.md +207 -0
  17. .agents/skills/huggingface-community-evals/examples/.env.example +3 -0
  18. .agents/skills/huggingface-community-evals/examples/USAGE_EXAMPLES.md +101 -0
  19. .agents/skills/huggingface-community-evals/scripts/inspect_eval_uv.py +104 -0
  20. .agents/skills/huggingface-community-evals/scripts/inspect_vllm_uv.py +306 -0
  21. .agents/skills/huggingface-community-evals/scripts/lighteval_vllm_uv.py +297 -0
  22. .agents/skills/huggingface-datasets/.hf-skill-manifest.json +4 -0
  23. .agents/skills/huggingface-datasets/SKILL.md +107 -0
  24. .agents/skills/huggingface-gradio/SKILL.md +298 -0
  25. .agents/skills/huggingface-gradio/examples.md +613 -0
  26. .agents/skills/huggingface-llm-trainer/.hf-skill-manifest.json +4 -0
  27. .agents/skills/huggingface-llm-trainer/SKILL.md +738 -0
  28. .agents/skills/huggingface-llm-trainer/references/gguf_conversion.md +296 -0
  29. .agents/skills/huggingface-llm-trainer/references/hardware_guide.md +283 -0
  30. .agents/skills/huggingface-llm-trainer/references/hub_saving.md +364 -0
  31. .agents/skills/huggingface-llm-trainer/references/local_training_macos.md +231 -0
  32. .agents/skills/huggingface-llm-trainer/references/reliability_principles.md +371 -0
  33. .agents/skills/huggingface-llm-trainer/references/trackio_guide.md +189 -0
  34. .agents/skills/huggingface-llm-trainer/references/training_methods.md +150 -0
  35. .agents/skills/huggingface-llm-trainer/references/training_patterns.md +203 -0
  36. .agents/skills/huggingface-llm-trainer/references/troubleshooting.md +282 -0
  37. .agents/skills/huggingface-llm-trainer/references/unsloth.md +313 -0
  38. .agents/skills/huggingface-llm-trainer/scripts/convert_to_gguf.py +424 -0
  39. .agents/skills/huggingface-llm-trainer/scripts/dataset_inspector.py +417 -0
  40. .agents/skills/huggingface-llm-trainer/scripts/estimate_cost.py +150 -0
  41. .agents/skills/huggingface-llm-trainer/scripts/hf_benchmarks.py +659 -0
  42. .agents/skills/huggingface-llm-trainer/scripts/train_dpo_example.py +106 -0
  43. .agents/skills/huggingface-llm-trainer/scripts/train_grpo_example.py +89 -0
  44. .agents/skills/huggingface-llm-trainer/scripts/train_sft_example.py +122 -0
  45. .agents/skills/huggingface-llm-trainer/scripts/unsloth_sft_example.py +512 -0
  46. .agents/skills/huggingface-local-models/SKILL.md +113 -0
  47. .agents/skills/huggingface-local-models/references/hardware.md +38 -0
  48. .agents/skills/huggingface-local-models/references/hub-discovery.md +178 -0
  49. .agents/skills/huggingface-local-models/references/quantization.md +256 -0
  50. .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.