Core API

The core module provides the main user-facing API for LayoutLens.

LayoutLens Class

class layoutlens.LayoutLens(api_key=None, model='gpt-4o-mini', provider='openai', output_dir='layoutlens_output', cache_enabled=True, cache_type='memory', cache_ttl=3600, api_base=None, temperature=None)[source]

Bases: object

Simple API for AI-powered UI testing with natural language.

This class provides an intuitive interface for analyzing websites and screenshots using natural language queries, designed for developer workflows and CI/CD integration.

Examples: >>> lens = LayoutLens(api_key=”sk-…”) >>> result = lens.analyze(”https://example.com”, “Is the navigation clearly visible?”) >>> print(result.answer)

>>> # Compare two designs
>>> result = lens.compare(
...     ["before.png", "after.png"],
...     "Are these layouts consistent?"
... )
Parameters:
  • api_key (str | None)

  • model (str)

  • provider (str)

  • output_dir (str)

  • cache_enabled (bool)

  • cache_type (str)

  • cache_ttl (int)

  • api_base (str | None)

  • temperature (float | None)

async analyze(source: str | Path, query: str, viewport: Viewport | str = 'desktop', context: dict[str, Any] | None = None, instructions: Instructions | None = None, max_concurrent: int = 5) AnalysisResult[source]
async analyze(source: list[str | Path], query: str | list[str], viewport: Viewport | str = 'desktop', context: dict[str, Any] | None = None, instructions: Instructions | None = None, max_concurrent: int = 5) BatchResult
async analyze(source: str | Path, query: list[str], viewport: Viewport | str = 'desktop', context: dict[str, Any] | None = None, instructions: Instructions | None = None, max_concurrent: int = 5) BatchResult

Smart analyze method that handles single or multiple sources and queries.

Parameters:
  • source (str | Path | list[str | Path]) – Single URL/path or list of URLs/paths to analyze.

  • query (str | list[str]) – Single question or list of questions about the UI.

  • viewport (Viewport | str) – Viewport for capture (Viewport.DESKTOP, “desktop”, etc.).

  • context (dict[str, Any] | None) – Additional context for analysis (user_type, browser, etc.). Legacy format.

  • instructions (Instructions | None) – Rich instruction set with expert personas and structured context. Takes precedence over context if both provided.

  • max_concurrent (int) – Maximum concurrent operations for batch analysis.

Returns:

AnalysisResult for single source+query, BatchResult for multiple.

Return type:

AnalysisResult | BatchResult

Examples

# Single analysis >>> result = await lens.analyze(”https://github.com”, “Is it accessible?”)

# Multiple queries on one source >>> result = await lens.analyze(”https://github.com”, [“Is it accessible?”, “Mobile-friendly?”])

# Multiple sources, one query >>> result = await lens.analyze([“page1.html”, “page2.html”], “Is it good?”)

# Multiple sources and queries >>> result = await lens.analyze([“page1.html”, “page2.html”], [“Accessible?”, “Mobile?”])

async compare(sources, query='Are these layouts consistent?', viewport='desktop', context=None, instructions=None)[source]

Compare multiple URLs or screenshots.

Parameters:
  • sources (list[str | Path]) – List of URLs or screenshot paths to compare.

  • query (str) – Natural language question for comparison.

  • viewport (Viewport | str) – Viewport for captures (Viewport.DESKTOP or string).

  • context (dict[str, Any] | None) – Additional context for analysis.

  • instructions (Instructions | None) – Rich instructions for expert analysis.

Returns:

Comparison analysis with overall assessment.

Return type:

ComparisonResult

Example

>>> result = await lens.compare([
...     "https://example.com/before",
...     "https://example.com/after"
... ], "Did the redesign improve the user experience?")
async capture(source: str | Path, viewport: Viewport | str = 'desktop', wait_for_selector: str | None = None, wait_time: int | None = None, max_concurrent: int = 3) str[source]
async capture(source: list[str | Path], viewport: Viewport | str = 'desktop', wait_for_selector: str | None = None, wait_time: int | None = None, max_concurrent: int = 3) dict[str, str]

Smart capture method that handles single or multiple sources uniformly.

Parameters:
  • source (str | Path | list[str | Path]) – Single URL/path or list of URLs/paths to capture.

  • viewport (Viewport | str) – Viewport for capture (Viewport.DESKTOP, “desktop”, etc.).

  • wait_for_selector (str | None) – CSS selector to wait for before capturing.

  • wait_time (int | None) – Additional wait time in milliseconds.

  • max_concurrent (int) – Maximum concurrent captures for multiple sources.

Returns:

Returns screenshot path as string. Multiple sources: Returns dict mapping source to screenshot path.

Return type:

Single source

Examples

# Single URL >>> path = await lens.capture(”https://example.com”) # Returns: “/path/to/screenshot.png”

# Multiple URLs >>> paths = await lens.capture([”https://example.com/page1”, “https://site2.com”]) # Returns: {”https://example.com/page1”: “/path1.png”, “https://site2.com”: “/path2.png”}

# HTML files >>> path = await lens.capture(“page.html”) >>> paths = await lens.capture([“page1.html”, “page2.html”])

# Existing images (validation) >>> path = await lens.capture(“screenshot.png”)

async judge(image_path, prompt, *, max_tokens=AUTO, timeout=120.0)[source]

Send prompt VERBATIM with an image and return a parsed verdict.

This is the faithful judge interface for external evaluation harnesses (e.g. UIJudgeBench). Unlike analyze(), LayoutLens adds NOTHING to the prompt: no system persona, no scaffolding, no appended JSON-format instruction. The caller owns the entire prompt, including any response contract. The call always hits the model (no caching) and honors the per-model parameter policy (Claude 4.6+/5 omit temperature).

Parameters:
  • image_path (str | Path) – Path to an existing image file (mime inferred from the extension: .jpg/.jpeg -> JPEG, otherwise PNG).

  • prompt (str) – The exact text to send as the sole text block.

  • max_tokens (int | _Auto) – Maximum tokens to generate. Defaults to AUTO, which resolves to 8000 for reasoning/thinking models (they spend thinking tokens inside this budget) and 300 otherwise. Pass an explicit integer to override.

  • timeout (float) – Per-call timeout in seconds (default 120 — reasoning models can take well over 30s on a single judgment).

Returns:

JudgeResult with the parsed answer/confidence/rationale, the raw text, a refusal flag, per-model usage split, and the parse mode.

Raises:
Return type:

JudgeResult

async judge_batch(requests, *, max_tokens=AUTO, resume=True, manifest_path=None, poll_interval=10.0, poll_timeout=86400.0, reasoning_effort=None, image_detail='auto')[source]

Judge many image+prompt requests over a provider batch transport.

The batched counterpart to judge(): each request sends its prompt VERBATIM with the same image bytes as judge(), honors the reasoning-aware max_tokens default and the per-model parameter policy, and is parsed into a JudgeResult. Batch APIs are ~50% cheaper and the right transport for bulk offline evaluation (e.g. UIJudgeBench). LayoutLens is thus the reference batched judge.

The backend is chosen from an explicit provider/model combination: provider="gemini" with gemini/* uses the google-genai inline batch (optional extra layoutlens[gemini]); native OpenAI uses the official Responses Batch API, and supported non-Gemini/non-OpenAI providers use the litellm file-based batch. All backends are resumable via a manifest.

Parameters:
  • requests (list[BatchRequest]) – The batch items. Each id must be unique and keys its result. A request whose image is missing yields an "unknown" result rather than aborting the batch.

  • max_tokens (int | _Auto) – Per-request token budget. Defaults to AUTO (8000 for reasoning models, else 300); an explicit integer overrides.

  • resume (bool) – When True (default), collect any prior jobs from the manifest first and submit only uncovered ids. When False, the selected manifest path must not already exist.

  • manifest_path (str | Path | None) – Where submitted job ids persist for resume. Defaults to a content-addressed path under output_dir/batch keyed by the backend, endpoint, model, token budget, reasoning effort, image detail, exact prompts, image MIME types, and image bytes.

  • poll_interval (float) – Seconds between batch-status polls.

  • poll_timeout (float) – Max seconds to wait for a single batch job.

  • reasoning_effort (str | None) – Native OpenAI reasoning effort. None retains the model default; otherwise one of none, low, medium, high, xhigh, or max. Rejected for other providers so a requested setting is never silently ignored.

  • image_detail (str) – Native OpenAI image-detail setting: auto, low, high, or original. Non-default values are rejected for other providers.

Returns:

{request_id: JudgeResult} for every request.

Raises:
  • AuthenticationError – If no API key is configured for a mapped provider.

  • ImportError – If a gemini/* model is used without google-genai.

  • ValidationError – If request ids repeat or an existing resume manifest does not match the exact request.

Return type:

dict[str, JudgeResult]

async check_accessibility(source, standards=None, compliance_level='AA', viewport='desktop', mode='hybrid')[source]

Accessibility audit: deterministic axe-core, LLM vision, or both.

Parameters:
  • source (str | Path) – URL or file path to analyze

  • standards (list[str] | None) – Accessibility standards to apply (default: WCAG 2.1, Section 508)

  • compliance_level (ComplianceLevel | str) – WCAG compliance level (ComplianceLevel.AA or string)

  • viewport (Viewport | str) – Viewport for analysis (Viewport.DESKTOP or string)

  • mode (Literal['hybrid', 'axe', 'llm']) – "hybrid" (default) combines deterministic axe-core checks with LLM analysis (axe violations force a “no” verdict). "axe" runs axe-core only (no API key required). "llm" runs the legacy vision-only audit. The axe run honors compliance_level: A -> wcag2a, AA -> wcag2a``+``wcag2aa, AAA additionally includes wcag2aaa.

Returns:

Detailed accessibility assessment with specific WCAG guidance

Raises:

ValueError – If compliance_level is not a valid WCAG level

Return type:

AnalysisResult

async check_layout(source, viewport='desktop', mode='hybrid', scorer=None)[source]

Layout check: deterministic geometry/contrast scan, LLM vision, or both.

The deterministic scan measures contrast, sibling overlap, clipped content, viewport protrusion, page-level horizontal overflow, truncated text, WCAG-aware target spacing, complete focus obscuration, and text occlusion — directly off the rendered page, with no LLM and no API key.

Parameters:
  • source (str | Path) – URL or HTML file to check (images have no DOM to measure — use mode="llm" for pre-rendered screenshots).

  • viewport (Viewport | str) – Viewport for the scan/capture.

  • mode (Literal['hybrid', 'deterministic', 'llm']) – "hybrid" (default) runs the deterministic scan AND LLM vision, forcing a “no” verdict when the scan measures any defect. "deterministic" runs the scan only — keyless. "llm" is vision-only.

  • scorer (LayoutScorer | None) – Optional pre-configured LayoutScorer (custom thresholds).

Returns:

AnalysisResult. In deterministic/hybrid modes metadata["layout"] holds the full layout report with per-finding measurements.

Raises:

ValidationError – In deterministic mode when source is an image, which has no DOM to measure.

Return type:

AnalysisResult

async optimize_conversions(source, business_goals=None, industry=None, target_audience=None, viewport='desktop')[source]

Conversion rate optimization analysis using CRO expert knowledge.

Parameters:
  • source (str | Path) – URL or file path to analyze

  • business_goals (list[str] | None) – Business objectives (e.g., reduce_cart_abandonment)

  • industry (str | None) – Industry context for specialized recommendations

  • target_audience (str | None) – Target audience for optimization focus

  • viewport (Viewport | str) – Viewport for analysis (Viewport.DESKTOP or string)

Returns:

Detailed CRO recommendations with A/B testing suggestions

Return type:

AnalysisResult

async analyze_mobile_ux(source, device_types=None, performance_focus=True)[source]

Mobile UX analysis using mobile expert knowledge.

Parameters:
  • source (str | Path) – URL or file path to analyze

  • device_types (list[str] | None) – Target devices (smartphone, tablet)

  • performance_focus (bool) – Include performance optimization analysis

Returns:

Mobile-specific UX recommendations and optimizations

Return type:

AnalysisResult

async audit_ecommerce(source, page_type='product_page', business_model='b2c', viewport='desktop')[source]

E-commerce UX audit using retail expert knowledge.

Parameters:
  • source (str | Path) – URL or file path to analyze

  • page_type (str) – Type of e-commerce page (product_page, checkout, homepage)

  • business_model (str) – Business model (b2c, b2b)

  • viewport (Viewport | str) – Viewport for analysis (Viewport.DESKTOP or string)

Returns:

E-commerce specific recommendations for conversion improvement

Return type:

AnalysisResult

async analyze_with_expert(source, query, expert_persona, focus_areas=None, user_context=None, viewport='desktop')[source]

Analyze using a specific domain expert persona.

Parameters:
  • source (str | Path) – URL or file path to analyze

  • query (str) – Question to analyze

  • expert_persona (Expert | str) – Expert to use (Expert.ACCESSIBILITY or string)

  • focus_areas (list[str] | None) – Specific areas to focus analysis on

  • user_context (dict[str, Any] | None) – Rich context about users and requirements

  • viewport (Viewport | str) – Viewport for analysis (Viewport.DESKTOP or string)

Returns:

Expert-level analysis with domain-specific recommendations

Return type:

AnalysisResult

async compare_with_expert(sources, query, expert_persona, focus_areas=None, viewport='desktop')[source]

Compare multiple sources using domain expert knowledge.

Parameters:
  • sources (list[str | Path]) – List of URLs or file paths to compare

  • query (str) – Comparison question

  • expert_persona (Expert | str) – Expert to use for comparison (Expert.ACCESSIBILITY or string)

  • focus_areas (list[str] | None) – Specific areas to focus comparison on

  • viewport (Viewport | str) – Viewport for analysis (Viewport.DESKTOP or string)

Returns:

Expert comparison with domain-specific insights

Return type:

ComparisonResult

async run_test_suite(suite, parallel=False, max_workers=4)[source]

Run a test suite and return one result per test case.

Parameters:
  • suite (UITestSuite) – The test suite to run.

  • parallel (bool) – Run test cases concurrently instead of serially.

  • max_workers (int) – Maximum concurrent test cases when parallel.

Returns:

List of UITestResult objects, in suite order.

Return type:

list[UITestResult]

create_test_suite(name, description, test_cases)[source]

Create a test suite from spec dicts (see UITestSuite.from_specs).

Parameters:
Return type:

UITestSuite

get_cache_stats()[source]

Get cache performance statistics.

Return type:

dict[str, Any]

clear_cache()[source]

Clear all cached analysis results.

Return type:

None

enable_cache()[source]

Enable caching.

Return type:

None

disable_cache()[source]

Disable caching.

Return type:

None

Result Classes

class layoutlens.AnalysisResult(source, query, answer, confidence, reasoning, screenshot_path=None, viewport='desktop', timestamp=<factory>, execution_time=0.0, metadata=<factory>)[source]

Bases: object

Result from analyzing a single URL or screenshot.

Parameters:
source: str
query: str
answer: str
confidence: float
reasoning: str
screenshot_path: str | None
viewport: str
timestamp: str
execution_time: float
metadata: dict[str, Any]
to_json()[source]

Export result to JSON string.

Return type:

str

class layoutlens.ComparisonResult(sources, query, answer, confidence, reasoning, individual_analyses=<factory>, screenshot_paths=<factory>, timestamp=<factory>, execution_time=0.0, metadata=<factory>)[source]

Bases: object

Result from comparing multiple sources.

Parameters:
sources: list[str]
query: str
answer: str
confidence: float
reasoning: str
individual_analyses: list[AnalysisResult]
screenshot_paths: list[str]
timestamp: str
execution_time: float
metadata: dict[str, Any]
to_json()[source]

Export result to JSON string.

Return type:

str

class layoutlens.BatchResult(results, total_queries, successful_queries, average_confidence, total_execution_time, total_prompt_tokens=0, total_completion_tokens=0, total_tokens=0, estimated_cost_usd=None, timestamp=<factory>)[source]

Bases: object

Result from batch analysis.

Parameters:
  • results (list[AnalysisResult])

  • total_queries (int)

  • successful_queries (int)

  • average_confidence (float)

  • total_execution_time (float)

  • total_prompt_tokens (int)

  • total_completion_tokens (int)

  • total_tokens (int)

  • estimated_cost_usd (float | None)

  • timestamp (str)

results: list[AnalysisResult]
total_queries: int
successful_queries: int
average_confidence: float
total_execution_time: float
total_prompt_tokens: int
total_completion_tokens: int
total_tokens: int
estimated_cost_usd: float | None
timestamp: str
to_json()[source]

Export result to JSON string.

Return type:

str

Test Suite Classes

class layoutlens.UITestCase(name, html_path, queries, viewports=<factory>, metadata=<factory>, expected_results=None, expected_confidence=0.7)[source]

Bases: object

Represents a single test case for UI testing.

expected_results declares what the analysis must assert against and is required (see UITestSuite.from_dict). Schema:

expected_results:
  answer: "yes"                    # or "no" — compared against the
                                    # parsed yes/no of the analysis answer
  contains: ["nav", "contrast"]    # optional; each term must appear
                                    # (case-insensitively) in answer + reasoning

Both keys are individually optional, but at least one of them must be present — an empty or missing expected_results is a load-time error. expected_confidence sets the minimum result.confidence required in addition to any content assertions.

Parameters:
name: str
html_path: str
queries: list[str]
viewports: list[str]
metadata: dict[str, Any]
expected_results: dict[str, Any] | None = None
expected_confidence: float = 0.7
to_dict()[source]

Convert test case to dictionary.

Return type:

dict[str, Any]

class layoutlens.UITestSuite(name, description, test_cases, metadata=<factory>)[source]

Bases: object

Represents a collection of test cases.

Parameters:
name: str
description: str
test_cases: list[UITestCase]
metadata: dict[str, Any]
to_dict()[source]

Convert test suite to dictionary.

Return type:

dict[str, Any]

classmethod from_dict(data)[source]

Create test suite from dictionary.

Parameters:

data (dict[str, Any]) – Serialized suite definition.

Returns:

Validated test suite.

Raises:

ValidationError – If any test case is missing expected_results (or declares neither “answer” nor “contains”). Assertions are required per case — there is no confidence-only fallback.

Return type:

UITestSuite

save(filepath)[source]

Save test suite to JSON file.

Parameters:

filepath (Path)

Return type:

None

classmethod load(filepath)[source]

Load a test suite from a JSON or YAML file (by extension).

Parameters:

filepath (Path)

Return type:

UITestSuite

classmethod from_yaml(filepath)[source]

Load a test suite from a YAML file.

Parameters:

filepath (str | Path)

Return type:

UITestSuite

classmethod from_specs(name, description, test_cases)[source]

Create a test suite from test-case spec dicts.

Each spec follows the same shape as a YAML test case, including a required expected_results (see the UITestCase docstring for schema) — validated identically to, and via the same helper as, from_dict().

Parameters:
  • name (str) – Suite name.

  • description (str) – Suite description.

  • test_cases (list[dict[str, Any]]) – Serialized test-case definitions.

Returns:

Validated test suite.

Raises:

ValidationError – If any spec is missing expected_results.

Return type:

UITestSuite

class layoutlens.UITestResult(suite_name, test_case_name, total_tests, passed_tests, failed_tests, results, duration_seconds, metadata=<factory>)[source]

Bases: object

Results from running a test suite.

Parameters:
suite_name: str
test_case_name: str
total_tests: int
passed_tests: int
failed_tests: int
results: list[AnalysisResult]
duration_seconds: float
metadata: dict[str, Any]
property success_rate: float

Calculate success rate.

to_dict()[source]

Convert to dictionary for serialization.

Return type:

dict[str, Any]

to_json()[source]

Export result to JSON string.

Return type:

str