Skip to main content

Privacy-first self-healing agent for Playwright tests. AI-powered, 100% local, zero data exposure.

Project description

Healix

PyPI version Python License: MIT CI

Healix is a privacy-first, self-healing web automation agent. It fixes broken Playwright selectors using AI — and everything runs 100% on your machine. No data ever leaves your device.

Built for teams in healthcare, fintech, government, and enterprise where sending DOM data to cloud APIs is not an option.

The Problem

Web automation is fragile. Selectors break when:

  • Developers change class names
  • DOM structure shifts
  • Content loads dynamically
  • A/B tests alter page layouts

Why Healix?

  • Zero data exposure — AI runs locally via Ollama. No API keys, no cloud, no telemetry. Your DOM never leaves localhost
  • Compliance ready — Safe for HIPAA, SOC 2, PCI-DSS, and air-gapped environments
  • Zero config — Drop-in replacement for flaky selectors, no test rewrites needed
  • Browser-aware — Separate caching per browser engine (Chromium, Firefox, WebKit)
  • Learns over time — Caches successful fixes for instant replay on subsequent runs
  • Transparent — Logs every decision with confidence scores and reasoning

The Solution

Healix uses an agentic loop to automatically detect and fix selector failures:

  1. Observe — Analyze the current page state and DOM
  2. Reason — Use AI to determine the correct selector/action
  3. Act — Execute the corrected action
  4. Verify — Confirm the action succeeded
  5. Learn — Cache successful fixes for future use

Operational Workflow

                        Test Failure Detected
                                |
                          Check Cache
                         /          \
                      Hit            Miss
                       |               |
               Apply Cached       DOM Scrubbing
                 Selector        & Minification
                       |               |
                Re-run Test       Query Ollama
                 /       \        (Local LLM)
            Success     Fail          |
               |          \    Confidence Scoring
               |           \      /          \
               |            \  High          Low
               |             \  |             |
               |          Re-run with     Manual Review
               |           AI Fix          Required
               |           /    \             |
               |       Success  Fail          |
               |          |       |           |
               |    Update Cache  |           |
               |          |       |           |
                \         |      /           /
                 \        |     /           /
              Healing  ----+----    -------
             Successful    |
                           |
                  Log Code Proposal
                           |
                    Action Complete

Architecture

healix/
├── .github/workflows/
│   └── ci.yml                 # CI: tests on Python 3.9/3.11/3.12
├── src/healix/
│   ├── __init__.py            # Public API (smart_click, Healix)
│   ├── engine.py              # Core self-healing logic
│   ├── prometheus_metrics.py  # Optional: push heal metrics to Pushgateway
│   └── utilities/
│       └── dom_scrubber.py    # DOM cleaning helpers
├── tests/
│   ├── integration/
│   │   └── test_login.py      # Live browser healing demo
│   └── unit/
│       └── test_engine.py     # 18 unit tests (no Ollama needed)
├── CHANGELOG.md               # Version history
├── LICENSE                    # MIT
├── pyproject.toml             # Package config & dependencies
└── README.md

~/.healix/                     # Runtime data (created automatically)
├── cache.json                 # Persistent selector cache
└── proposals.json             # Logged code fix suggestions

Prerequisites

  • Python 3.9 or higher
  • Ollama running locally — Install Ollama, then:
    ollama serve
    ollama pull qwen2.5-coder:7b
    

Installation

pip install healix-ai

Or install from source:

git clone https://github.com/kunaal-ai/healix.git
cd healix
pip install -e .

Make sure to install Playwright browsers:

playwright install chromium

Quick Start

import asyncio
from playwright.async_api import async_playwright
from healix import smart_click

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        page = await browser.new_page()

        await page.goto("https://the-internet.herokuapp.com/login")

        # These selectors are intentionally wrong — Healix will heal them
        await smart_click(page, "input#wrong-id", text_to_fill="tomsmith")
        await smart_click(page, "input[name='bad_field']", text_to_fill="SuperSecretPassword!")
        await smart_click(page, "button.does-not-exist")

        print("Login healed!" if await page.locator(".flash.success").is_visible() else "Failed")
        await browser.close()

if __name__ == "__main__":
    asyncio.run(main())

Zero-Refactor Integration (New!)

