CoolFace
Apppublic

AS0711/a11y-seo-retrofitter

sourceHugging Facemitupdated 5mo agoView on Hugging Face
0likes
App README

A11y & SEO Web Retrofitter

An OpenEnv-compatible reinforcement learning environment where AI agents learn to fix web accessibility (WCAG 2.1) and SEO violations through minimal, surgical HTML edits.

What is this?

The A11y & SEO Web Retrofitter is a structured RL environment that trains AI agents to identify and correct accessibility and SEO violations in real-world HTML pages — without breaking anything else.

At each step the agent receives a structured observation (annotated DOM, list of detected violations, per-check scores) and selects from a constrained set of three actions. The environment scores every action immediately and returns a shaped reward signal, enabling standard policy-gradient and LLM-based agent training loops.

The design philosophy is surgical precision over wholesale rewriting. Actions are small and targeted — the kind a careful developer would make. Large structural rewrites are penalised.

The environment is fully OpenEnv-compatible, exposing a REST API on port 7860 with /reset, /step, /state, /health, and /tasks endpoints.


Why does this matter?

An estimated 96.3% of homepages have detectable WCAG 2.1 failures (WebAIM Million Report, 2024). For the 1.3 billion people living with a disability, these are real barriers — not inconveniences.

Fixing legacy HTML at scale is expensive and slow. Traditional linters find violations but cannot fix them intelligently. LLMs powerful enough to understand HTML often rewrite entire pages, breaking layouts, scripts, and valid structure in the process.

This environment formalises the repair problem: given a page with known violations, can an agent learn a policy that maximises accessibility and SEO scores while minimising edit distance from the original?


Observation Space

FieldTypeDescription
dom_skeletonstringFull HTML annotated with data-env-id on every element
audit_reportlist[Violation]Violations with rule_id, severity, and target_id
scoresdict[str, float]Per-check scores in [0.0, 1.0]
overall_scorefloatTask-weighted aggregate score
step_countintSteps taken so far in the episode

Action Space

update_element_attribute

Modify or add an HTML attribute on a specific element.

ParamTypeDescription
target_idstrdata-env-id of the element to modify
attributestrAttribute name (e.g. "alt", "lang", "aria-label")
new_valuestrNew attribute value
reasoningstrAgent justification

insert_meta_tag

Insert a new <meta> element into <head>.

ParamTypeDescription
namestrname= or property= value (e.g. "description", "og:title")
contentstrcontent= value
reasoningstrAgent justification

swap_node_tag

Replace an element's HTML tag while preserving its content and attributes.

ParamTypeDescription
target_idstrdata-env-id of the element
new_tagstrReplacement tag name (e.g. "h2", "button")
reasoningstrAgent justification

Tasks

Easy — Isolated Semantic Errors

File: corpus/easy_task.html — Blog post: "The Future of Renewable Energy"

#FlawFix Action
1<title> tag missinginsert_meta_tag or manual DOM fix
2lang attribute missing from <html>update_element_attribute
3All <img> missing alt attributeupdate_element_attribute × N
4One <button> has no accessible labelupdate_element_attribute (aria-label)

Checks: page-title, html-lang, img-alt, button-label


Medium — Structural Flaws

File: corpus/medium_task.html — Product landing page: "FocusFlow"

#FlawFix Action
1Heading hierarchy breaks: h1→h4→h4→h2swap_node_tag
2Two paragraphs with color:#aaaaaa (2.3:1 contrast)update_element_attribute (style)
3<meta name="description"> missinginsert_meta_tag
4<meta name="viewport"> missinginsert_meta_tag

Checks: heading-order, color-contrast, meta-description, meta-viewport


Hard — Full Production Optimisation

File: corpus/hard_task.html — E-commerce page: "Velvet Roast"

#FlawFix Action
1<div role="button"> — no tabindex, no aria-labelupdate_element_attribute × 2
2<label for="X"> points to non-existent idupdate_element_attribute (for=)
3Two <h1> elements on the same pageswap_node_tag
4All OG meta tags missinginsert_meta_tag × 3
5<meta name="viewport"> missinginsert_meta_tag
6Product image has alt="" (wrong for meaningful image)update_element_attribute

Checks: aria-div-button, label-for, duplicate-h1, og-title, og-description, og-image, meta-viewport, meaningful-img-alt


Reward Function

ComponentRangeDescription
Progress[-1.0, +1.0](new_score − old_score) × 10, clipped
Step penalty-0.01Constant cost per step — rewards efficiency
Edit penalty[−0.5, 0]−min(edit_chars / 500, 0.5) — penalises large edits
Structure penalty−1.0 or 0Fires if a previously-passing check regresses hard

Total range: [-1.5, +1.0]


Setup & Usage

Run with Docker

bash
docker build -t a11y-env .
docker run -p 7860:7860 a11y-env

Run locally

bash
pip install -r requirements.txt
uvicorn main:app --port 7860 --reload

Run inference

bash
export API_BASE_URL=https://api.groq.com/openai/v1
export MODEL_NAME=llama-3.3-70b-versatile
export HF_TOKEN=your_groq_api_key_here
python inference.py

Quick API test

bash
# Reset to easy task
curl -X POST http://localhost:7860/reset \
  -H 'Content-Type: application/json' \
  -d '{"task_id": "easy"}'

# Apply an action
curl -X POST http://localhost:7860/step \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "update_element_attribute",
    "target_id": "env-1",
    "attribute": "lang",
    "new_value": "en",
    "reasoning": "Add missing lang attribute"
  }'

# Check state
curl http://localhost:7860/state

Project Structure

a11y-seo-retrofitter/
├── inference.py          # Baseline agent inference loop
├── main.py               # FastAPI application
├── openenv.yaml          # OpenEnv manifest
├── Dockerfile
├── requirements.txt
├── environment/
│   ├── env.py            # Core RL environment (reset/step/state)
│   ├── models.py         # Pydantic models (Observation, Action, StepResult)
│   ├── actions.py        # Action executor
│   ├── injector.py       # data-env-id DOM annotator
│   └── reward.py         # Reward function
├── graders/
│   ├── base.py           # Abstract BaseGrader
│   ├── easy.py           # Easy task grader (4 checks)
│   ├── medium.py         # Medium task grader (4 checks)
│   └── hard.py           # Hard task grader (8 checks)
├── audit/
│   └── checker.py        # 15-check WCAG/SEO audit engine
└── corpus/
    ├── easy_task.html    # Blog post with 4 injected flaws
    ├── medium_task.html  # Landing page with 4 structural flaws
    └── hard_task.html    # E-commerce page with 6 ARIA/OG flaws

Baseline Scores

Model: Qwen/Qwen2.5-Coder-7B-Instruct via Hugging Face Inference Router Environment: https://AS0711-a11y-seo-retrofitter.hf.space
TaskFinal ScoreSteps TakenSuccess
Easy1.006✅ True
Medium1.005✅ True
Hard1.0011✅ True

Notes:

  • Easy fixed in 6 steps: lang, 3× img-alt, page-title (via insert_meta_tag name=title), button aria-label.
  • Medium fixed in 5 steps: meta-description, heading order, color contrast, meta-viewport.
  • Hard fixed in 11 steps: meaningful-img-alt, duplicate-h1, aria-div-button (tabindex + aria-label), OG tags (title/description/image), label-for, meta-viewport.

License

MIT