Skip to main content

guml

Compile GUML — an intermediate representation for LLM-generated user interfaces — to HTML, React, Svelte and more. The real Rust compiler, built as a native extension. No Node, no Rust toolchain, no build step.

pip install guml
import guml

src = '''page "Dashboard"
card
  h Revenue
  metric $48,120
  p Up 12% on last month
'''

html = guml.render(src)      # a complete page: no JavaScript, nothing to serve alongside it
guml.check(src)              # []

pip install guml also puts a guml command on your PATH.


Two things this is for

1. Getting a model to build UI

This is what GUML exists for. A model writing React emits JSX, hooks, effect dependencies, Tailwind class strings and ARIA attributes — most of it mechanically derivable. GUML moves that to the compiler, so the model writes what it actually decided and everything conventional is generated.

import guml, anthropic

client = anthropic.Anthropic()

prompt = guml.SPEC + "\n\n" + guml.registry(["card", "btn", "list", "metric"])

reply = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=2000,
    system=prompt,
    messages=[{"role": "user", "content": "A dashboard showing revenue and recent orders"}],
)

source = reply.content[0].text

# Free round. Strips a markdown fence, formats, applies every unambiguous fix.
# Costs nothing and resolves a surprising share of what a first generation gets wrong.
source = guml.repair(source).text

problems = guml.check(source)
if problems:
    # Every problem in one pass, never just the first — each repair round is a full generation.
    feedback = "\n".join(f"{d.code} line {d.line}: {d.message}" for d in problems)
    ...

html = guml.render(source)
  • guml.SPEC — the language spec, sized to sit in a system prompt. Rules, not vocabulary.
  • guml.registry([...]) — the vocabulary, as a prompt-sized slice. Ask for the dozen tags a task needs rather than all 49.
  • guml.repair() — everything a repair loop can do without another model call.
  • guml.check() — every diagnostic in one pass. Codes are append-only and never renumbered, so a loop can key on them.

2. Serving pages from Python

render() produces HTML with no JavaScript and no build step, so it drops into any framework.

Flask

@app.get("/dashboard")
def dashboard():
    return guml.render(SOURCE)

FastAPI

from fastapi.responses import HTMLResponse

@app.get("/dashboard", response_class=HTMLResponse)
def dashboard():
    return guml.render(SOURCE)

Django

from django.http import HttpResponse

def dashboard(request):
    return HttpResponse(guml.render(SOURCE))

Jinja — the one integration shipped rather than documented, because it is the one that is annoying to write correctly:

pip install 'guml[jinja]'
from guml.jinja import GumlExtension
app.jinja_env.add_extension(GumlExtension)
<div class="panel">{{ source | guml }}</div>

It defaults to fragment=True — no doctype, no <head>, no <main> — and marks the result safe so Jinja does not escape it into visible angle brackets.


Security

js and raw blocks compile through unchanged. That is GUML's documented escape hatch and its security boundary at the same time: everything outside them is constrained, everything inside them is the author's own code.

So render() defaults to level="core" — markup only, no state, no data, no actions, no js:

guml.render(source)                 # core: safe for a document you did not write
guml.render(source, level="app")    # full: executes any `js` the author included

This deliberately differs from the guml build CLI, which defaults to app. Different threat model: the CLI compiles a file you wrote, and render() very often does not.

Before rendering something at app level, capabilities() tells you what it will actually do:

caps = guml.capabilities(source)
if caps.uses_escape_hatch:
    raise ValueError("this document contains js")

response.headers["Content-Security-Policy"] = caps.csp

API

render(src, *, level, style, fragment) GUML → HTML
compile(src, backend, *, level) any of react, svelte, html, html-bare, html-fragment, html-cdn, wc, json, a2ui, mcp-ui
check(src, *, level) list[Diagnostic], every problem in one pass
raise_for_errors(src) the raising version, for try/except
repair(src) / fix(src) mechanical repair, no model call
format(src) / canonical(src) idempotent formatting / normalisation for comparison
capabilities(src, backend) what it does, plus a matching CSP
registry(tags) prompt-sized vocabulary slice
SPEC the language spec, for a system prompt
BACKENDS every backend name, from the compiler itself

Diagnostic is a frozen dataclass — d.code, d.line, d.column, d.message, d.help, d.suggestion, d.is_error — not a dict. Fully typed, py.typed included.

Threading

The API is synchronous, deliberately: compiling is CPU-bound and takes single-digit milliseconds, and an async def wrapper would be fake async that still blocks the loop. The GIL is released for the duration of every compile, so Flask on threads and FastAPI's threadpool genuinely parallelise. For a very large document in an async handler, await asyncio.to_thread(guml.render, src).


Command line

guml build app.guml --backend html
guml check app.guml
guml fmt app.guml --write
guml capabilities app.guml

A subset — enough that compiling a .guml file does not require a Rust toolchain. The full CLI (source maps, custom themes, registry validation, token estimates) is cargo install guml-cli.


Also available


MIT.

Metadata

Release files for guml 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for guml 0.1.0
File Size Uploaded Provenance Actions
guml-0.1.0.tar.gz 402.1 kB Without provenance Details for guml-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for guml 0.1.0
File Interpreter ABI Platform Actions
guml-0.1.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details for guml-0.1.0-cp39-abi3-win_amd64.whl

Total release size: 1.1 MB

Release files / guml-0.1.0.tar.gz

Download URL guml-0.1.0.tar.gz
Size 402.1 kB
Tags Source
SHA-256 checksum
How to use checksums
024d840a9c3fb208ca6887f503bf725887a5fd7c74f657bd2fe550014fcb5551
BLAKE2b-256 checksum
How to use checksums
35c37a4b7c1a180b13e37e61e9807257abd9bf45a2b22bec473e7eea50f8487c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / guml-0.1.0-cp39-abi3-win_amd64.whl

Download URL guml-0.1.0-cp39-abi3-win_amd64.whl
Size 728.9 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
257e0b1f4ec6ec39a69921b9c4bfd890bbdbfeb3419f9b37d7575efff95565e8
BLAKE2b-256 checksum
How to use checksums
8470a1bee6c8fb026419fcce9b155e09c4a819d7454d9f1059f5e9b73f18e0dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Security for guml 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page