Privacy-first self-healing agent for Playwright tests. AI-powered, 100% local, zero data exposure.
Project description
Healix
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:
- Observe — Analyze the current page state and DOM
- Reason — Use AI to determine the correct selector/action
- Act — Execute the corrected action
- Verify — Confirm the action succeeded
- 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_passedhealix_heal_duration_seconds(histogram) — time to heal per locator; labels:testhealix_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:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- 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.
- 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.
- Async cache writes — Write
cache.jsonin a background thread or after the run so the test thread doesn’t block on disk I/O; reduces perceived heal latency. - 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.
- 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. - Optional: faster parser — Try
lxmlas the BeautifulSoup parser forget_clean_dom()and benchmark; fall back tohtml.parserif not installed. - 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.
- 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
- Issues: github.com/kunaal-ai/healix/issues
- Changelog: See Releases
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08dcb26b422d37da30e78263d42a2fc1e38721b57464c53188295075466e4775
|
|
| MD5 |
b24339a2a10c3eb336bb0e5e97421c2e
|
|
| BLAKE2b-256 |
feecca8d3c637d8fe0487e684e13a6e9b5a6e27ea69121b5b7af7b3581d72bff
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec1b50781180089235eb9e1d247a60dd8add2b721eebb49addeffb8ea6ca1559
|
|
| MD5 |
d03c50c5b75367f3d4aef78c1619ec2d
|
|
| BLAKE2b-256 |
24db0d7122e090ced3fd54d877618047e8be8b6ea3f82d25ef95a7b6f1de3211
|