Spaces:
Running
v0.8.3 PEFT Anti-Pattern Checker — anti-bullshit pack #9
Browse filesPEFT's `get_peft_model(base, config)` creates a FRESH adapter — it
does not load saved weights from a path. Users who paste tutorial code
and try to resume from a checkpoint silently throw away their training.
peft #2115 has the canonical bug report (12+ thumbs).
🔧 PEFT Lint (17th mode):
- Paste your PEFT/LoRA training script
- Static linter scans for 4 classes of bug:
1. silent_base_load — `get_peft_model` + checkpoint path string in
same script, no `PeftModel.from_pretrained`. Suggests the fix
inline with the detected path.
2. qlora_order — `prepare_model_for_kbit_training` AFTER
`get_peft_model`. Reversed order silently breaks gradient flow
through LoRA layers (loss → NaN or training nothing).
3. target_modules_mismatch — user's `target_modules=[…]` doesn't
overlap with the conventional list for the architecture
detected from a model id string. Covers Llama / Mistral / Qwen2
/ Phi / Phi-3 / Gemma / Falcon / Bloom / GPT-2 / GPT-NeoX / MPT.
4. alpha_not_2r — `lora_alpha` ratio off the α=2r / α=r
conventions (info-only).
- Findings rendered with severity badge, line number, per-rule
explainer + fix hint.
Pure logic in `js/peft_anti_pattern.js` (codes + params, no human
strings); main.js renders with i18n. Comment + string-literal stripping
prevents false positives on commented-out code or unrelated literals.
47 i18n keys × 4 langs (EN/ES/FR/ZH) = 188 keys, parity clean.
Solutions Hub `peft_loading` pain upgraded from
`tafagent_planned_mode: "🔧 PEFT Anti-Pattern Checker (v0.8.2)"` →
`tafagent_mode: "🔧 PEFT Anti-Pattern Checker"` (planned: → covered).
Help modal v0.8.3 entry + Inventory anti-bullshit-pack list updated +
task tile "⚙️ Set up an eval correctly" gains the new mode button.
Source citations:
- https://github.com/huggingface/peft/issues/2115 (the silent bug)
- https://huggingface.co/docs/peft/main/en/developer_guides/troubleshooting
- PEFT `get_layer_status() / get_model_status()` runtime check
Verified: 7/7 logic cases (silent base-load, correct from_pretrained,
QLoRA order, target_modules mismatch, alpha info, no-peft, comment
stripping) + 188/188 i18n parity + headless e2e (tab/section/three
examples render verdict+line+fix). 18 mode tabs total.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- data/solutions_hub.json +2 -3
- index.html +29 -0
- js/i18n.js +196 -0
- js/main.js +158 -1
- js/peft_anti_pattern.js +331 -0
|
@@ -222,15 +222,14 @@
|
|
| 222 |
"id": "peft_loading",
|
| 223 |
"category": "training",
|
| 224 |
"pain": "`get_peft_model()` before `PeftModel.from_pretrained()` silently loads base model — LoRA weights ignored.",
|
| 225 |
-
"tafagent_mode":
|
| 226 |
"external_tools": [
|
| 227 |
{"name": "HF PEFT troubleshooting (canonical)", "url": "https://huggingface.co/docs/peft/main/en/developer_guides/troubleshooting", "type": "docs"},
|
| 228 |
{"name": "peft #2115 — original bug report", "url": "https://github.com/huggingface/peft/issues/2115", "type": "issue"},
|
| 229 |
{"name": "PEFT get_layer_status() / get_model_status()", "url": "https://huggingface.co/docs/peft/main/en/package_reference/peft_model", "type": "docs"}
|
| 230 |
],
|
| 231 |
"best_for": "If you suspect your LoRA isn't being applied, call `model.get_layer_status()` and check `active_adapters` is non-empty.",
|
| 232 |
-
"not_for": null
|
| 233 |
-
"tafagent_planned_mode": "🔧 PEFT Anti-Pattern Checker (v0.8.2)"
|
| 234 |
},
|
| 235 |
{
|
| 236 |
"id": "intruder_dimensions",
|
|
|
|
| 222 |
"id": "peft_loading",
|
| 223 |
"category": "training",
|
| 224 |
"pain": "`get_peft_model()` before `PeftModel.from_pretrained()` silently loads base model — LoRA weights ignored.",
|
| 225 |
+
"tafagent_mode": "🔧 PEFT Anti-Pattern Checker",
|
| 226 |
"external_tools": [
|
| 227 |
{"name": "HF PEFT troubleshooting (canonical)", "url": "https://huggingface.co/docs/peft/main/en/developer_guides/troubleshooting", "type": "docs"},
|
| 228 |
{"name": "peft #2115 — original bug report", "url": "https://github.com/huggingface/peft/issues/2115", "type": "issue"},
|
| 229 |
{"name": "PEFT get_layer_status() / get_model_status()", "url": "https://huggingface.co/docs/peft/main/en/package_reference/peft_model", "type": "docs"}
|
| 230 |
],
|
| 231 |
"best_for": "If you suspect your LoRA isn't being applied, call `model.get_layer_status()` and check `active_adapters` is non-empty.",
|
| 232 |
+
"not_for": null
|
|
|
|
| 233 |
},
|
| 234 |
{
|
| 235 |
"id": "intruder_dimensions",
|
|
@@ -219,6 +219,9 @@
|
|
| 219 |
<p><strong data-i18n="help.v082.cot.title">📋 JSON CoT-aware Linter</strong></p>
|
| 220 |
<p data-i18n="help.v082.cot.body">Constrained-decoding engines (llguidance, Outlines, SGLang grammars) emit JSON properties in the order your schema declares them. If you write <code>{ answer, reasoning }</code> the model commits to <code>answer</code> first and CoT collapses into post-hoc justification. Paste any schema (or example response) — the linter classifies each field as <em>reasoning</em>, <em>answer</em>, or <em>other</em>, flags the ordering, and emits a reordered fix you can copy back. <em>Use case</em>: 'My CoT prompt works in plaintext but degrades under JSON mode' → run linter, find the inverted order, fix.</p>
|
| 221 |
|
|
|
|
|
|
|
|
|
|
| 222 |
<p><strong data-i18n="help.v081.hub.title">🧭 Solutions Hub</strong></p>
|
| 223 |
<p data-i18n="help.v081.hub.body">tafagent as integrator, not silo. 30+ pains across 7 categories (eval reliability · diagnostics · setup · training · retrieval · multimodal · observability), each mapped to (a) the tafagent mode that addresses it, if any, and (b) the best-of-breed external tools the community already trusts (RAGAS, MTEB, HELM, MCP Schema Validator, llm-stats, llguidance, GlitchMiner, etc.). Search box matches across pain, scenario, and tool name. <em>Use case</em>: 'I have problem X — does tafagent solve it, and if not, who does?'</p>
|
| 224 |
|
|
@@ -332,6 +335,7 @@
|
|
| 332 |
<li data-i18n="inv.v07.niah"><strong>🔍 NIAH→Reason</strong> — does your "128k context" actually reason there, or just retrieve?</li>
|
| 333 |
<li data-i18n="inv.v08.saturation"><strong>📈 Saturation</strong> — is your benchmark still useful, or are all frontier models tied at the top?</li>
|
| 334 |
<li data-i18n="inv.v082.cot"><strong>📋 JSON CoT</strong> — lints structured-output schemas for the answer-before-reasoning anti-pattern that silently breaks Chain-of-Thought.</li>
|
|
|
|
| 335 |
<li data-i18n="inv.v081.hub"><strong>🧭 Solutions Hub</strong> — every documented pain mapped to a tafagent mode or curated external tool. Don't reinvent — find.</li>
|
| 336 |
</ul>
|
| 337 |
</details>
|
|
@@ -404,6 +408,7 @@
|
|
| 404 |
<button data-mode-link="template" data-i18n="modes.template">📜 Chat-template</button>
|
| 405 |
<button data-mode-link="diagnose" data-i18n="modes.diagnose">🩺 Diagnose CLI</button>
|
| 406 |
<button data-mode-link="cot" data-i18n="modes.cot">📋 JSON CoT</button>
|
|
|
|
| 407 |
</div>
|
| 408 |
</div>
|
| 409 |
<div class="task-tile">
|
|
@@ -461,6 +466,7 @@
|
|
| 461 |
<button class="mode-btn" data-mode="niah" role="tab" aria-selected="false" data-i18n="modes.niah">🔍 NIAH→Reason</button>
|
| 462 |
<button class="mode-btn" data-mode="saturation" role="tab" aria-selected="false" data-i18n="modes.saturation">📈 Saturation</button>
|
| 463 |
<button class="mode-btn" data-mode="cot" role="tab" aria-selected="false" data-i18n="modes.cot">📋 JSON CoT</button>
|
|
|
|
| 464 |
<button class="mode-btn" data-mode="hub" role="tab" aria-selected="false" data-i18n="modes.hub">🧭 Solutions</button>
|
| 465 |
</div>
|
| 466 |
<p id="mode-desc" class="recipe-desc" data-i18n="modes.desc">
|
|
@@ -1032,6 +1038,29 @@
|
|
| 1032 |
<div id="cot-output" style="margin-top: 1em;"></div>
|
| 1033 |
</section>
|
| 1034 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1035 |
<section id="hub-section" style="display:none;">
|
| 1036 |
<h2><span data-i18n="hub.title">🧭 Solutions Hub</span>
|
| 1037 |
<span class="info"><span class="tooltip" data-i18n="hub.tip">
|
|
|
|
| 219 |
<p><strong data-i18n="help.v082.cot.title">📋 JSON CoT-aware Linter</strong></p>
|
| 220 |
<p data-i18n="help.v082.cot.body">Constrained-decoding engines (llguidance, Outlines, SGLang grammars) emit JSON properties in the order your schema declares them. If you write <code>{ answer, reasoning }</code> the model commits to <code>answer</code> first and CoT collapses into post-hoc justification. Paste any schema (or example response) — the linter classifies each field as <em>reasoning</em>, <em>answer</em>, or <em>other</em>, flags the ordering, and emits a reordered fix you can copy back. <em>Use case</em>: 'My CoT prompt works in plaintext but degrades under JSON mode' → run linter, find the inverted order, fix.</p>
|
| 221 |
|
| 222 |
+
<p><strong data-i18n="help.v083.peft.title">🔧 PEFT Anti-Pattern Checker</strong></p>
|
| 223 |
+
<p data-i18n="help.v083.peft.body">PEFT's <code>get_peft_model(base, config)</code> creates a FRESH adapter — it does not load saved weights from a path. Users who paste tutorial code and try to resume from a checkpoint silently throw away their training. peft #2115 has the canonical bug report. This linter scans your training script for the pattern + 3 related issues (QLoRA ordering, target_modules/arch mismatch, lora_alpha ratio) and reports findings with line numbers and suggested fixes. <em>Use case</em>: before you launch a 10-hour LoRA fine-tune, paste your script — catch the silent bugs in 200ms.</p>
|
| 224 |
+
|
| 225 |
<p><strong data-i18n="help.v081.hub.title">🧭 Solutions Hub</strong></p>
|
| 226 |
<p data-i18n="help.v081.hub.body">tafagent as integrator, not silo. 30+ pains across 7 categories (eval reliability · diagnostics · setup · training · retrieval · multimodal · observability), each mapped to (a) the tafagent mode that addresses it, if any, and (b) the best-of-breed external tools the community already trusts (RAGAS, MTEB, HELM, MCP Schema Validator, llm-stats, llguidance, GlitchMiner, etc.). Search box matches across pain, scenario, and tool name. <em>Use case</em>: 'I have problem X — does tafagent solve it, and if not, who does?'</p>
|
| 227 |
|
|
|
|
| 335 |
<li data-i18n="inv.v07.niah"><strong>🔍 NIAH→Reason</strong> — does your "128k context" actually reason there, or just retrieve?</li>
|
| 336 |
<li data-i18n="inv.v08.saturation"><strong>📈 Saturation</strong> — is your benchmark still useful, or are all frontier models tied at the top?</li>
|
| 337 |
<li data-i18n="inv.v082.cot"><strong>📋 JSON CoT</strong> — lints structured-output schemas for the answer-before-reasoning anti-pattern that silently breaks Chain-of-Thought.</li>
|
| 338 |
+
<li data-i18n="inv.v083.peft"><strong>🔧 PEFT Lint</strong> — catches the silent <code>get_peft_model</code> base-load (peft #2115) + QLoRA order + target_modules / arch mismatch.</li>
|
| 339 |
<li data-i18n="inv.v081.hub"><strong>🧭 Solutions Hub</strong> — every documented pain mapped to a tafagent mode or curated external tool. Don't reinvent — find.</li>
|
| 340 |
</ul>
|
| 341 |
</details>
|
|
|
|
| 408 |
<button data-mode-link="template" data-i18n="modes.template">📜 Chat-template</button>
|
| 409 |
<button data-mode-link="diagnose" data-i18n="modes.diagnose">🩺 Diagnose CLI</button>
|
| 410 |
<button data-mode-link="cot" data-i18n="modes.cot">📋 JSON CoT</button>
|
| 411 |
+
<button data-mode-link="peft" data-i18n="modes.peft">🔧 PEFT Lint</button>
|
| 412 |
</div>
|
| 413 |
</div>
|
| 414 |
<div class="task-tile">
|
|
|
|
| 466 |
<button class="mode-btn" data-mode="niah" role="tab" aria-selected="false" data-i18n="modes.niah">🔍 NIAH→Reason</button>
|
| 467 |
<button class="mode-btn" data-mode="saturation" role="tab" aria-selected="false" data-i18n="modes.saturation">📈 Saturation</button>
|
| 468 |
<button class="mode-btn" data-mode="cot" role="tab" aria-selected="false" data-i18n="modes.cot">📋 JSON CoT</button>
|
| 469 |
+
<button class="mode-btn" data-mode="peft" role="tab" aria-selected="false" data-i18n="modes.peft">🔧 PEFT Lint</button>
|
| 470 |
<button class="mode-btn" data-mode="hub" role="tab" aria-selected="false" data-i18n="modes.hub">🧭 Solutions</button>
|
| 471 |
</div>
|
| 472 |
<p id="mode-desc" class="recipe-desc" data-i18n="modes.desc">
|
|
|
|
| 1038 |
<div id="cot-output" style="margin-top: 1em;"></div>
|
| 1039 |
</section>
|
| 1040 |
|
| 1041 |
+
<!-- PEFT Anti-Pattern Checker (mode=peft, v0.8.3 anti-bullshit pack #9) -->
|
| 1042 |
+
<section id="peft-section" style="display:none;">
|
| 1043 |
+
<h2><span data-i18n="peft.title">🔧 PEFT Anti-Pattern Checker</span>
|
| 1044 |
+
<span class="info"><span class="tooltip" data-i18n="peft.tip">
|
| 1045 |
+
<strong>Why this matters</strong>: <code>get_peft_model(base, config)</code> creates a FRESH adapter — it does NOT load saved weights. Users who want to resume from a checkpoint must call <code>PeftModel.from_pretrained(base, path)</code>. peft #2115 documents the silent base-model bug. This linter scans your training script for that pattern (and 3 others: QLoRA ordering, target_modules/arch mismatch, lora_alpha ratio).
|
| 1046 |
+
</span></span>
|
| 1047 |
+
</h2>
|
| 1048 |
+
<p class="recipe-desc" data-i18n="peft.desc">
|
| 1049 |
+
<strong>Don't burn 10 hours of training on a base model.</strong> Paste your PEFT setup code — the linter flags silent base-model loads, QLoRA ordering bugs, target_modules/arch mismatches, and lora_alpha conventions.
|
| 1050 |
+
</p>
|
| 1051 |
+
<div class="form-row">
|
| 1052 |
+
<textarea id="peft-input" rows="14" style="width:100%;font-family:monospace;font-size:0.9em;" data-i18n-placeholder="peft.input.placeholder" placeholder="from peft import LoraConfig, get_peft_model base = AutoModelForCausalLM.from_pretrained('meta-llama/Llama-3-8B') config = LoraConfig(r=16, lora_alpha=32, target_modules=['q_proj','v_proj']) model = get_peft_model(base, config) # resume from saved adapter: model.load_state_dict('./outputs/checkpoint-1000/adapter_model.bin')"></textarea>
|
| 1053 |
+
</div>
|
| 1054 |
+
<div class="form-row">
|
| 1055 |
+
<button type="button" id="peft-lint-btn" data-i18n="peft.lint_btn">🔍 Lint</button>
|
| 1056 |
+
<button type="button" id="peft-example-bug-btn" class="secondary" data-i18n="peft.example_bug_btn">↳ Example: silent base-load</button>
|
| 1057 |
+
<button type="button" id="peft-example-qlora-btn" class="secondary" data-i18n="peft.example_qlora_btn">↳ Example: QLoRA order bug</button>
|
| 1058 |
+
<button type="button" id="peft-example-clean-btn" class="secondary" data-i18n="peft.example_clean_btn">↳ Example: clean</button>
|
| 1059 |
+
</div>
|
| 1060 |
+
<p id="peft-status" class="recipe-desc" style="font-size:0.92em;"></p>
|
| 1061 |
+
<div id="peft-output" style="margin-top: 1em;"></div>
|
| 1062 |
+
</section>
|
| 1063 |
+
|
| 1064 |
<section id="hub-section" style="display:none;">
|
| 1065 |
<h2><span data-i18n="hub.title">🧭 Solutions Hub</span>
|
| 1066 |
<span class="info"><span class="tooltip" data-i18n="hub.tip">
|
|
@@ -545,6 +545,55 @@ export const TRANSLATIONS = {
|
|
| 545 |
"help.v082.cot.title": "📋 JSON CoT-aware Linter",
|
| 546 |
"help.v082.cot.body": "Constrained-decoding engines (llguidance, Outlines, SGLang grammars) emit JSON properties in the order your schema declares them. If you write <code>{ answer, reasoning }</code> the model commits to <code>answer</code> first and CoT collapses into post-hoc justification. Paste any schema (or example response) — the linter classifies each field as <em>reasoning</em>, <em>answer</em>, or <em>other</em>, flags the ordering, and emits a reordered fix you can copy back. <em>Use case</em>: 'My CoT prompt works in plaintext but degrades under JSON mode' → run linter, find the inverted order, fix.",
|
| 547 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
"inv.v081.hub": "<strong>🧭 Solutions Hub</strong> — every documented pain mapped to a tafagent mode or curated external tool. Don't reinvent — find.",
|
| 549 |
"help.v081.hub.title": "🧭 Solutions Hub",
|
| 550 |
"help.v081.hub.body": "tafagent as integrator, not silo. 30+ pains across 7 categories (eval reliability · diagnostics · setup · training · retrieval · multimodal · observability), each mapped to (a) the tafagent mode that addresses it, if any, and (b) the best-of-breed external tools the community already trusts (RAGAS, MTEB, HELM, MCP Schema Validator, llm-stats, llguidance, GlitchMiner, etc.). Search box matches across pain, scenario, and tool name. <em>Use case</em>: 'I have problem X — does tafagent solve it, and if not, who does?'",
|
|
@@ -1549,6 +1598,55 @@ export const TRANSLATIONS = {
|
|
| 1549 |
"help.v082.cot.title": "📋 Linter JSON con consciencia CoT",
|
| 1550 |
"help.v082.cot.body": "Los motores de constrained decoding (llguidance, Outlines, gramáticas SGLang) emiten propiedades JSON en el orden que declara tu schema. Si escribes <code>{ answer, reasoning }</code> el modelo se compromete con <code>answer</code> primero y el CoT se reduce a justificación post-hoc. Pega cualquier schema (o respuesta de ejemplo) — el linter clasifica cada campo como <em>razonamiento</em>, <em>respuesta</em> u <em>otro</em>, señala el ordenamiento, y emite una corrección reordenada para copiar de vuelta. <em>Caso de uso</em>: 'Mi prompt CoT funciona en texto pero degrada en modo JSON' → ejecuta linter, encuentra el orden invertido, corrige.",
|
| 1551 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1552 |
"inv.v081.hub": "<strong>🧭 Solutions Hub</strong> — cada pain documentado mapeado a un mode tafagent o herramienta externa curada. No reinventes — encuentra.",
|
| 1553 |
"help.v081.hub.title": "🧭 Solutions Hub",
|
| 1554 |
"help.v081.hub.body": "tafagent como integrador, no silo. 30+ pains en 7 categorías (eval reliability · diagnósticos · setup · training · retrieval · multimodal · observability), cada uno mapeado a (a) el mode tafagent que lo resuelve, si existe, y (b) las herramientas externas best-of-breed que la comunidad ya usa (RAGAS, MTEB, HELM, MCP Schema Validator, llm-stats, llguidance, GlitchMiner, etc.). Caja de búsqueda matchea pain, scenario, y nombre de herramienta. <em>Caso de uso</em>: 'tengo problema X — ¿lo resuelve tafagent, y si no, quién?'",
|
|
@@ -2417,6 +2515,55 @@ export const TRANSLATIONS = {
|
|
| 2417 |
"help.v082.cot.title": "📋 Linter JSON conscient de CoT",
|
| 2418 |
"help.v082.cot.body": "Les moteurs de décodage contraint (llguidance, Outlines, grammaires SGLang) émettent les propriétés JSON dans l'ordre que votre schema déclare. Si vous écrivez <code>{ answer, reasoning }</code> le modèle s'engage sur <code>answer</code> en premier et le CoT se réduit à une justification a posteriori. Collez n'importe quel schema (ou réponse exemple) — le linter classe chaque champ comme <em>raisonnement</em>, <em>réponse</em> ou <em>autre</em>, signale l'ordre, et émet une correction réordonnée à copier. <em>Cas d'usage</em> : 'Mon prompt CoT marche en texte brut mais dégrade en mode JSON' → lancez le linter, trouvez l'ordre inversé, corrigez.",
|
| 2419 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2420 |
"inv.v081.hub": "<strong>🧭 Solutions Hub</strong> — chaque pain documenté mappé à un mode tafagent ou outil externe curé. Ne réinventez pas — trouvez.",
|
| 2421 |
"help.v081.hub.title": "🧭 Solutions Hub",
|
| 2422 |
"help.v081.hub.body": "tafagent comme intégrateur, pas silo. 30+ pains à travers 7 catégories (eval reliability · diagnostics · setup · training · retrieval · multimodal · observability), chacun mappé à (a) le mode tafagent qui le résout, s'il existe, et (b) les outils externes best-of-breed que la communauté utilise déjà (RAGAS, MTEB, HELM, MCP Schema Validator, llm-stats, llguidance, GlitchMiner, etc.). La barre de recherche matche pain, scénario, et nom d'outil. <em>Cas d'usage</em> : 'j'ai le problème X — tafagent le résout-il, et sinon, qui ?'",
|
|
@@ -3285,6 +3432,55 @@ export const TRANSLATIONS = {
|
|
| 3285 |
"help.v082.cot.title": "📋 JSON CoT 感知 Linter",
|
| 3286 |
"help.v082.cot.body": "约束解码引擎(llguidance、Outlines、SGLang 语法)按 schema 声明的顺序输出 JSON 属性。如果你写 <code>{ answer, reasoning }</code>,模型先承诺 <code>answer</code>,CoT 就退化为事后辩护。粘贴任意 schema(或示例响应)——linter 把每个字段分类为<em>推理</em>、<em>答案</em>或<em>其他</em>,标记顺序,并输出可复制回去的重排修复。<em>用例</em>:『我的 CoT 提示在纯文本中正常但在 JSON 模式下退化』→ 运行 linter,找到颠倒的顺序,修复。",
|
| 3287 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3288 |
"inv.v081.hub": "<strong>🧭 Solutions Hub</strong> — 每个文档化的问题都映射到一个 tafagent 模式或精选外部工具。别重复发明 — 去找。",
|
| 3289 |
"help.v081.hub.title": "🧭 Solutions Hub",
|
| 3290 |
"help.v081.hub.body": "tafagent 作为集成者而非孤岛。30+ 问题跨 7 类别(评估可靠性 · 诊断 · 设置 · 训练 · 检索 · 多模态 · 可观测性),每个映射到(a)解决它的 tafagent 模式(若存在),以及(b)社区已信任的最佳外部工具(RAGAS、MTEB、HELM、MCP Schema Validator、llm-stats、llguidance、GlitchMiner 等)。搜索框匹配 pain、场景和工具名称。<em>用例</em>:'我有问题 X — tafagent 解决它吗,如果不,谁解决?'",
|
|
|
|
| 545 |
"help.v082.cot.title": "📋 JSON CoT-aware Linter",
|
| 546 |
"help.v082.cot.body": "Constrained-decoding engines (llguidance, Outlines, SGLang grammars) emit JSON properties in the order your schema declares them. If you write <code>{ answer, reasoning }</code> the model commits to <code>answer</code> first and CoT collapses into post-hoc justification. Paste any schema (or example response) — the linter classifies each field as <em>reasoning</em>, <em>answer</em>, or <em>other</em>, flags the ordering, and emits a reordered fix you can copy back. <em>Use case</em>: 'My CoT prompt works in plaintext but degrades under JSON mode' → run linter, find the inverted order, fix.",
|
| 547 |
|
| 548 |
+
// v0.8.3 — anti-bullshit pack #9: PEFT Anti-Pattern Checker
|
| 549 |
+
"modes.peft": "🔧 PEFT Lint",
|
| 550 |
+
"mode_desc.peft": "Static linter for PEFT/LoRA training scripts. Catches the silent base-model load (peft #2115), QLoRA prepare/get_peft_model ordering, target_modules/arch mismatch, and lora_alpha conventions.",
|
| 551 |
+
"peft.title": "🔧 PEFT Anti-Pattern Checker",
|
| 552 |
+
"peft.tip": "<code>get_peft_model(base, config)</code> creates a FRESH adapter — it does NOT load saved weights. Users who want to resume from a checkpoint must call <code>PeftModel.from_pretrained(base, path)</code>. peft #2115 documents the silent base-model bug. This linter scans your training script for that pattern (and 3 others: QLoRA ordering, target_modules/arch mismatch, lora_alpha ratio).",
|
| 553 |
+
"peft.desc": "<strong>Don't burn 10 hours of training on a base model.</strong> Paste your PEFT setup code — the linter flags silent base-model loads, QLoRA ordering bugs, target_modules/arch mismatches, and lora_alpha conventions.",
|
| 554 |
+
"peft.input.placeholder": "from peft import LoraConfig, get_peft_model …",
|
| 555 |
+
"peft.lint_btn": "🔍 Lint",
|
| 556 |
+
"peft.example_bug_btn": "↳ Example: silent base-load",
|
| 557 |
+
"peft.example_qlora_btn": "↳ Example: QLoRA order bug",
|
| 558 |
+
"peft.example_clean_btn": "↳ Example: clean",
|
| 559 |
+
"peft.status.done": "✅ {verdict} — {n} finding(s)",
|
| 560 |
+
"peft.line": "line {n}",
|
| 561 |
+
"peft.summary": "{total} finding(s)",
|
| 562 |
+
"peft.attribution": "Refs:",
|
| 563 |
+
"peft.detected_at_line": "appears at line",
|
| 564 |
+
"peft.suggested_fix": "Suggested:",
|
| 565 |
+
"peft.detected_arch": "Detected arch",
|
| 566 |
+
"peft.from_model_id": "(from model id",
|
| 567 |
+
"peft.your_modules": "Your target_modules",
|
| 568 |
+
"peft.expected_modules": "Expected for this arch",
|
| 569 |
+
"peft.match_ratio": "{hits} of {total} match.",
|
| 570 |
+
"peft.ratio": "ratio",
|
| 571 |
+
"peft.alpha.convention": "convention is α=2r or α=r",
|
| 572 |
+
"peft.qlora_order.detail": "prepare_model_for_kbit_training (line {prepare_line}) runs AFTER get_peft_model (line {get_peft_model_line}). Reverse the order — call prepare FIRST, then get_peft_model.",
|
| 573 |
+
"peft.no_peft_calls.detail": "No get_peft_model / PeftModel.from_pretrained / LoraConfig calls detected. Paste a PEFT/LoRA setup snippet.",
|
| 574 |
+
"peft.verdict.errors_found": "❌ Errors found",
|
| 575 |
+
"peft.verdict.warnings_only": "⚠ Warnings",
|
| 576 |
+
"peft.verdict.info_only": "ℹ Info",
|
| 577 |
+
"peft.verdict.clean": "✅ Clean — no issues detected",
|
| 578 |
+
"peft.verdict.no_peft_calls": "ℹ No PEFT calls detected",
|
| 579 |
+
"peft.verdict.empty_input": "ℹ Empty input",
|
| 580 |
+
"peft.rule.silent_base_load.label": "Silent base-model load (peft #2115)",
|
| 581 |
+
"peft.rule.silent_base_load.explain": "<code>get_peft_model(base, config)</code> creates a NEW adapter — it does NOT load saved weights. The checkpoint hint in your code suggests you want to RESUME training from a saved adapter, but this code path will quietly start fresh and overwrite the run.",
|
| 582 |
+
"peft.rule.silent_base_load.fix": "Replace <code>get_peft_model(base, config)</code> with <code>PeftModel.from_pretrained(base, path)</code> when resuming. Verify with <code>model.get_layer_status()</code> after load.",
|
| 583 |
+
"peft.rule.qlora_order.label": "QLoRA ordering bug",
|
| 584 |
+
"peft.rule.qlora_order.explain": "<code>prepare_model_for_kbit_training</code> must be called BEFORE <code>get_peft_model</code>. Reversed, the kbit prep doesn't apply to the LoRA layers and gradient computation breaks (loss → NaN, or silent training of nothing).",
|
| 585 |
+
"peft.rule.qlora_order.fix": "Reorder: <code>base = prepare_model_for_kbit_training(base)</code> then <code>model = get_peft_model(base, config)</code>.",
|
| 586 |
+
"peft.rule.target_modules_mismatch.label": "target_modules / arch mismatch",
|
| 587 |
+
"peft.rule.target_modules_mismatch.explain": "Your <code>target_modules</code> list doesn't match the conventional module names for the architecture detected in your code. PEFT will silently apply LoRA to nothing (or to the wrong layers).",
|
| 588 |
+
"peft.rule.target_modules_mismatch.fix": "Verify module names with <code>print([n for n,_ in model.named_modules()])</code> on the loaded base model, or use the architecture-specific list shown above.",
|
| 589 |
+
"peft.rule.alpha_not_2r.label": "lora_alpha ≠ 2r convention",
|
| 590 |
+
"peft.rule.alpha_not_2r.explain": "Most published LoRA recipes use either <code>α = 2r</code> (effective unit scale) or <code>α = r</code> (reduced effective LR). A custom ratio works but warrants a sanity check.",
|
| 591 |
+
"peft.rule.alpha_not_2r.fix": "Sanity-check the ratio against your reference recipe. If intentional, ignore this finding.",
|
| 592 |
+
"peft.rule.no_peft_calls.label": "No PEFT calls detected",
|
| 593 |
+
"inv.v083.peft": "<strong>🔧 PEFT Lint</strong> — catches the silent <code>get_peft_model</code> base-load (peft #2115) + QLoRA order + target_modules / arch mismatch.",
|
| 594 |
+
"help.v083.peft.title": "🔧 PEFT Anti-Pattern Checker",
|
| 595 |
+
"help.v083.peft.body": "PEFT's <code>get_peft_model(base, config)</code> creates a FRESH adapter — it does not load saved weights from a path. Users who paste tutorial code and try to resume from a checkpoint silently throw away their training. peft #2115 has the canonical bug report. This linter scans your training script for the pattern + 3 related issues (QLoRA ordering, target_modules/arch mismatch, lora_alpha ratio) and reports findings with line numbers and suggested fixes. <em>Use case</em>: before you launch a 10-hour LoRA fine-tune, paste your script — catch the silent bugs in 200ms.",
|
| 596 |
+
|
| 597 |
"inv.v081.hub": "<strong>🧭 Solutions Hub</strong> — every documented pain mapped to a tafagent mode or curated external tool. Don't reinvent — find.",
|
| 598 |
"help.v081.hub.title": "🧭 Solutions Hub",
|
| 599 |
"help.v081.hub.body": "tafagent as integrator, not silo. 30+ pains across 7 categories (eval reliability · diagnostics · setup · training · retrieval · multimodal · observability), each mapped to (a) the tafagent mode that addresses it, if any, and (b) the best-of-breed external tools the community already trusts (RAGAS, MTEB, HELM, MCP Schema Validator, llm-stats, llguidance, GlitchMiner, etc.). Search box matches across pain, scenario, and tool name. <em>Use case</em>: 'I have problem X — does tafagent solve it, and if not, who does?'",
|
|
|
|
| 1598 |
"help.v082.cot.title": "📋 Linter JSON con consciencia CoT",
|
| 1599 |
"help.v082.cot.body": "Los motores de constrained decoding (llguidance, Outlines, gramáticas SGLang) emiten propiedades JSON en el orden que declara tu schema. Si escribes <code>{ answer, reasoning }</code> el modelo se compromete con <code>answer</code> primero y el CoT se reduce a justificación post-hoc. Pega cualquier schema (o respuesta de ejemplo) — el linter clasifica cada campo como <em>razonamiento</em>, <em>respuesta</em> u <em>otro</em>, señala el ordenamiento, y emite una corrección reordenada para copiar de vuelta. <em>Caso de uso</em>: 'Mi prompt CoT funciona en texto pero degrada en modo JSON' → ejecuta linter, encuentra el orden invertido, corrige.",
|
| 1600 |
|
| 1601 |
+
// v0.8.3 — anti-bullshit pack #9: PEFT Anti-Pattern Checker
|
| 1602 |
+
"modes.peft": "🔧 PEFT Lint",
|
| 1603 |
+
"mode_desc.peft": "Linter estático para scripts de entrenamiento PEFT/LoRA. Detecta carga silenciosa del modelo base (peft #2115), orden de prepare_model_for_kbit_training/get_peft_model en QLoRA, mismatch de target_modules/arch, y conveniones de lora_alpha.",
|
| 1604 |
+
"peft.title": "🔧 Verificador de anti-patrones PEFT",
|
| 1605 |
+
"peft.tip": "<code>get_peft_model(base, config)</code> crea un adapter NUEVO — NO carga pesos guardados. Quien quiera reanudar desde un checkpoint debe llamar <code>PeftModel.from_pretrained(base, path)</code>. peft #2115 documenta el bug de carga silenciosa del modelo base. Este linter escanea tu script en busca de ese patrón (y otros 3: orden QLoRA, mismatch target_modules/arch, ratio lora_alpha).",
|
| 1606 |
+
"peft.desc": "<strong>No quemes 10 horas de entrenamiento sobre un modelo base.</strong> Pega tu código de setup PEFT — el linter señala cargas silenciosas del base, bugs de orden QLoRA, mismatches target_modules/arch, y conveniones lora_alpha.",
|
| 1607 |
+
"peft.input.placeholder": "from peft import LoraConfig, get_peft_model …",
|
| 1608 |
+
"peft.lint_btn": "🔍 Lintear",
|
| 1609 |
+
"peft.example_bug_btn": "↳ Ejemplo: carga silenciosa del base",
|
| 1610 |
+
"peft.example_qlora_btn": "↳ Ejemplo: bug de orden QLoRA",
|
| 1611 |
+
"peft.example_clean_btn": "↳ Ejemplo: limpio",
|
| 1612 |
+
"peft.status.done": "✅ {verdict} — {n} hallazgo(s)",
|
| 1613 |
+
"peft.line": "línea {n}",
|
| 1614 |
+
"peft.summary": "{total} hallazgo(s)",
|
| 1615 |
+
"peft.attribution": "Referencias:",
|
| 1616 |
+
"peft.detected_at_line": "aparece en la línea",
|
| 1617 |
+
"peft.suggested_fix": "Sugerencia:",
|
| 1618 |
+
"peft.detected_arch": "Arch detectada",
|
| 1619 |
+
"peft.from_model_id": "(desde model id",
|
| 1620 |
+
"peft.your_modules": "Tus target_modules",
|
| 1621 |
+
"peft.expected_modules": "Esperados para esta arch",
|
| 1622 |
+
"peft.match_ratio": "{hits} de {total} coinciden.",
|
| 1623 |
+
"peft.ratio": "ratio",
|
| 1624 |
+
"peft.alpha.convention": "la convención es α=2r o α=r",
|
| 1625 |
+
"peft.qlora_order.detail": "prepare_model_for_kbit_training (línea {prepare_line}) corre DESPUÉS de get_peft_model (línea {get_peft_model_line}). Invierte el orden — llama prepare PRIMERO, luego get_peft_model.",
|
| 1626 |
+
"peft.no_peft_calls.detail": "No se detectan llamadas a get_peft_model / PeftModel.from_pretrained / LoraConfig. Pega un snippet de setup PEFT/LoRA.",
|
| 1627 |
+
"peft.verdict.errors_found": "❌ Errores encontrados",
|
| 1628 |
+
"peft.verdict.warnings_only": "⚠ Avisos",
|
| 1629 |
+
"peft.verdict.info_only": "ℹ Info",
|
| 1630 |
+
"peft.verdict.clean": "✅ Limpio — sin issues detectados",
|
| 1631 |
+
"peft.verdict.no_peft_calls": "ℹ Sin llamadas PEFT detectadas",
|
| 1632 |
+
"peft.verdict.empty_input": "ℹ Entrada vacía",
|
| 1633 |
+
"peft.rule.silent_base_load.label": "Carga silenciosa del modelo base (peft #2115)",
|
| 1634 |
+
"peft.rule.silent_base_load.explain": "<code>get_peft_model(base, config)</code> crea un adapter NUEVO — NO carga pesos guardados. La pista de checkpoint en tu código sugiere que quieres REANUDAR el entrenamiento desde un adapter guardado, pero esta ruta arrancará silenciosamente desde cero y sobrescribirá la corrida.",
|
| 1635 |
+
"peft.rule.silent_base_load.fix": "Reemplaza <code>get_peft_model(base, config)</code> por <code>PeftModel.from_pretrained(base, path)</code> al reanudar. Verifica con <code>model.get_layer_status()</code> tras cargar.",
|
| 1636 |
+
"peft.rule.qlora_order.label": "Bug de orden QLoRA",
|
| 1637 |
+
"peft.rule.qlora_order.explain": "<code>prepare_model_for_kbit_training</code> debe llamarse ANTES de <code>get_peft_model</code>. Invertido, la prep kbit no aplica a las capas LoRA y el cálculo del gradiente se rompe (loss → NaN, o entrenamiento silencioso de nada).",
|
| 1638 |
+
"peft.rule.qlora_order.fix": "Reordena: <code>base = prepare_model_for_kbit_training(base)</code> luego <code>model = get_peft_model(base, config)</code>.",
|
| 1639 |
+
"peft.rule.target_modules_mismatch.label": "Mismatch target_modules / arch",
|
| 1640 |
+
"peft.rule.target_modules_mismatch.explain": "Tu lista <code>target_modules</code> no coincide con los nombres convencionales para la arquitectura detectada en tu código. PEFT aplicará LoRA silenciosamente a nada (o a las capas equivocadas).",
|
| 1641 |
+
"peft.rule.target_modules_mismatch.fix": "Verifica los nombres con <code>print([n for n,_ in model.named_modules()])</code> sobre el modelo base cargado, o usa la lista específica de la arch mostrada arriba.",
|
| 1642 |
+
"peft.rule.alpha_not_2r.label": "lora_alpha ≠ 2r (convención)",
|
| 1643 |
+
"peft.rule.alpha_not_2r.explain": "La mayoría de recetas LoRA publicadas usan o <code>α = 2r</code> (escala efectiva unitaria) o <code>α = r</code> (LR efectivo reducido). Un ratio custom funciona pero merece una verificación.",
|
| 1644 |
+
"peft.rule.alpha_not_2r.fix": "Verifica el ratio contra tu receta de referencia. Si es intencional, ignora este hallazgo.",
|
| 1645 |
+
"peft.rule.no_peft_calls.label": "Sin llamadas PEFT detectadas",
|
| 1646 |
+
"inv.v083.peft": "<strong>🔧 PEFT Lint</strong> — detecta la carga silenciosa de <code>get_peft_model</code> sobre el base (peft #2115) + orden QLoRA + mismatch target_modules / arch.",
|
| 1647 |
+
"help.v083.peft.title": "🔧 Verificador de anti-patrones PEFT",
|
| 1648 |
+
"help.v083.peft.body": "El <code>get_peft_model(base, config)</code> de PEFT crea un adapter NUEVO — no carga pesos guardados desde una ruta. Quien pega código de tutorial e intenta reanudar desde un checkpoint tira silenciosamente su entrenamiento. peft #2115 tiene el bug report canónico. Este linter escanea tu script buscando el patrón + 3 issues relacionados (orden QLoRA, mismatch target_modules/arch, ratio lora_alpha) y reporta hallazgos con números de línea y sugerencias. <em>Caso de uso</em>: antes de lanzar un fine-tune LoRA de 10 horas, pega tu script — atrapa los bugs silenciosos en 200ms.",
|
| 1649 |
+
|
| 1650 |
"inv.v081.hub": "<strong>🧭 Solutions Hub</strong> — cada pain documentado mapeado a un mode tafagent o herramienta externa curada. No reinventes — encuentra.",
|
| 1651 |
"help.v081.hub.title": "🧭 Solutions Hub",
|
| 1652 |
"help.v081.hub.body": "tafagent como integrador, no silo. 30+ pains en 7 categorías (eval reliability · diagnósticos · setup · training · retrieval · multimodal · observability), cada uno mapeado a (a) el mode tafagent que lo resuelve, si existe, y (b) las herramientas externas best-of-breed que la comunidad ya usa (RAGAS, MTEB, HELM, MCP Schema Validator, llm-stats, llguidance, GlitchMiner, etc.). Caja de búsqueda matchea pain, scenario, y nombre de herramienta. <em>Caso de uso</em>: 'tengo problema X — ¿lo resuelve tafagent, y si no, quién?'",
|
|
|
|
| 2515 |
"help.v082.cot.title": "📋 Linter JSON conscient de CoT",
|
| 2516 |
"help.v082.cot.body": "Les moteurs de décodage contraint (llguidance, Outlines, grammaires SGLang) émettent les propriétés JSON dans l'ordre que votre schema déclare. Si vous écrivez <code>{ answer, reasoning }</code> le modèle s'engage sur <code>answer</code> en premier et le CoT se réduit à une justification a posteriori. Collez n'importe quel schema (ou réponse exemple) — le linter classe chaque champ comme <em>raisonnement</em>, <em>réponse</em> ou <em>autre</em>, signale l'ordre, et émet une correction réordonnée à copier. <em>Cas d'usage</em> : 'Mon prompt CoT marche en texte brut mais dégrade en mode JSON' → lancez le linter, trouvez l'ordre inversé, corrigez.",
|
| 2517 |
|
| 2518 |
+
// v0.8.3 — anti-bullshit pack #9: PEFT Anti-Pattern Checker
|
| 2519 |
+
"modes.peft": "🔧 PEFT Lint",
|
| 2520 |
+
"mode_desc.peft": "Linter statique pour les scripts d'entraînement PEFT/LoRA. Attrape le chargement silencieux du modèle de base (peft #2115), l'ordre prepare/get_peft_model en QLoRA, le mismatch target_modules/arch, et les conventions de lora_alpha.",
|
| 2521 |
+
"peft.title": "🔧 Vérificateur d'anti-patterns PEFT",
|
| 2522 |
+
"peft.tip": "<code>get_peft_model(base, config)</code> crée un nouvel adaptateur — il ne CHARGE PAS les poids sauvegardés. Pour reprendre depuis un checkpoint il faut appeler <code>PeftModel.from_pretrained(base, path)</code>. peft #2115 documente le bug du chargement silencieux. Ce linter scanne votre script à la recherche de ce pattern (et 3 autres : ordre QLoRA, mismatch target_modules/arch, ratio lora_alpha).",
|
| 2523 |
+
"peft.desc": "<strong>Ne brûlez pas 10 heures d'entraînement sur un modèle de base.</strong> Collez votre code de setup PEFT — le linter signale les chargements silencieux du base, les bugs d'ordre QLoRA, les mismatches target_modules/arch, et les conventions lora_alpha.",
|
| 2524 |
+
"peft.input.placeholder": "from peft import LoraConfig, get_peft_model …",
|
| 2525 |
+
"peft.lint_btn": "🔍 Linter",
|
| 2526 |
+
"peft.example_bug_btn": "↳ Exemple : chargement silencieux du base",
|
| 2527 |
+
"peft.example_qlora_btn": "↳ Exemple : bug d'ordre QLoRA",
|
| 2528 |
+
"peft.example_clean_btn": "↳ Exemple : propre",
|
| 2529 |
+
"peft.status.done": "✅ {verdict} — {n} découverte(s)",
|
| 2530 |
+
"peft.line": "ligne {n}",
|
| 2531 |
+
"peft.summary": "{total} découverte(s)",
|
| 2532 |
+
"peft.attribution": "Réfs :",
|
| 2533 |
+
"peft.detected_at_line": "apparaît à la ligne",
|
| 2534 |
+
"peft.suggested_fix": "Suggéré :",
|
| 2535 |
+
"peft.detected_arch": "Arch détectée",
|
| 2536 |
+
"peft.from_model_id": "(depuis model id",
|
| 2537 |
+
"peft.your_modules": "Vos target_modules",
|
| 2538 |
+
"peft.expected_modules": "Attendus pour cette arch",
|
| 2539 |
+
"peft.match_ratio": "{hits} sur {total} correspondent.",
|
| 2540 |
+
"peft.ratio": "ratio",
|
| 2541 |
+
"peft.alpha.convention": "la convention est α=2r ou α=r",
|
| 2542 |
+
"peft.qlora_order.detail": "prepare_model_for_kbit_training (ligne {prepare_line}) s'exécute APRÈS get_peft_model (ligne {get_peft_model_line}). Inversez l'ordre — appelez prepare D'ABORD, puis get_peft_model.",
|
| 2543 |
+
"peft.no_peft_calls.detail": "Aucun appel à get_peft_model / PeftModel.from_pretrained / LoraConfig détecté. Collez un snippet de setup PEFT/LoRA.",
|
| 2544 |
+
"peft.verdict.errors_found": "❌ Erreurs trouvées",
|
| 2545 |
+
"peft.verdict.warnings_only": "⚠ Avertissements",
|
| 2546 |
+
"peft.verdict.info_only": "ℹ Info",
|
| 2547 |
+
"peft.verdict.clean": "✅ Propre — aucun problème détecté",
|
| 2548 |
+
"peft.verdict.no_peft_calls": "ℹ Aucun appel PEFT détecté",
|
| 2549 |
+
"peft.verdict.empty_input": "ℹ Entrée vide",
|
| 2550 |
+
"peft.rule.silent_base_load.label": "Chargement silencieux du modèle de base (peft #2115)",
|
| 2551 |
+
"peft.rule.silent_base_load.explain": "<code>get_peft_model(base, config)</code> crée un NOUVEL adaptateur — il NE charge PAS les poids sauvegardés. L'indice de checkpoint dans votre code suggère que vous voulez REPRENDRE l'entraînement depuis un adaptateur sauvegardé, mais ce chemin de code redémarrera silencieusement à zéro et écrasera la run.",
|
| 2552 |
+
"peft.rule.silent_base_load.fix": "Remplacez <code>get_peft_model(base, config)</code> par <code>PeftModel.from_pretrained(base, path)</code> à la reprise. Vérifiez avec <code>model.get_layer_status()</code> après le chargement.",
|
| 2553 |
+
"peft.rule.qlora_order.label": "Bug d'ordre QLoRA",
|
| 2554 |
+
"peft.rule.qlora_order.explain": "<code>prepare_model_for_kbit_training</code> doit être appelé AVANT <code>get_peft_model</code>. Inversé, la préparation kbit ne s'applique pas aux couches LoRA et le calcul du gradient casse (loss → NaN, ou entraînement silencieux de rien).",
|
| 2555 |
+
"peft.rule.qlora_order.fix": "Réordonnez : <code>base = prepare_model_for_kbit_training(base)</code> puis <code>model = get_peft_model(base, config)</code>.",
|
| 2556 |
+
"peft.rule.target_modules_mismatch.label": "Mismatch target_modules / arch",
|
| 2557 |
+
"peft.rule.target_modules_mismatch.explain": "Votre liste <code>target_modules</code> ne correspond pas aux noms de modules conventionnels pour l'architecture détectée dans votre code. PEFT appliquera LoRA silencieusement à rien (ou aux mauvaises couches).",
|
| 2558 |
+
"peft.rule.target_modules_mismatch.fix": "Vérifiez les noms avec <code>print([n for n,_ in model.named_modules()])</code> sur le modèle de base chargé, ou utilisez la liste spécifique à l'arch montrée ci-dessus.",
|
| 2559 |
+
"peft.rule.alpha_not_2r.label": "lora_alpha ≠ 2r (convention)",
|
| 2560 |
+
"peft.rule.alpha_not_2r.explain": "La plupart des recettes LoRA publiées utilisent soit <code>α = 2r</code> (échelle effective unitaire) soit <code>α = r</code> (LR effectif réduit). Un ratio custom marche mais mérite une vérification.",
|
| 2561 |
+
"peft.rule.alpha_not_2r.fix": "Vérifiez le ratio contre votre recette de référence. Si intentionnel, ignorez cette découverte.",
|
| 2562 |
+
"peft.rule.no_peft_calls.label": "Aucun appel PEFT détecté",
|
| 2563 |
+
"inv.v083.peft": "<strong>🔧 PEFT Lint</strong> — attrape le chargement silencieux de <code>get_peft_model</code> sur le base (peft #2115) + ordre QLoRA + mismatch target_modules / arch.",
|
| 2564 |
+
"help.v083.peft.title": "🔧 Vérificateur d'anti-patterns PEFT",
|
| 2565 |
+
"help.v083.peft.body": "Le <code>get_peft_model(base, config)</code> de PEFT crée un NOUVEL adaptateur — il ne charge pas les poids sauvegardés depuis un chemin. Quiconque colle du code de tuto et essaie de reprendre depuis un checkpoint jette silencieusement son entraînement. peft #2115 contient le bug report canonique. Ce linter scanne votre script à la recherche du pattern + 3 problèmes liés (ordre QLoRA, mismatch target_modules/arch, ratio lora_alpha) et rapporte les découvertes avec numéros de ligne et corrections suggérées. <em>Cas d'usage</em> : avant de lancer un fine-tune LoRA de 10 heures, collez votre script — attrapez les bugs silencieux en 200ms.",
|
| 2566 |
+
|
| 2567 |
"inv.v081.hub": "<strong>🧭 Solutions Hub</strong> — chaque pain documenté mappé à un mode tafagent ou outil externe curé. Ne réinventez pas — trouvez.",
|
| 2568 |
"help.v081.hub.title": "🧭 Solutions Hub",
|
| 2569 |
"help.v081.hub.body": "tafagent comme intégrateur, pas silo. 30+ pains à travers 7 catégories (eval reliability · diagnostics · setup · training · retrieval · multimodal · observability), chacun mappé à (a) le mode tafagent qui le résout, s'il existe, et (b) les outils externes best-of-breed que la communauté utilise déjà (RAGAS, MTEB, HELM, MCP Schema Validator, llm-stats, llguidance, GlitchMiner, etc.). La barre de recherche matche pain, scénario, et nom d'outil. <em>Cas d'usage</em> : 'j'ai le problème X — tafagent le résout-il, et sinon, qui ?'",
|
|
|
|
| 3432 |
"help.v082.cot.title": "📋 JSON CoT 感知 Linter",
|
| 3433 |
"help.v082.cot.body": "约束解码引擎(llguidance、Outlines、SGLang 语法)按 schema 声明的顺序输出 JSON 属性。如果你写 <code>{ answer, reasoning }</code>,模型先承诺 <code>answer</code>,CoT 就退化为事后辩护。粘贴任意 schema(或示例响应)——linter 把每个字段分类为<em>推理</em>、<em>答案</em>或<em>其他</em>,标记顺序,并输出可复制回去的重排修复。<em>用例</em>:『我的 CoT 提示在纯文本中正常但在 JSON 模式下退化』→ 运行 linter,找到颠倒的顺序,修复。",
|
| 3434 |
|
| 3435 |
+
// v0.8.3 — anti-bullshit pack #9: PEFT Anti-Pattern Checker
|
| 3436 |
+
"modes.peft": "🔧 PEFT Lint",
|
| 3437 |
+
"mode_desc.peft": "PEFT/LoRA 训练脚本的静态 linter。捕获基础模型的静默加载(peft #2115)、QLoRA 中 prepare/get_peft_model 顺序、target_modules/架构不匹配、以及 lora_alpha 约定。",
|
| 3438 |
+
"peft.title": "🔧 PEFT 反模式检查器",
|
| 3439 |
+
"peft.tip": "<code>get_peft_model(base, config)</code> 创建一个全新的 adapter——它不加载已保存的权重。想从 checkpoint 恢复必须调用 <code>PeftModel.from_pretrained(base, path)</code>。peft #2115 记录了基础模型静默加载的 bug。这个 linter 扫描你的训练脚本查找此模式(以及另外 3 个:QLoRA 顺序、target_modules/架构不匹配、lora_alpha 比率)。",
|
| 3440 |
+
"peft.desc": "<strong>不要在基础模型上烧掉 10 小时的训练。</strong> 粘贴你的 PEFT 设置代码——linter 会标记基础模型的静默加载、QLoRA 顺序 bug、target_modules/架构不匹配,以及 lora_alpha 约定。",
|
| 3441 |
+
"peft.input.placeholder": "from peft import LoraConfig, get_peft_model …",
|
| 3442 |
+
"peft.lint_btn": "🔍 Lint",
|
| 3443 |
+
"peft.example_bug_btn": "↳ 示例:基础模型静默加载",
|
| 3444 |
+
"peft.example_qlora_btn": "↳ 示例:QLoRA 顺序 bug",
|
| 3445 |
+
"peft.example_clean_btn": "�� 示例:干净",
|
| 3446 |
+
"peft.status.done": "✅ {verdict} — {n} 项发现",
|
| 3447 |
+
"peft.line": "第 {n} 行",
|
| 3448 |
+
"peft.summary": "{total} 项发现",
|
| 3449 |
+
"peft.attribution": "参考:",
|
| 3450 |
+
"peft.detected_at_line": "出现在第",
|
| 3451 |
+
"peft.suggested_fix": "建议:",
|
| 3452 |
+
"peft.detected_arch": "检测到的架构",
|
| 3453 |
+
"peft.from_model_id": "(来自 model id",
|
| 3454 |
+
"peft.your_modules": "你的 target_modules",
|
| 3455 |
+
"peft.expected_modules": "此架构预期",
|
| 3456 |
+
"peft.match_ratio": "{hits} / {total} 匹配。",
|
| 3457 |
+
"peft.ratio": "比率",
|
| 3458 |
+
"peft.alpha.convention": "约定为 α=2r 或 α=r",
|
| 3459 |
+
"peft.qlora_order.detail": "prepare_model_for_kbit_training(第 {prepare_line} 行)在 get_peft_model(第 {get_peft_model_line} 行)之后运行。请反转顺序——先调用 prepare,然后 get_peft_model。",
|
| 3460 |
+
"peft.no_peft_calls.detail": "未检测到 get_peft_model / PeftModel.from_pretrained / LoraConfig 调用。粘贴 PEFT/LoRA 设置代码片段。",
|
| 3461 |
+
"peft.verdict.errors_found": "❌ 发现错误",
|
| 3462 |
+
"peft.verdict.warnings_only": "⚠ 警告",
|
| 3463 |
+
"peft.verdict.info_only": "ℹ 信息",
|
| 3464 |
+
"peft.verdict.clean": "✅ 干净——未检测到问题",
|
| 3465 |
+
"peft.verdict.no_peft_calls": "ℹ 未检测到 PEFT 调用",
|
| 3466 |
+
"peft.verdict.empty_input": "ℹ 空输入",
|
| 3467 |
+
"peft.rule.silent_base_load.label": "基础模型静默加载(peft #2115)",
|
| 3468 |
+
"peft.rule.silent_base_load.explain": "<code>get_peft_model(base, config)</code> 创建一个新的 adapter——它不加载已保存的权重。你代码中的 checkpoint 提示表明你想从已保存的 adapter 恢复训练,但这个代码路径会悄悄从头开始并覆盖该次运行。",
|
| 3469 |
+
"peft.rule.silent_base_load.fix": "恢复时请用 <code>PeftModel.from_pretrained(base, path)</code> 替换 <code>get_peft_model(base, config)</code>。加载后用 <code>model.get_layer_status()</code> 验证。",
|
| 3470 |
+
"peft.rule.qlora_order.label": "QLoRA 顺序 bug",
|
| 3471 |
+
"peft.rule.qlora_order.explain": "<code>prepare_model_for_kbit_training</code> 必须在 <code>get_peft_model</code> 之前调用。反转后,kbit 准备不会应用到 LoRA 层,梯度计算会破裂(loss → NaN,或静默训练空内容)。",
|
| 3472 |
+
"peft.rule.qlora_order.fix": "重新排序:<code>base = prepare_model_for_kbit_training(base)</code> 然后 <code>model = get_peft_model(base, config)</code>。",
|
| 3473 |
+
"peft.rule.target_modules_mismatch.label": "target_modules / 架构不匹配",
|
| 3474 |
+
"peft.rule.target_modules_mismatch.explain": "你的 <code>target_modules</code> 列表与代码中检测到的架构的常规模块名不匹配。PEFT 会静默地把 LoRA 应用到无(或错误的层)。",
|
| 3475 |
+
"peft.rule.target_modules_mismatch.fix": "在加载的基础模型上用 <code>print([n for n,_ in model.named_modules()])</code> 验证模块名,或使用上面显示的特定架构列表。",
|
| 3476 |
+
"peft.rule.alpha_not_2r.label": "lora_alpha ≠ 2r(约定)",
|
| 3477 |
+
"peft.rule.alpha_not_2r.explain": "大多数已发表的 LoRA 配方使用 <code>α = 2r</code>(有效单位尺度)或 <code>α = r</code>(降低有效 LR)。自定义比率可行但值得检查。",
|
| 3478 |
+
"peft.rule.alpha_not_2r.fix": "对照参考配方核对比率。如果是有意的,忽略此发现。",
|
| 3479 |
+
"peft.rule.no_peft_calls.label": "未检测到 PEFT 调用",
|
| 3480 |
+
"inv.v083.peft": "<strong>🔧 PEFT Lint</strong> — 捕获 <code>get_peft_model</code> 在基础模型上的静默加载(peft #2115)+ QLoRA 顺序 + target_modules / 架构不匹配。",
|
| 3481 |
+
"help.v083.peft.title": "🔧 PEFT 反模式检查器",
|
| 3482 |
+
"help.v083.peft.body": "PEFT 的 <code>get_peft_model(base, config)</code> 创建一个新的 adapter——它不从路径加载已保存的权重。粘贴教程代码并尝试从 checkpoint 恢复的人会静默地丢掉训练。peft #2115 是规范的 bug 报告。这个 linter 扫描你的脚本查找该模式 + 3 个相关问题(QLoRA 顺序、target_modules/架构不匹配、lora_alpha 比率),并报告带行号和建议修复的发现。<em>用例</em>:在启动 10 小时的 LoRA fine-tune 之前,粘贴你的脚本——在 200ms 内捕获静默 bug。",
|
| 3483 |
+
|
| 3484 |
"inv.v081.hub": "<strong>🧭 Solutions Hub</strong> — 每个文档化的问题都映射到一个 tafagent 模式或精选外部工具。别重复发明 — 去找。",
|
| 3485 |
"help.v081.hub.title": "🧭 Solutions Hub",
|
| 3486 |
"help.v081.hub.body": "tafagent 作为集成者而非孤岛。30+ 问题跨 7 类别(评估可靠性 · 诊断 · 设置 · 训练 · 检索 · 多模态 · 可观测性),每个映射到(a)解决它的 tafagent 模式(若存在),以及(b)社区已信任的最佳外部工具(RAGAS、MTEB、HELM、MCP Schema Validator、llm-stats、llguidance、GlitchMiner 等)。搜索框匹配 pain、场景和工具名称。<em>用例</em>:'我有问题 X — tafagent 解决它吗,如果不,谁解决?'",
|
|
@@ -28,6 +28,7 @@ import {
|
|
| 28 |
hubStats, getCategoryMeta,
|
| 29 |
} from "./solutions_hub.js";
|
| 30 |
import { lintJsonCot, reorderJsonText, classifyFieldName } from "./json_cot_linter.js";
|
|
|
|
| 31 |
|
| 32 |
// Attach HF Hub search-as-you-type to all 5 model id inputs (Profile, Recipe,
|
| 33 |
// Unmask, Template, Quant). Hits public huggingface.co/api/models. Idempotent.
|
|
@@ -218,6 +219,7 @@ document.addEventListener("click", (e) => {
|
|
| 218 |
quant: "quant-section", drift: "drift-section", niah: "niah-section",
|
| 219 |
saturation: "saturation-section",
|
| 220 |
cot: "cot-section",
|
|
|
|
| 221 |
hub: "hub-section",
|
| 222 |
}[targetMode];
|
| 223 |
if (sectionId) {
|
|
@@ -243,7 +245,7 @@ document.querySelectorAll(".mode-btn").forEach(btn => {
|
|
| 243 |
"diagnose-section", "phase-section", "unmask-section",
|
| 244 |
"template-section", "arena-section", "contam-section",
|
| 245 |
"quant-section", "drift-section", "niah-section",
|
| 246 |
-
"saturation-section", "cot-section", "hub-section"].forEach(id => {
|
| 247 |
const el = $(id);
|
| 248 |
if (el) el.style.display = "none";
|
| 249 |
});
|
|
@@ -256,6 +258,7 @@ document.querySelectorAll(".mode-btn").forEach(btn => {
|
|
| 256 |
quant: "quant-section", drift: "drift-section", niah: "niah-section",
|
| 257 |
saturation: "saturation-section",
|
| 258 |
cot: "cot-section",
|
|
|
|
| 259 |
hub: "hub-section",
|
| 260 |
};
|
| 261 |
const sectionId = sectionMap[mode];
|
|
@@ -264,6 +267,7 @@ document.querySelectorAll(".mode-btn").forEach(btn => {
|
|
| 264 |
if (mode === "phase") initPhaseDiagram();
|
| 265 |
if (mode === "saturation") initSaturation();
|
| 266 |
if (mode === "cot") initCot();
|
|
|
|
| 267 |
if (mode === "hub") initHub();
|
| 268 |
});
|
| 269 |
});
|
|
@@ -3555,6 +3559,159 @@ $("cot-example-bad-btn")?.addEventListener("click", () => {
|
|
| 3555 |
runCotLint();
|
| 3556 |
});
|
| 3557 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3558 |
// ════════════════════════════════════════════════════════════════════
|
| 3559 |
// Bootstrap
|
| 3560 |
// ════════════════════════════════════════════════════════════════════
|
|
|
|
| 28 |
hubStats, getCategoryMeta,
|
| 29 |
} from "./solutions_hub.js";
|
| 30 |
import { lintJsonCot, reorderJsonText, classifyFieldName } from "./json_cot_linter.js";
|
| 31 |
+
import { lintPeftCode, ARCH_TARGET_MODULES } from "./peft_anti_pattern.js";
|
| 32 |
|
| 33 |
// Attach HF Hub search-as-you-type to all 5 model id inputs (Profile, Recipe,
|
| 34 |
// Unmask, Template, Quant). Hits public huggingface.co/api/models. Idempotent.
|
|
|
|
| 219 |
quant: "quant-section", drift: "drift-section", niah: "niah-section",
|
| 220 |
saturation: "saturation-section",
|
| 221 |
cot: "cot-section",
|
| 222 |
+
peft: "peft-section",
|
| 223 |
hub: "hub-section",
|
| 224 |
}[targetMode];
|
| 225 |
if (sectionId) {
|
|
|
|
| 245 |
"diagnose-section", "phase-section", "unmask-section",
|
| 246 |
"template-section", "arena-section", "contam-section",
|
| 247 |
"quant-section", "drift-section", "niah-section",
|
| 248 |
+
"saturation-section", "cot-section", "peft-section", "hub-section"].forEach(id => {
|
| 249 |
const el = $(id);
|
| 250 |
if (el) el.style.display = "none";
|
| 251 |
});
|
|
|
|
| 258 |
quant: "quant-section", drift: "drift-section", niah: "niah-section",
|
| 259 |
saturation: "saturation-section",
|
| 260 |
cot: "cot-section",
|
| 261 |
+
peft: "peft-section",
|
| 262 |
hub: "hub-section",
|
| 263 |
};
|
| 264 |
const sectionId = sectionMap[mode];
|
|
|
|
| 267 |
if (mode === "phase") initPhaseDiagram();
|
| 268 |
if (mode === "saturation") initSaturation();
|
| 269 |
if (mode === "cot") initCot();
|
| 270 |
+
if (mode === "peft") initPeft();
|
| 271 |
if (mode === "hub") initHub();
|
| 272 |
});
|
| 273 |
});
|
|
|
|
| 3559 |
runCotLint();
|
| 3560 |
});
|
| 3561 |
|
| 3562 |
+
// ════════════════════════════════════════════════════════════════════
|
| 3563 |
+
// 🔧 PEFT Anti-Pattern Checker (v0.8.3 anti-bullshit pack #9)
|
| 3564 |
+
// ════════════════════════════════════════════════════════════════════
|
| 3565 |
+
const PEFT_SEVERITY_BG = {
|
| 3566 |
+
error: "#f85149",
|
| 3567 |
+
warning: "#d29922",
|
| 3568 |
+
info: "#58a6ff",
|
| 3569 |
+
};
|
| 3570 |
+
const PEFT_VERDICT_BG = {
|
| 3571 |
+
errors_found: "#f85149",
|
| 3572 |
+
warnings_only: "#d29922",
|
| 3573 |
+
info_only: "#58a6ff",
|
| 3574 |
+
clean: "#3fb950",
|
| 3575 |
+
no_peft_calls: "#8b949e",
|
| 3576 |
+
empty_input: "#8b949e",
|
| 3577 |
+
};
|
| 3578 |
+
|
| 3579 |
+
let __peftInited = false;
|
| 3580 |
+
|
| 3581 |
+
function initPeft() {
|
| 3582 |
+
if (__peftInited) return;
|
| 3583 |
+
__peftInited = true;
|
| 3584 |
+
// No-op (no async data); placeholder kept for symmetry with other modes.
|
| 3585 |
+
}
|
| 3586 |
+
|
| 3587 |
+
function renderPeftFinding(f) {
|
| 3588 |
+
const sevBg = PEFT_SEVERITY_BG[f.severity] || "#8b949e";
|
| 3589 |
+
const sevBadge = `<span class="badge" style="background:${sevBg};">${f.severity.toUpperCase()}</span>`;
|
| 3590 |
+
const ruleLabel = t(`peft.rule.${f.rule}.label`) || f.rule;
|
| 3591 |
+
const lineLabel = f.line != null
|
| 3592 |
+
? `<span class="subtle" style="font-size:0.85em;">${tFmt("peft.line", { n: f.line }) || `line ${f.line}`}</span>`
|
| 3593 |
+
: "";
|
| 3594 |
+
const explainer = t(`peft.rule.${f.rule}.explain`) || "";
|
| 3595 |
+
const fixHint = t(`peft.rule.${f.rule}.fix`) || "";
|
| 3596 |
+
// Per-rule rendering details
|
| 3597 |
+
let detail = "";
|
| 3598 |
+
if (f.rule === "silent_base_load") {
|
| 3599 |
+
detail = `<p><code>${escapeHtml(f.params.checkpoint_hint)}</code> ${t("peft.detected_at_line") || "appears at line"} ${f.params.checkpoint_line}</p>
|
| 3600 |
+
<p><strong>${t("peft.suggested_fix") || "Suggested:"}</strong> <code>${escapeHtml(f.params.fix)}</code></p>`;
|
| 3601 |
+
} else if (f.rule === "qlora_order") {
|
| 3602 |
+
detail = `<p>${tFmt("peft.qlora_order.detail", f.params) || `prepare_model_for_kbit_training (line ${f.params.prepare_line}) runs AFTER get_peft_model (line ${f.params.get_peft_model_line}). Reverse the order.`}</p>`;
|
| 3603 |
+
} else if (f.rule === "target_modules_mismatch") {
|
| 3604 |
+
detail = `
|
| 3605 |
+
<p><strong>${t("peft.detected_arch") || "Detected arch"}:</strong> <code>${escapeHtml(f.params.detected_arch)}</code> ${t("peft.from_model_id") || "(from model id"} <code>${escapeHtml(f.params.detected_from)}</code>)</p>
|
| 3606 |
+
<p><strong>${t("peft.your_modules") || "Your target_modules"}:</strong> <code>${escapeHtml(f.params.user_modules.join(", "))}</code></p>
|
| 3607 |
+
<p><strong>${t("peft.expected_modules") || "Expected for this arch"}:</strong> <code>${escapeHtml(f.params.expected_modules.join(", "))}</code></p>
|
| 3608 |
+
<p class="subtle" style="font-size:0.85em;">${tFmt("peft.match_ratio", f.params) || `${f.params.hits} of ${f.params.total} match.`}</p>
|
| 3609 |
+
`;
|
| 3610 |
+
} else if (f.rule === "alpha_not_2r") {
|
| 3611 |
+
detail = `<p><code>r=${f.params.r}, lora_alpha=${f.params.lora_alpha}</code> → ${t("peft.ratio") || "ratio"} ${f.params.ratio}× (${t("peft.alpha.convention") || "convention is α=2r or α=r"})</p>`;
|
| 3612 |
+
} else if (f.rule === "no_peft_calls") {
|
| 3613 |
+
detail = `<p>${t("peft.no_peft_calls.detail") || "No get_peft_model / PeftModel.from_pretrained / LoraConfig calls detected. Paste a PEFT/LoRA setup snippet."}</p>`;
|
| 3614 |
+
}
|
| 3615 |
+
return `
|
| 3616 |
+
<details open class="unmask-panel" style="margin: 0.5em 0;">
|
| 3617 |
+
<summary class="unmask-panel-title">
|
| 3618 |
+
${sevBadge} <strong>${ruleLabel}</strong> ${lineLabel}
|
| 3619 |
+
</summary>
|
| 3620 |
+
${explainer ? `<p>${explainer}</p>` : ""}
|
| 3621 |
+
${detail}
|
| 3622 |
+
${fixHint ? `<p class="recipe-desc" style="margin-top:0.5em;">${fixHint}</p>` : ""}
|
| 3623 |
+
</details>
|
| 3624 |
+
`;
|
| 3625 |
+
}
|
| 3626 |
+
|
| 3627 |
+
function renderPeftResult(result) {
|
| 3628 |
+
const verdict = t(`peft.verdict.${result.code}`) || result.code;
|
| 3629 |
+
const verdictBg = PEFT_VERDICT_BG[result.code] || "#8b949e";
|
| 3630 |
+
const verdictBadge = `<span class="badge" style="background:${verdictBg};">${verdict}</span>`;
|
| 3631 |
+
const findings = result.findings || [];
|
| 3632 |
+
const findingsHtml = findings.map(renderPeftFinding).join("");
|
| 3633 |
+
const summary = result.summary
|
| 3634 |
+
? `<p class="subtle" style="font-size:0.9em;">${tFmt("peft.summary", result.summary) || `${result.summary.total} finding(s)`}</p>`
|
| 3635 |
+
: "";
|
| 3636 |
+
|
| 3637 |
+
// Source attribution
|
| 3638 |
+
const attribution = `
|
| 3639 |
+
<p class="recipe-desc subtle" style="font-size:0.82em;margin-top:1em;">
|
| 3640 |
+
${t("peft.attribution") || "Refs:"}
|
| 3641 |
+
<a href="https://github.com/huggingface/peft/issues/2115" target="_blank" rel="noopener noreferrer">peft #2115</a> ·
|
| 3642 |
+
<a href="https://huggingface.co/docs/peft/main/en/developer_guides/troubleshooting" target="_blank" rel="noopener noreferrer">PEFT troubleshooting</a> ·
|
| 3643 |
+
<a href="https://huggingface.co/docs/peft/main/en/package_reference/peft_model" target="_blank" rel="noopener noreferrer">get_layer_status / get_model_status</a>
|
| 3644 |
+
</p>
|
| 3645 |
+
`;
|
| 3646 |
+
|
| 3647 |
+
return `<div class="arena-result">
|
| 3648 |
+
<p style="font-size:1.1em;">${verdictBadge}</p>
|
| 3649 |
+
${summary}
|
| 3650 |
+
${findingsHtml}
|
| 3651 |
+
${attribution}
|
| 3652 |
+
</div>`;
|
| 3653 |
+
}
|
| 3654 |
+
|
| 3655 |
+
function runPeftLint() {
|
| 3656 |
+
const text = $("peft-input")?.value || "";
|
| 3657 |
+
const result = lintPeftCode(text);
|
| 3658 |
+
$("peft-output").innerHTML = renderPeftResult(result);
|
| 3659 |
+
$("peft-status").textContent = tFmt("peft.status.done", {
|
| 3660 |
+
verdict: t(`peft.verdict.${result.code}`) || result.code,
|
| 3661 |
+
n: result.findings?.length || 0,
|
| 3662 |
+
});
|
| 3663 |
+
}
|
| 3664 |
+
|
| 3665 |
+
const PEFT_EXAMPLE_BUG = `from peft import LoraConfig, get_peft_model
|
| 3666 |
+
from transformers import AutoModelForCausalLM
|
| 3667 |
+
|
| 3668 |
+
base = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3-8B")
|
| 3669 |
+
config = LoraConfig(
|
| 3670 |
+
r=16,
|
| 3671 |
+
lora_alpha=32,
|
| 3672 |
+
target_modules=["q_proj", "v_proj"],
|
| 3673 |
+
)
|
| 3674 |
+
model = get_peft_model(base, config)
|
| 3675 |
+
# resume from saved checkpoint?
|
| 3676 |
+
model.load_state_dict("./outputs/checkpoint-1000/adapter_model.bin")
|
| 3677 |
+
`;
|
| 3678 |
+
|
| 3679 |
+
const PEFT_EXAMPLE_QLORA = `from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
|
| 3680 |
+
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
|
| 3681 |
+
|
| 3682 |
+
bnb = BitsAndBytesConfig(load_in_4bit=True)
|
| 3683 |
+
base = AutoModelForCausalLM.from_pretrained(
|
| 3684 |
+
"meta-llama/Llama-3-8B",
|
| 3685 |
+
quantization_config=bnb,
|
| 3686 |
+
)
|
| 3687 |
+
config = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"])
|
| 3688 |
+
model = get_peft_model(base, config)
|
| 3689 |
+
# WRONG ORDER: prepare_model_for_kbit_training must come BEFORE get_peft_model
|
| 3690 |
+
model = prepare_model_for_kbit_training(model)
|
| 3691 |
+
`;
|
| 3692 |
+
|
| 3693 |
+
const PEFT_EXAMPLE_CLEAN = `from peft import PeftModel
|
| 3694 |
+
from transformers import AutoModelForCausalLM
|
| 3695 |
+
|
| 3696 |
+
base = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3-8B")
|
| 3697 |
+
# Resume from saved adapter — correct PEFT pattern.
|
| 3698 |
+
model = PeftModel.from_pretrained(base, "./outputs/checkpoint-1000")
|
| 3699 |
+
`;
|
| 3700 |
+
|
| 3701 |
+
$("peft-lint-btn")?.addEventListener("click", runPeftLint);
|
| 3702 |
+
$("peft-example-bug-btn")?.addEventListener("click", () => {
|
| 3703 |
+
$("peft-input").value = PEFT_EXAMPLE_BUG;
|
| 3704 |
+
runPeftLint();
|
| 3705 |
+
});
|
| 3706 |
+
$("peft-example-qlora-btn")?.addEventListener("click", () => {
|
| 3707 |
+
$("peft-input").value = PEFT_EXAMPLE_QLORA;
|
| 3708 |
+
runPeftLint();
|
| 3709 |
+
});
|
| 3710 |
+
$("peft-example-clean-btn")?.addEventListener("click", () => {
|
| 3711 |
+
$("peft-input").value = PEFT_EXAMPLE_CLEAN;
|
| 3712 |
+
runPeftLint();
|
| 3713 |
+
});
|
| 3714 |
+
|
| 3715 |
// ════════════════════════════════════════════════════════════════════
|
| 3716 |
// Bootstrap
|
| 3717 |
// ════════════════════════════════════════════════════════════════════
|
|
@@ -0,0 +1,331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// PEFT Anti-Pattern Checker (v0.8.3 anti-bullshit pack #9)
|
| 2 |
+
//
|
| 3 |
+
// Static linter for PEFT / LoRA training code paths. Targets the most
|
| 4 |
+
// expensive bugs the community has documented: silent base-model loads
|
| 5 |
+
// (peft #2115), QLoRA ordering errors, and arch/target_modules mismatch.
|
| 6 |
+
//
|
| 7 |
+
// Pain (Solutions Hub `peft_loading`): `get_peft_model()` called before
|
| 8 |
+
// `PeftModel.from_pretrained()` silently loads the base model and a
|
| 9 |
+
// FRESH adapter, ignoring the user's saved LoRA weights. Hours of
|
| 10 |
+
// training thrown away with no error.
|
| 11 |
+
//
|
| 12 |
+
// Source citations:
|
| 13 |
+
// - peft #2115 — original silent-load bug
|
| 14 |
+
// - https://huggingface.co/docs/peft/main/en/developer_guides/troubleshooting
|
| 15 |
+
// - PEFT `get_layer_status() / get_model_status()` runtime check
|
| 16 |
+
//
|
| 17 |
+
// Pure logic — no human strings. Returns codes+params; main.js does
|
| 18 |
+
// the i18n lookup. Same shape as json_cot_linter.js.
|
| 19 |
+
|
| 20 |
+
// =============================================================================
|
| 21 |
+
// Token/pattern definitions
|
| 22 |
+
// =============================================================================
|
| 23 |
+
|
| 24 |
+
// Rough comment + string stripping. NOT a real Python parser; we only
|
| 25 |
+
// need to remove obvious noise so regex matches don't fire inside
|
| 26 |
+
// docstrings or commented-out code. Anything still in scope after this
|
| 27 |
+
// is treated as "live" Python.
|
| 28 |
+
function stripCommentsAndStrings(code) {
|
| 29 |
+
// Remove triple-quoted strings (greedy match across newlines)
|
| 30 |
+
let s = code.replace(/"""[\s\S]*?"""/g, "");
|
| 31 |
+
s = s.replace(/'''[\s\S]*?'''/g, "");
|
| 32 |
+
// Remove single-line strings (but keep the line so line numbers stay valid)
|
| 33 |
+
s = s.replace(/"(?:\\.|[^"\\\n])*"/g, '""');
|
| 34 |
+
s = s.replace(/'(?:\\.|[^'\\\n])*'/g, "''");
|
| 35 |
+
// Remove `# ...` comments to end of line
|
| 36 |
+
s = s.replace(/#[^\n]*/g, "");
|
| 37 |
+
return s;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
function findFirstMatchLine(stripped, pattern) {
|
| 41 |
+
const lines = stripped.split("\n");
|
| 42 |
+
for (let i = 0; i < lines.length; i++) {
|
| 43 |
+
if (pattern.test(lines[i])) return i + 1; // 1-indexed
|
| 44 |
+
}
|
| 45 |
+
return null;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
function findAllMatchLines(stripped, pattern) {
|
| 49 |
+
const out = [];
|
| 50 |
+
const lines = stripped.split("\n");
|
| 51 |
+
for (let i = 0; i < lines.length; i++) {
|
| 52 |
+
if (pattern.test(lines[i])) out.push(i + 1);
|
| 53 |
+
}
|
| 54 |
+
return out;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
// Extract STRING LITERALS from the ORIGINAL code (we kept them in the
|
| 58 |
+
// raw text so we can scan their contents for adapter/checkpoint hints).
|
| 59 |
+
// Returns array of { value, line }.
|
| 60 |
+
function extractStringLiterals(code) {
|
| 61 |
+
const out = [];
|
| 62 |
+
const re = /(["'])((?:\\.|(?!\1)[^\\\n])*)\1/g;
|
| 63 |
+
const lines = code.split("\n");
|
| 64 |
+
let lineStart = 0;
|
| 65 |
+
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
| 66 |
+
const line = lines[lineIdx];
|
| 67 |
+
let m;
|
| 68 |
+
re.lastIndex = 0;
|
| 69 |
+
const lineRe = new RegExp(re.source, re.flags);
|
| 70 |
+
while ((m = lineRe.exec(line)) !== null) {
|
| 71 |
+
out.push({ value: m[2], line: lineIdx + 1 });
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
return out;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
// Extract `target_modules=[...]` literal lists. Returns array of
|
| 78 |
+
// { modules: [..], line }. Best-effort; only catches literal lists.
|
| 79 |
+
function extractTargetModules(code) {
|
| 80 |
+
const out = [];
|
| 81 |
+
const re = /target_modules\s*=\s*\[([^\]]*)\]/g;
|
| 82 |
+
let m;
|
| 83 |
+
while ((m = re.exec(code)) !== null) {
|
| 84 |
+
const inner = m[1];
|
| 85 |
+
const modules = (inner.match(/["']([^"']+)["']/g) || []).map(s => s.slice(1, -1));
|
| 86 |
+
// Compute line number
|
| 87 |
+
const before = code.slice(0, m.index);
|
| 88 |
+
const line = before.split("\n").length;
|
| 89 |
+
out.push({ modules, line });
|
| 90 |
+
}
|
| 91 |
+
return out;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
// Extract `r=N` and `lora_alpha=N` from the same call site. Best-effort.
|
| 95 |
+
function extractLoraConfig(code) {
|
| 96 |
+
const out = [];
|
| 97 |
+
// Find LoraConfig(...) calls and capture the args block (single-line or balanced).
|
| 98 |
+
const re = /LoraConfig\s*\(([^)]*)\)/g;
|
| 99 |
+
let m;
|
| 100 |
+
while ((m = re.exec(code)) !== null) {
|
| 101 |
+
const args = m[1];
|
| 102 |
+
const r = args.match(/\br\s*=\s*(\d+)/);
|
| 103 |
+
const alpha = args.match(/lora_alpha\s*=\s*(\d+)/);
|
| 104 |
+
const before = code.slice(0, m.index);
|
| 105 |
+
const line = before.split("\n").length;
|
| 106 |
+
out.push({
|
| 107 |
+
r: r ? parseInt(r[1], 10) : null,
|
| 108 |
+
lora_alpha: alpha ? parseInt(alpha[1], 10) : null,
|
| 109 |
+
line,
|
| 110 |
+
});
|
| 111 |
+
}
|
| 112 |
+
return out;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
// =============================================================================
|
| 116 |
+
// Architecture → conventional target_modules
|
| 117 |
+
// =============================================================================
|
| 118 |
+
|
| 119 |
+
// Mapping built from public PEFT docs + transformers configs. Conservative:
|
| 120 |
+
// only architectures with stable, well-documented module names. When the
|
| 121 |
+
// user's target_modules don't match the listed arch family, we flag it.
|
| 122 |
+
const ARCH_TARGET_MODULES = {
|
| 123 |
+
llama: ["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"],
|
| 124 |
+
mistral: ["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"],
|
| 125 |
+
qwen2: ["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"],
|
| 126 |
+
phi: ["q_proj","k_proj","v_proj","dense","fc1","fc2"],
|
| 127 |
+
phi3: ["qkv_proj","o_proj","gate_up_proj","down_proj"],
|
| 128 |
+
gemma: ["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"],
|
| 129 |
+
falcon: ["query_key_value","dense","dense_h_to_4h","dense_4h_to_h"],
|
| 130 |
+
bloom: ["query_key_value","dense","dense_h_to_4h","dense_4h_to_h"],
|
| 131 |
+
gpt2: ["c_attn","c_proj","c_fc"],
|
| 132 |
+
gptneox: ["query_key_value","dense","dense_h_to_4h","dense_4h_to_h"],
|
| 133 |
+
mpt: ["Wqkv","out_proj","up_proj","down_proj"],
|
| 134 |
+
};
|
| 135 |
+
|
| 136 |
+
// Token hints in HF model ids that map to the keys above. Any one of
|
| 137 |
+
// these matching is enough to claim "the user is targeting arch X".
|
| 138 |
+
const ARCH_ID_HINTS = {
|
| 139 |
+
llama: /\b(?:llama|llama-?[123]|tinyllama|vicuna|alpaca|deepseek|mixtral)\b/i,
|
| 140 |
+
mistral: /\bmistral\b/i,
|
| 141 |
+
qwen2: /\bqwen2?\b/i,
|
| 142 |
+
phi: /\bphi-?[12]\b/i,
|
| 143 |
+
phi3: /\bphi-?3\b/i,
|
| 144 |
+
gemma: /\bgemma\b/i,
|
| 145 |
+
falcon: /\bfalcon\b/i,
|
| 146 |
+
bloom: /\bbloom\b/i,
|
| 147 |
+
gpt2: /\bgpt-?2\b/i,
|
| 148 |
+
gptneox: /\b(?:gpt-?neox|pythia|dolly)\b/i,
|
| 149 |
+
mpt: /\bmpt\b/i,
|
| 150 |
+
};
|
| 151 |
+
|
| 152 |
+
function detectArch(stringLiterals) {
|
| 153 |
+
for (const lit of stringLiterals) {
|
| 154 |
+
for (const [arch, hint] of Object.entries(ARCH_ID_HINTS)) {
|
| 155 |
+
if (hint.test(lit.value)) {
|
| 156 |
+
return { arch, source: lit.value, line: lit.line };
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
}
|
| 160 |
+
return null;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
// =============================================================================
|
| 164 |
+
// Heuristics for detecting "this string is a saved adapter checkpoint path"
|
| 165 |
+
// =============================================================================
|
| 166 |
+
|
| 167 |
+
const CHECKPOINT_HINT_RE =
|
| 168 |
+
/(?:adapter[_-]?(?:config|model)|adapter\.safetensors|adapter_model\.bin|peft[_-]?model|lora[_-]?weights?|checkpoint(?:[-_/]\d+)?|\boutput[_-]?dir\b|trained?[_-]?lora)/i;
|
| 169 |
+
|
| 170 |
+
function findAdapterCheckpointHint(stringLiterals) {
|
| 171 |
+
for (const lit of stringLiterals) {
|
| 172 |
+
if (CHECKPOINT_HINT_RE.test(lit.value)) return lit;
|
| 173 |
+
}
|
| 174 |
+
return null;
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
// =============================================================================
|
| 178 |
+
// Public entry point
|
| 179 |
+
// =============================================================================
|
| 180 |
+
|
| 181 |
+
const RULES = {
|
| 182 |
+
// Strong correctness issues — almost certainly a bug
|
| 183 |
+
silent_base_load: { severity: "error" },
|
| 184 |
+
qlora_order: { severity: "error" },
|
| 185 |
+
target_modules_mismatch: { severity: "warning" },
|
| 186 |
+
// Optional / informational
|
| 187 |
+
alpha_not_2r: { severity: "info" },
|
| 188 |
+
no_peft_calls: { severity: "info" },
|
| 189 |
+
};
|
| 190 |
+
|
| 191 |
+
export function lintPeftCode(text) {
|
| 192 |
+
if (typeof text !== "string" || !text.trim()) {
|
| 193 |
+
return { code: "empty_input", findings: [] };
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
const stripped = stripCommentsAndStrings(text);
|
| 197 |
+
|
| 198 |
+
// Bail if no PEFT-related calls at all — unhelpful otherwise.
|
| 199 |
+
const hasGetPeftModel = /\bget_peft_model\s*\(/.test(stripped);
|
| 200 |
+
const hasFromPretrained = /\bPeftModel\s*\.\s*from_pretrained\s*\(/.test(stripped);
|
| 201 |
+
const hasPrepareKbit = /\bprepare_model_for_kbit_training\s*\(/.test(stripped);
|
| 202 |
+
const hasLoraConfig = /\bLoraConfig\s*\(/.test(stripped);
|
| 203 |
+
const hasBnbConfig = /\bBitsAndBytesConfig\s*\(/.test(stripped);
|
| 204 |
+
|
| 205 |
+
if (
|
| 206 |
+
!hasGetPeftModel &&
|
| 207 |
+
!hasFromPretrained &&
|
| 208 |
+
!hasPrepareKbit &&
|
| 209 |
+
!hasLoraConfig
|
| 210 |
+
) {
|
| 211 |
+
return {
|
| 212 |
+
code: "no_peft_calls",
|
| 213 |
+
findings: [{
|
| 214 |
+
rule: "no_peft_calls",
|
| 215 |
+
severity: "info",
|
| 216 |
+
line: null,
|
| 217 |
+
params: {},
|
| 218 |
+
}],
|
| 219 |
+
};
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
const findings = [];
|
| 223 |
+
const stringLiterals = extractStringLiterals(text);
|
| 224 |
+
|
| 225 |
+
// ─── Rule A: silent base-model load (peft #2115) ─────────────────────────
|
| 226 |
+
// Pattern: `get_peft_model(...)` is the only model-creation path AND
|
| 227 |
+
// there's a string literal that looks like a saved adapter path.
|
| 228 |
+
// Likely user wants to LOAD a saved adapter but is creating a new one.
|
| 229 |
+
if (hasGetPeftModel && !hasFromPretrained) {
|
| 230 |
+
const hint = findAdapterCheckpointHint(stringLiterals);
|
| 231 |
+
if (hint) {
|
| 232 |
+
const getPeftLine = findFirstMatchLine(stripped, /\bget_peft_model\s*\(/);
|
| 233 |
+
findings.push({
|
| 234 |
+
rule: "silent_base_load",
|
| 235 |
+
severity: "error",
|
| 236 |
+
line: getPeftLine,
|
| 237 |
+
params: {
|
| 238 |
+
checkpoint_hint: hint.value,
|
| 239 |
+
checkpoint_line: hint.line,
|
| 240 |
+
fix: `PeftModel.from_pretrained(base_model, ${JSON.stringify(hint.value)})`,
|
| 241 |
+
},
|
| 242 |
+
});
|
| 243 |
+
}
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
// ─── Rule B: QLoRA ordering — prepare_model_for_kbit_training AFTER get_peft_model ──
|
| 247 |
+
if (hasPrepareKbit && hasGetPeftModel) {
|
| 248 |
+
const prepLine = findFirstMatchLine(stripped, /\bprepare_model_for_kbit_training\s*\(/);
|
| 249 |
+
const peftLine = findFirstMatchLine(stripped, /\bget_peft_model\s*\(/);
|
| 250 |
+
if (prepLine !== null && peftLine !== null && prepLine > peftLine) {
|
| 251 |
+
findings.push({
|
| 252 |
+
rule: "qlora_order",
|
| 253 |
+
severity: "error",
|
| 254 |
+
line: prepLine,
|
| 255 |
+
params: {
|
| 256 |
+
prepare_line: prepLine,
|
| 257 |
+
get_peft_model_line: peftLine,
|
| 258 |
+
},
|
| 259 |
+
});
|
| 260 |
+
}
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
// ─── Rule C: target_modules / arch mismatch ─────────────────────────────
|
| 264 |
+
const targetModuleCalls = extractTargetModules(text);
|
| 265 |
+
const detectedArch = detectArch(stringLiterals);
|
| 266 |
+
if (targetModuleCalls.length > 0 && detectedArch !== null) {
|
| 267 |
+
const expected = ARCH_TARGET_MODULES[detectedArch.arch];
|
| 268 |
+
if (expected) {
|
| 269 |
+
const expectedSet = new Set(expected);
|
| 270 |
+
for (const tm of targetModuleCalls) {
|
| 271 |
+
if (tm.modules.length === 0) continue;
|
| 272 |
+
const hits = tm.modules.filter(m => expectedSet.has(m)).length;
|
| 273 |
+
const ratio = hits / tm.modules.length;
|
| 274 |
+
// Less than half of user's specified modules are in the expected list.
|
| 275 |
+
if (ratio < 0.5) {
|
| 276 |
+
findings.push({
|
| 277 |
+
rule: "target_modules_mismatch",
|
| 278 |
+
severity: "warning",
|
| 279 |
+
line: tm.line,
|
| 280 |
+
params: {
|
| 281 |
+
user_modules: tm.modules,
|
| 282 |
+
detected_arch: detectedArch.arch,
|
| 283 |
+
detected_from: detectedArch.source,
|
| 284 |
+
expected_modules: expected,
|
| 285 |
+
hits,
|
| 286 |
+
total: tm.modules.length,
|
| 287 |
+
},
|
| 288 |
+
});
|
| 289 |
+
}
|
| 290 |
+
}
|
| 291 |
+
}
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
// ─── Rule D: lora_alpha ≠ 2*r convention ────────────────────────────────
|
| 295 |
+
// Common rule of thumb: alpha = 2*r gives roughly unit-scale LoRA.
|
| 296 |
+
// alpha = r is also seen but reduces effective LR. Anything else is
|
| 297 |
+
// worth surfacing as info.
|
| 298 |
+
const loraCfgs = extractLoraConfig(text);
|
| 299 |
+
for (const cfg of loraCfgs) {
|
| 300 |
+
if (cfg.r != null && cfg.lora_alpha != null) {
|
| 301 |
+
const ratio = cfg.lora_alpha / cfg.r;
|
| 302 |
+
if (ratio !== 1 && ratio !== 2) {
|
| 303 |
+
findings.push({
|
| 304 |
+
rule: "alpha_not_2r",
|
| 305 |
+
severity: "info",
|
| 306 |
+
line: cfg.line,
|
| 307 |
+
params: {
|
| 308 |
+
r: cfg.r,
|
| 309 |
+
lora_alpha: cfg.lora_alpha,
|
| 310 |
+
ratio: Math.round(ratio * 100) / 100,
|
| 311 |
+
},
|
| 312 |
+
});
|
| 313 |
+
}
|
| 314 |
+
}
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
// ─── Aggregate verdict code ──────────────────────────────────────────────
|
| 318 |
+
let code;
|
| 319 |
+
if (findings.length === 0) {
|
| 320 |
+
code = "clean";
|
| 321 |
+
} else if (findings.some(f => f.severity === "error")) {
|
| 322 |
+
code = "errors_found";
|
| 323 |
+
} else if (findings.some(f => f.severity === "warning")) {
|
| 324 |
+
code = "warnings_only";
|
| 325 |
+
} else {
|
| 326 |
+
code = "info_only";
|
| 327 |
+
}
|
| 328 |
+
return { code, findings, summary: { total: findings.length } };
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
export { ARCH_TARGET_MODULES, ARCH_ID_HINTS };
|