Already have a large project? You don't need to rewrite your Page Objects. Just "patch" the page at the start of your test:

from healix import Healix
from playwright.sync_api import expect

def test_login(page):
    # Patch the page once
    page = Healix.patch(page)
    
    # 0 changes needed to your Page Objects!
    # page.locator() now automatically produces self-healing locators.
    login_page = LoginPage(page)
    
    login_page.login("user", "pass")
    expect(login_page.welcome_msg).to_be_visible()

Advanced Usage

Self-Healing Assertions

Standard Playwright assertions (expect) are strict. If a selector breaks, the test fails immediately. Healix provides smart_locator to fix this:

from healix import smart_locator
from playwright.async_api import expect

# ❌ Standard: Fails immediately if #header-v1 is missing
# await expect(page.locator("#header-v1")).to_have_text("Welcome")

# ✅ Healix: Finds the element (e.g. #header-v2) using AI, then asserts
header = await smart_locator(page, "#header-v1")
await expect(header).to_have_text("Welcome")

Page Object Model (POM)

Healix works great with Page Objects. Since AI healing is async, use async methods instead of properties:

from healix import smart_locator, smart_click

class LoginPage:
    def __init__(self, page):
        self.page = page

    # Action: Use smart_click
    async def login(self, user, pw):
        await smart_click(self.page, "#username", text_to_fill=user)
        await smart_click(self.page, "#password", text_to_fill=pw)
        await smart_click(self.page, "#login-btn")

    # element: Use smart_locator
    async def get_welcome_message(self):
        # Heals selector if it breaks
        return await smart_locator(self.page, ".welcome-text")

Real-World Scenarios

Scenario A: The "No-ID" Heuristic

Problem: You used a role-based locator, but the developer changed the text.

# Broken: The button text changed from "Bill Pay" to "Make Payment"
# self.page.get_by_role("link", name="Bill Pay")
await smart_click(page, 'internal:role=link[name="Bill Pay"]')

Healix logic: AI sees the href="/billpay" and the new "Make Payment" text. It matches them semantically and suggests the update.

Scenario B: Cascading Failure (Parent ID Shift)

Problem: A parent container's ID changed, breaking all child selectors.

# Broken: #billpayResult was renamed to #payment-status
# expect(page.locator("#billpayResult h1.title")).to_have_text("Complete")
locator = await healed_locator(page, "#billpayResult h1.title")
await expect(locator).to_have_text("Complete")

Healix logic: AI focuses on the target h1.title. It finds the title element on the page and identifies its new parent #payment-status automatically.

Scenario C: Dynamic/Randomized IDs

Problem: Elements have IDs like btn-12345 that change every deployment.

# Broken: ID is now btn-99887
await smart_click(page, "#btn-12345")

Healix logic: AI ignores the random suffix and matches via class names, labels, and position. It will suggest a more stable selector like button.primary[type="submit"].

Comparison: When to use what?

Goal Use this API Why?
Click/Type smart_click() Handled in one line. Best for actions.
Verify Text healed_locator() Returns a Playwright Locator for expect().
Page Objects healed_locator() Allows defining lazy, healed elements inside class methods.

How It Works

DOM Cleaning & Privacy

  • Strips scripts, styles, and heavy elements to save tokens
  • Focuses on actionable elements (buttons, links, inputs)
  • All processing happens in-memory on your machine

AI-Powered Reasoning

  • Uses local LLM (Ollama) for selector analysis — no external API calls
  • Considers both technical errors and visible page state
  • Returns confidence scores and explanations

Persistent Learning

  • Caches successful fixes in ~/.healix/cache.json
  • Gets faster over time as it learns common patterns
  • Maintains context across test runs

Privacy & Security

Healix was designed from the ground up for zero-data-exposure environments:

What Where it runs Data leaves device?
AI inference (Ollama) localhost:11434 No
DOM analysis In-process Python No
Selector cache ~/.healix/ No
Browser automation Local Playwright No
  • No cloud APIs — The LLM runs entirely on your hardware via Ollama
  • No telemetry — Healix collects zero analytics or usage data
  • No network calls — The only outbound traffic is your test navigating to its target URL
  • No API keys — Nothing to configure, nothing to leak

