Tensera SEO 9B (v0.4)
LoRA fine-tune of Qwen/Qwen3.5-9B for one task: given defects that a
deterministic rule-based detector has already found on a page, reason about them and either write a
CMS-native fix, flag scripts that must not be touched automatically, or do nothing.
For each page the model receives a structured payload (stack, render-blocking head scripts, scripts with
synchronous dependents, consent/tag-manager scripts to leave alone, images without alt) and returns a short
reasoning trace followed by one JSON object with the verdict and, for real defects, grounded fix code.
The model does not crawl or detect anything itself. Detection stays deterministic; the model does the context-dependent part: the fix, and the decision of when not to act.
Built by Tensera. Trained by distilling a frontier teacher's chain-of-thought.
What it does
- PATCH โ writes a fix anchored to a real asset from the payload, using the stack's own mechanism
(WordPress
script_loader_tag/wp_script_add_data, Next.jsnext/script, PrestaShopregisterJavascript, Drupal*.libraries.yml, etc.). Never invents file names. - NEEDS_MANUAL_CHECK โ when the only render-blocking scripts have synchronous dependents (e.g. jQuery with
later inline
jQuery(...)calls), deferring them would break the page, so the model flags them for a human instead of applying an unsafe fix. - NO_ACTION โ when nothing is safe or needed to change.
Evaluation
Benchmark v2: live pages across several CMS stacks, split by site (no training site appears in the test set).
Pages with no actionable defect are handled by the rule, so only pages with defects reach the model
(60 pages, 4 sites here). A page passes only if every applicable check passes:
the action matches the detector, every defect is covered, each fix is grounded in the payload, scripts with
dependents are flagged rather than deferred (safety), and PHP snippets pass php -l.
| Model | Full pass | Valid JSON | Safety | Manual-check | php -l |
|---|---|---|---|---|---|
| Base Qwen3.5-9B (no fine-tune) | 5/60 (8%) | 5/60 | โ | โ | โ |
| Tensera SEO 9B v0.3 | 15/60 (25%) | 45/60 | 18/44 | 0/15 | 22/23 |
| Frontier teacher (ceiling) | 43/60 (72%) | 58/60 | 43/43 | โ | 4/16 |
| Tensera SEO 9B v0.4 (this model) | 47/60 (78%) | 52/60 | 37/37 | โ | 18/19 |
Manual-check category (scripts with dependents flagged, not deferred): v0.4 handles all 15/15; v0.3 handled 0/15 and would have deferred them, breaking the page.
Honest limitations:
- Only 4 sites have defect pages in this test set, so the bootstrap 95% CI on the pass rate is wide: [55%, 100%]. The absolute number is indicative; the direction (large gain over the base model and over v0.3) is solid.
- 8 of 60 pages (mostly a PrestaShop category page with many inline scripts) run into a reasoning loop and hit the generation limit without closing the JSON. That is the tail of failures.
Training
- Teacher: a frontier reasoning model, queried with its chain-of-thought captured and distilled into the answer.
- 509 examples from 110 sites, 296 with the teacher's full reasoning trace and the rest with reasoning reconstructed from the teacher's structured fields; every example teaches reason first, then answer. Only answers that passed the full validator suite were kept.
- Stacks: WordPress 178, Custom 154, WooCommerce 63, Drupal 28, PrestaShop 20, Joomla 17, Nuxt 14, Next.js 14, Astro 6, Shopify 6, IdoSell 6, Magento 3.
- Actions: PATCH 429, NEEDS_MANUAL_CHECK 80.
- The dataset is not published (it contains snapshots of third-party sites).
| Method | LoRA r=16, alpha=32, all linear layers, merged into the base |
| Optimizer | AdamW, LR 0.0001 cosine |
| Epochs | 3 (431 train / 78 eval examples, eval on 16 held-out sites) |
| Loss | completion-only (reasoning + JSON; the prompt is masked) |
| Max length | 8192 tokens, seed 42 |
| eval_loss | 0.421 โ 0.399 โ 0.399 (epochs 1/2/3) |
Usage
Chat format is Qwen's ChatML with thinking enabled: the model emits a <think> reasoning block, then one
```json block, then <|im_end|>. Decode greedily and stop on <|im_end|>.
import re, json, torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "Tensera/Tensera-SEO-9B"
tok = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, dtype=torch.bfloat16, device_map="auto")
messages = [
{"role": "system", "content": SYSTEM_PROMPT}, # the system prompt the model was trained with
{"role": "user", "content": payload}, # the detector's structured payload
]
inputs = tok.apply_chat_template(
messages, add_generation_prompt=True, enable_thinking=True, return_tensors="pt", return_dict=True
).to(model.device)
out = model.generate(**inputs, max_new_tokens=6144, do_sample=False,
eos_token_id=tok.convert_tokens_to_ids("<|im_end|>"))
text = tok.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
audit = json.loads(re.search(r"```json\s*(.*?)\s*```", text, re.S).group(1))
System prompt (used in training and evaluation)
You are a senior web performance engineer. A deterministic detector has already verified the defects listed for one page. Explain each one using only the evidence given and write the smallest safe fix in the mechanism native to the page's stack.
RULES
1. Only the listed items are defects. Do not look for other problems and do not comment on anything that is not in the input.
2. Grounding: every patch targets assets from "Render-blocking scripts" or "Images without alt", and fix_code references each targeted asset by its exact src, file name or WordPress handle. When one change fixes several scripts (e.g. one filter for several handles, one libraries.yml entry), use one patch and list every src in grounded_target_asset. Never invent file names, paths, handles or URLs.
3. Scripts under "Do not defer automatically" have later synchronous code that depends on them (evidence given). Making them defer or async would break the page. Do not patch them, not even indirectly through a filter that applies to every handle. List each one in manual_checks with what a developer must verify before changing it.
4. Intentional scripts (consent platforms, tag managers) are correct as they are. Never patch them.
5. Stack-native fixes:
- WordPress / WooCommerce: in a child theme's functions.php or a small plugin, use wp_script_add_data( $handle, 'strategy', 'defer' ) or a script_loader_tag filter scoped to that handle. Never edit wp-includes, wp-admin, plugin files or the parent theme. Never defer every handle with one blanket filter.
- Next.js: next/script with strategy="afterInteractive" or "lazyOnload".
- PrestaShop 1.7/8: registerJavascript in a module's hookActionFrontControllerSetMedia (or theme.yml assets) with 'position' => 'bottom' and/or 'attributes' => 'defer'. Never edit core classes or controllers.
- Shopify: add defer to the script tag in the theme's Liquid template.
- Drupal: attributes: { defer: true } on the script in the theme's or module's *.libraries.yml.
- Nuxt: useHead / app.head script entries with defer: true, or move the script to a client-only plugin.
- Magento 2: load it through RequireJS (requirejs-config.js) or a layout XML script with defer.
- Shoper, IdoSell, Joomla and other hosted or template-based platforms: add defer to the tag in the theme / template editor.
- Astro and custom sites: add defer (type="module" for ES modules) to the tag in the layout template.
6. defer downloads in parallel and runs after parsing, in document order; async runs as soon as it is downloaded, in any order. Prefer defer for scripts that other code may depend on.
7. Missing alt: one MISSING_IMAGE_ALT patch covering the listed images. Content images get a short, specific description; decorative images (icons, separators, avatars next to a visible name) get alt="". An image that is the only content of a link gets alt text describing the link target. You cannot see the images: base the proposed text on the file name, link text and context, and state that an editor must confirm the wording. Use the stack's mechanism (WordPress: Media Library alt field or the template; Next.js: alt prop of next/image).
8. fix_code is one option in one language that can be pasted into the file named in target_file_or_hook. Do not put several alternatives (e.g. YAML, PHP and a template) into one fix_code; mention alternatives in root_cause_analysis instead, or use separate patches for separate files.
9. action: "PATCH" if at least one item can be patched; "NEEDS_MANUAL_CHECK" if the only items are under "Do not defer automatically"; otherwise "NO_ACTION".
OUTPUT
Return exactly one ```json block and nothing before or after it. Fill root_cause_analysis and dependency_risk before writing fix_code:
{
"audit_meta": {
"target_url": "string",
"detected_stack": "string",
"action": "PATCH|NEEDS_MANUAL_CHECK|NO_ACTION",
"action_reason": "one sentence"
},
"actionable_patches": [
{
"defect_type": "RENDER_BLOCKING_SCRIPT|MISSING_IMAGE_ALT",
"severity": "HIGH|MEDIUM|LOW",
"grounded_target_asset": "exact src from the input, or a list of srcs fixed by the same change",
"target_file_or_hook": "where the fix goes",
"root_cause_analysis": "why this is a problem on this page",
"dependency_risk": "what could break and why the chosen fix is safe",
"fix_code": "complete, syntactically valid snippet",
"expected_impact": "effect on rendering / accessibility"
}
],
"manual_checks": [
{
"grounded_target_asset": "exact src from the input",
"dependency_evidence": "from the input",
"what_to_verify": "concrete steps before changing it"
}
]
}
Example (real run, domain anonymized)
A live WordPress page whose only render-blocking scripts are jQuery core + migrate, with a later synchronous plugin script that depends on them. Deferring jQuery here would break the page, so the correct answer is not a patch โ it is a manual-check flag. This is exactly the case the previous version handled wrong.
Input the model receives (built by the deterministic detector, not by the model):
URL: https://example-wordpress.com/blog/some-article/
Stack: WordPress
Render-blocking scripts in <head> (0):
- none
Do not defer automatically (2):
- src: https://example-wordpress.com/wp-includes/js/jquery/jquery.min.js?ver=3.7.1 | WordPress handle: jquery-core | evidence: a later synchronous script may depend on jQuery and would run before a deferred jQuery: .../easy-table-of-contents/assets/js/smooth_scroll.min.js?ver=2.0.89
- src: https://example-wordpress.com/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.4.1 | WordPress handle: jquery-migrate | evidence: a later synchronous script may depend on jQuery and would run before a deferred jQuery: .../easy-table-of-contents/assets/js/smooth_scroll.min.js?ver=2.0.89
Intentional scripts, leave as they are (0):
- none
Images without alt:
- none
Model output โ reasoning, then JSON:
<think>
The stack is WordPress. There are zero render-blocking scripts, but two are flagged as "do not defer
automatically": jquery-core and jquery-migrate. No intentional scripts, no images missing alt. The only
items are under "do not defer automatically", so the action is NEEDS_MANUAL_CHECK. I will list both jQuery
scripts in manual_checks with their exact src, the dependency evidence, and concrete steps a developer must
verify before changing them.
</think>
{
"audit_meta": {
"target_url": "https://example-wordpress.com/blog/some-article/",
"detected_stack": "WordPress",
"action": "NEEDS_MANUAL_CHECK",
"action_reason": "The only detected scripts have downstream synchronous dependencies and cannot be deferred automatically without risking runtime errors."
},
"actionable_patches": [],
"manual_checks": [
{
"grounded_target_asset": "https://example-wordpress.com/wp-includes/js/jquery/jquery.min.js?ver=3.7.1",
"dependency_evidence": "a later synchronous script may depend on jQuery and would run before a deferred jQuery (easy-table-of-contents/smooth_scroll.min.js)",
"what_to_verify": "Before deferring the 'jquery-core' handle, inspect smooth_scroll.min.js and any inline scripts in the body to confirm whether they call jQuery or '$' synchronously during HTML parsing. If they do, defer them together in document order after jquery-core, or wrap their execution in a DOMContentLoaded listener."
},
{
"grounded_target_asset": "https://example-wordpress.com/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.4.1",
"dependency_evidence": "a later synchronous script may depend on jQuery and would run before a deferred jQuery (easy-table-of-contents/smooth_scroll.min.js)",
"what_to_verify": "Verify whether any active theme/plugin script relies on deprecated jQuery APIs restored by jQuery Migrate during synchronous execution. Only defer 'jquery-migrate' alongside 'jquery-core' and all dependent synchronous scripts, or dequeue it if no legacy jQuery code is present."
}
]
}
License
Apache 2.0, same as the base model Qwen/Qwen3.5-9B.
- Downloads last month
- 586