Aliax SDK — real-time interceptor middleware for visual web agents (Set-of-Mark + deterministic execution).
Project description
Aliax SDK
Aliax is the real-time interceptor middleware for visual web agents. Translate the live page into a numbered Set-of-Mark image, hand it to your VLM, and let Aliax execute the action it picks — clicks, types, scrolls, the works — with deterministic accuracy.
When the agent still gets stuck, ship the failure to the offline
annotation queue with capture_failure().
Current version: 1.0.0 · Python: 3.8 – 3.12 · Changelog · Contributing
Installation
pip install aliax
playwright install chromium
The 3-line agent loop
from aliax import Aliax
from playwright.async_api import async_playwright
aliax = Aliax(api_key="sk_live_...") # mint one at /api-keys
async with async_playwright() as p:
page = await (await p.chromium.launch()).new_page()
await page.goto("https://shop.example.com/cart")
# 1. Translate the live DOM into a VLM-friendly Set-of-Mark.
ctx = await aliax.parse_ui(page)
# 2. Ask your VLM. It sees a numbered screenshot + a tiny JSON map.
decision = await ask_llm(image=ctx.image_bytes, map=ctx.elements)
# e.g. {"action": "CLICK", "element_id": "el_41"}
# 3. Aliax executes it natively — scroll-into-view, React onChange,
# iframe coords, retina DPR, all handled.
await aliax.execute(page, decision)
That's the entire happy path. No XPath wrangling, no Playwright boilerplate, no pixel guessing.
The safety net — capture_failure()
When the agent loops on a popup or hallucinates past recovery, snapshot the full state and ship it to the Aliax annotation queue for human correction:
await aliax.capture_failure(
page,
goal="Close newsletter popup",
thoughts=agent.current_reasoning,
last_attempted_action={"action": "CLICK", "element_id": "el_41"},
failure_reason="modal_blocked",
step=agent.loop_step,
)
Each failure becomes a (negative, positive) DPO pair — the agent's "stupid move" + the annotator's corrected tap — ready for fine-tuning.
parse_ui() — what you get back
ctx = await aliax.parse_ui(page)
| Attribute | Type | Description |
|---|---|---|
image_bytes |
bytes |
Native browser screenshot (JPEG by default — see render_config) with numbered Set-of-Mark boxes painted by the bundled JS overlay. Zero Python image processing. |
image_mime |
str |
"image/jpeg" by default, "image/png" if you opt in. |
image_size |
(int, int) |
(width, height) in physical pixels, parsed from the image header. |
elements |
list[dict] |
Each element: element_id, tag, role, text, bounds, editable, is_canvas, attrs. |
viewport |
dict |
{width, height, dpr, scroll_x, scroll_y} in CSS px. |
url |
str |
Page URL at capture time. |
Power-user: shrink your VLM bill with render_config
VLM providers bill image tokens by file weight + dimensions. JPEG at
quality=40 costs roughly 10× fewer tokens than the lossless
default. The Set-of-Mark IDs stay readable because they're rendered as
crisp DOM text by Chromium before the JPEG encoder runs.
# Default — pristine JPEG at q=80.
ctx = await aliax.parse_ui(page)
# Token-optimised — same readable IDs, ~10× cheaper per VLM call.
ctx = await aliax.parse_ui(
page,
render_config={"format": "jpeg", "quality": 40},
)
# Lossless when you need it (research / fine-tuning datasets).
ctx = await aliax.parse_ui(
page,
render_config={"format": "png"},
)
Quality is clamped to [30, 100] so a typo like quality=5 can't turn
the numbered boxes into illegible mush and crash your agent loop.
Convenience helper:
prompt = f"""
{ctx.llm_text_block()}
Pick the element_id to click. Reply JSON {{action, element_id}}.
"""
execute() — the action verbs
await aliax.execute(page, {"action": "CLICK", "element_id": "el_41"})
await aliax.execute(page, {"action": "TYPE", "element_id": "el_7", "value": "Nike Shoes"})
await aliax.execute(page, {"action": "HOVER", "element_id": "el_14"})
await aliax.execute(page, {"action": "PRESS", "element_id": "el_7", "key": "Enter"})
await aliax.execute(page, {"action": "SCROLL", "dy": 600})
await aliax.execute(page, {"action": "NAVIGATE", "url": "https://..."})
await aliax.execute(page, {"action": "WAIT", "ms": 1500})
await aliax.execute(page, {"action": "DONE"})
Raw coordinates are also accepted as an escape hatch:
await aliax.execute(page, {"action": "CLICK", "x": 905, "y": 150})
Under the hood, execute() scrolls the target into view, fires
React-friendly events (focus → keystrokes with a 50ms cadence), and
returns a small dict like {"ok": True, "coords": [905, 150]}. It
never raises — your agent loop stays alive.
How it works under the hood
- The bundled DOM mapper (
dom-mapper.min.js, shipped inside the wheel) traverses the live page including shadow DOMs and same-origin iframes. It returns only truly interactable elements with a minimum size of 12 CSS pixels — no clutter, no hallucinated boxes. - The same JS module paints a
position:fixed; pointer-events:none; contain:strictoverlay of numbered colored boxes at the top of the z-stack. Chromium renders the boxes in microseconds and the host page's layout / hover / IntersectionObserver state is untouched. - Playwright snaps one native screenshot via its C++ CDP path at
the format / quality you asked for in
render_config. No Python image processing, no Pillow dependency. - The overlay layer is torn down in a
try/finallyso a crash mid- capture still leaves the live page exactly as we found it. - Coordinates are cached so
execute(page, {"element_id": "el_41"})resolves instantly against the most recentparse_ui(). - Every call fires a non-blocking telemetry ping for usage tracking.
What's new in v1.0.0 — first PyPI release
The 1.0.0 cut is the first official PyPI release. The SDK has been
hardened for enterprise deployment — zipped wheels, serverless layers,
long-running agents — and the install name now matches the import
(pip install aliax → import aliax).
async with Aliax() as aliax:— full async context manager support so thehttpxconnection pool is torn down deterministically even if the agent loop crashes. No more socket leaks in 24/7 bots.ALIAX_API_KEYenv-var fallback —Aliax()with no args reads the key from the environment; 12-factor Docker / Lambda / GitHub Actions deployments don't have to thread it through code.- Zip-safe asset loading —
dom-mapper.min.jsis read viaimportlib.resources, so the SDK runs inside AWS Lambda layers and Google Cloud Run container images where__file__is a virtual path. - PEP 561 typed — ships
py.typedsomypy, Pyright, VS Code, and PyCharm consume the inline type hints out of the box. - PEP 621 packaging — pure
pyproject.toml, dynamic version pulled fromaliax/_version.py, MIT-licensed, classified asDevelopment Status :: 5 - Production/Stable. render_config={"format", "quality"}— the only knob AI engineers get for VLM token economics. Defaults are pristine JPEG atquality=80; drop toquality=40for roughly 10× cheaper VLM calls without sacrificing Set-of-Mark ID legibility. Quality is clamped to[30, 100]so a typo can't crash the agent.- Browser-native overlay — Set-of-Mark boxes are painted by the
bundled
dom-mapper.min.jsas aposition:fixed; pointer-events:none; contain:strictDOM layer. Chromium renders them in microseconds; Playwright snaps the screenshot via its native C++ CDP path. The SDK never touches the pixel buffer — no Pillow, no OpenCV, no Cairo. parse_ui(page)/execute(page, decision)/capture_failure(...)— the canonical 3-call agent loop, with a typedParseContext,AttemptedActiondataclass, and the full action catalog (CLICK,TYPE,TYPE_AND_ENTER,HOVER,SCROLL_{DOWN,UP,LEFT,RIGHT}).- Hardened transport — bearer-only auth, idempotency keys on
capture_failure, race-safe HTTP client init, non-blocking usage telemetry, automatic*.workers.devfallback if the apex DNS / TLS flakes, opt-out viafallback_endpoint="".
See CHANGELOG.md for the full changelog.
Project details
Release history Release notifications | RSS feed
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 aliax-1.0.0.tar.gz.
File metadata
- Download URL: aliax-1.0.0.tar.gz
- Upload date:
- Size: 81.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7cb10edc7c4cfc2b5362a5bf8797683e77fad2b68fed236bfb0c895cbfe1570d
|
|
| MD5 |
e93d309c42a948073768b3d1c9f8e45b
|
|
| BLAKE2b-256 |
c566c4d2d478dd873c72e7e2d0280cc9529e46b1170147a7f0a955c8edd73306
|
File details
Details for the file aliax-1.0.0-py3-none-any.whl.
File metadata
- Download URL: aliax-1.0.0-py3-none-any.whl
- Upload date:
- Size: 76.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0403be358a1f2f85470b6e28d33f0aa7cb9f45dce313986e9cb3b51bd0da6c49
|
|
| MD5 |
60d2c5dd7df9f6df45d83b0329c720e6
|
|
| BLAKE2b-256 |
740443317fb01b10861239168a7fbe0d9fa6abc93048106b9ee4dd52533e2435
|