This makes Healix safe for:

  • Healthcare (HIPAA) — Patient portals, EHR systems
  • Finance (SOC 2, PCI-DSS) — Banking apps, payment flows
  • Government (FedRAMP) — Internal tools, classified environments
  • Enterprise — Any org that prohibits sending DOM/HTML to third-party APIs

Configuration

Healix uses Ollama for local AI inference:

from healix import Healix

# Default model: qwen2.5-coder:7b
healix = Healix(model="your-preferred-model")

Make sure Ollama is running:

ollama serve

Prometheus & Grafana (optional)

If you already use Prometheus and Grafana, Healix can push heal metrics so you can see them on your dashboards.

Requirements: Install prometheus_client and set a Pushgateway URL:

pip install prometheus-client
export HEALIX_PUSHGATEWAY_URL="http://localhost:9091"   # or PUSHGATEWAY_URL

If prometheus_client is not installed or the URL is not set, Healix runs as usual and does not push any metrics.

Metrics:

  • healix_heals_total (counter) — labels: test, retry_passed
  • healix_heal_duration_seconds (histogram) — time to heal per locator; labels: test
  • healix_heal_cache_total (counter) — labels: test, cache (hit / miss)
  • Job name: healix (grouped by run timestamp)

Example Prometheus queries (for Grafana):

  • Total heals: sum(healix_heals_total)
  • Heals by test: sum by (test) (healix_heals_total)
  • Retry pass rate: sum(healix_heals_total{retry_passed="true"}) / sum(healix_heals_total)
  • p95 heal duration (s): histogram_quantile(0.95, rate(healix_heal_duration_seconds_bucket[5m]))
  • Cache hit rate: sum(rate(healix_heal_cache_total{cache="hit"}[5m])) / sum(rate(healix_heal_cache_total[5m]))

Ensure Prometheus scrapes your Pushgateway (same one you use for other test metrics); then add a panel with the query above. Use instant or range query with sum(healix_heals_total) — do not use increase(...) for the total count (push-based metrics are current values).

If Grafana shows 0 but the HTML report shows heals: After the run, check the terminal: you should see either Healix: Pushed N metric(s) to Pushgateway. or Healix: Metrics not pushed - ... / Healix: Push failed - .... If metrics were not pushed, install prometheus-client in the same environment that runs pytest and set HEALIX_PUSHGATEWAY_URL (or PUSHGATEWAY_URL) so the test process can reach the Pushgateway. Also confirm Prometheus is scraping that Pushgateway (job="pushgateway" or your scrape config).

Testing

# Run unit tests only (no browser/Ollama required; high coverage)
PYTHONPATH=src pytest tests/unit/ -v

# Run with coverage report (unit tests; requires pytest-cov)
PYTHONPATH=src pytest tests/unit/ --cov=healix --cov-report=term-missing

# Run all tests (integration tests require Playwright browsers)
pytest tests/

# Run integration tests (requires browser + Ollama for healing)
pytest tests/integration/

Unit tests cover the engine (DOM cleaning, caching, get_fix_sync, install_browsers, main), the pytest plugin (selector extraction, report generation), Prometheus metrics, and the public API. Integration tests require playwright install and optionally Ollama for self-healing behavior.

Contributing

Pull requests are welcome. If you’d like to contribute:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

This project is licensed under the MIT License.

Performance & efficiency

I document here how Healix behaves today (before) and what I’ve already improved (after), with evidence so you can see how efficiency evolves across versions.

Before (current behaviour — evidence)

Area Evidence Source
Heal latency (uncached) ~2–5 seconds per selector when AI is used README (typical range with local Ollama)
Heal latency (cached) Instant (no Ollama call) Cache hit path in engine.py: get_fix_sync returns early when cache_key in self.cache (line 112–113)
Ollama calls per failure Up to two calls per broken selector when the first suggestion matches multiple elements pytest_plugin.py lines 219–220: second get_fix_sync when count > 1 to request a scoped selector
Observability Only count of heals (success/fail); no duration or cache hit rate prometheus_metrics.py: single metric healix_heals_total with labels test, retry_passed — no Histogram/Summary for latency or cache labels
DOM processing Full parse + trim to 15k chars per heal engine.py get_clean_dom() (lines 83–106) and get_fix_sync (line 115)

After (implemented — evidence)

Area Before After Evidence
Ollama calls when count > 1 Two full get_fix_sync calls (full DOM each) One full get_fix_sync + one lightweight get_fix_scoped_sync (short prompt, optional 2k-char DOM snippet, 30s timeout) engine.py: new get_fix_scoped_sync(); pytest_plugin.py calls it instead of second get_fix_sync when count > 1
Heal duration Not measured Measured per heal and pushed to Prometheus pytest_plugin.py: time.perf_counter() around heal; report entry duration_seconds; prometheus_metrics.py: healix_heal_duration_seconds (Histogram, buckets 0.05–60s)
Cache hit rate Not exposed Exposed per heal and aggregated in Prometheus Report entry cache_hit (true when fix.explanation == "Cache hit"); prometheus_metrics.py: healix_heal_cache_total{test="...", cache="hit"|"miss"}

New Prometheus metrics (Grafana):

  • Heal duration: histogram_quantile(0.95, rate(healix_heal_duration_seconds_bucket[5m])) — p95 heal time (seconds).
  • Cache hit rate: sum(rate(healix_heal_cache_total{cache="hit"}[5m])) / sum(rate(healix_heal_cache_total[5m])) — proportion of heals served from cache.

Upcoming improvements (my backlog)

I keep this list so I can come back and tackle items one by one. When I finish one, I’ll move it to the Roadmap or Changelog and note it here.

  1. Smaller DOM sent to the model — Trim or sample the DOM more aggressively before sending to Ollama (e.g. 10k chars, or only elements near the broken selector) to cut latency and tokens per heal.
  2. Async cache writes — Write cache.json in a background thread or after the run so the test thread doesn’t block on disk I/O; reduces perceived heal latency.
  3. Single Ollama call when count > 1 — Try to get a “match exactly one” selector in the first prompt (e.g. stronger RULES in the prompt) so the scoped follow-up is needed less often; measure with the new metrics.
  4. DOM clean duration metric — Time get_clean_dom() and expose it (e.g. in report or Prometheus) so I can see if DOM processing is a bottleneck.
  5. Optional: faster parser — Try lxml as the BeautifulSoup parser for get_clean_dom() and benchmark; fall back to html.parser if not installed.
  6. Retry budget / backoff — If a heal fails or confidence is low, consider a single retry with a shorter prompt or different temperature before giving up; document behaviour in README.
  7. Heal summary in terminal — At session end, print a one-line summary (e.g. “N heals, X from cache, avg Ys”) so I can see efficiency without opening Grafana.

Support

Roadmap

  • Support for more AI models (OpenAI, Anthropic)
  • Visual regression testing
  • Multi-browser support expansion
  • Performance optimization — lightweight scoped Ollama call when count > 1; heal duration & cache hit metrics (see Performance & efficiency)
  • Plugin system for custom healing strategies

For more granular next steps I’m working through, see Upcoming improvements (my backlog) above.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

healix_ai-0.1.29.tar.gz (31.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

healix_ai-0.1.29-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file healix_ai-0.1.29.tar.gz.

File metadata

  • Download URL: healix_ai-0.1.29.tar.gz
  • Upload date:
  • Size: 31.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.9

File hashes

Hashes for healix_ai-0.1.29.tar.gz
Algorithm Hash digest
SHA256 08dcb26b422d37da30e78263d42a2fc1e38721b57464c53188295075466e4775
MD5 b24339a2a10c3eb336bb0e5e97421c2e
BLAKE2b-256 feecca8d3c637d8fe0487e684e13a6e9b5a6e27ea69121b5b7af7b3581d72bff

See more details on using hashes here.

File details

Details for the file healix_ai-0.1.29-py3-none-any.whl.

File metadata

  • Download URL: healix_ai-0.1.29-py3-none-any.whl
  • Upload date:
  • Size: 25.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.9

File hashes

Hashes for healix_ai-0.1.29-py3-none-any.whl
Algorithm Hash digest
SHA256 ec1b50781180089235eb9e1d247a60dd8add2b721eebb49addeffb8ea6ca1559
MD5 d03c50c5b75367f3d4aef78c1619ec2d
BLAKE2b-256 24db0d7122e090ced3fd54d877618047e8be8b6ea3f82d25ef95a7b6f1de3211

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page