Skip to main content

canonicalwebteam.store-llm

Flask extension making a Canonical store (snapcraft.io, charmhub.io, ...) readable by LLMs:

  • every public page is served as Markdown at /page.md
  • /llms.txt lists the public pages, discovered from the routing table (llmstxt.org)
  • /llms-full.txt concatenates their Markdown
  • <link rel="alternate" type="text/markdown"> can be advertised from the layout

The HTML to Markdown conversion is done by canonicalwebteam.markdown-response; this package adds the store-specific wiring around it.

Installation

pip install canonicalwebteam.store-llm

Usage

from canonicalwebteam.store_llm import StoreLLM

StoreLLM(
    app,
    base_url="https://snapcraft.io",
    site_name="Snapcraft",
    summary="Snapcraft is the home of the Snap Store, where ...",
    sections={
        "": "Main pages",
        "about": "Publishing a snap",
        "docs": "Documentation",
        "store": "Store",
    },
    section_order=["Main pages", "Publishing a snap", "Documentation", "Store"],
    extra_links=[
        {
            "section": "Documentation",
            "url": "https://snapcraft.io/docs/",
            "title": "Snap documentation",
            "description": "Reference and explanation for snaps and snapd.",
        },
        {
            "section": "Optional",
            "url": "https://snapcraft.io/llms-full.txt",
            "title": "Every page in one file",
            "description": "The pages above concatenated as Markdown.",
        },
    ],
)

The application factory pattern works too: store_llm = StoreLLM(base_url=..., ...) then store_llm.init_app(app).

Options

Option Default
base_url required Absolute URL of the site, used for the links in llms.txt
site_name required The H1 of llms.txt; also stripped from page titles (About | SnapcraftAbout)
summary required The blockquote under the H1 of llms.txt
sections {"": "Main pages"} Section label per first path segment. "" is for top-level pages such as /about; "about" for /about/publish. Unknown prefixes go to Other pages
section_order order of sections Section headings in the order they appear. Other pages follows, then any unlisted section alphabetically, then Optional last so agents can skip it
extra_links [] Links discovery cannot find: {"section", "url", "title", "description"} dicts
is_private is_login_gated callable(view) -> bool for pages with no Markdown version. The default recognises the stores' login_required decorator. None makes every page public
strip_elements / strip_classes Vanilla-aware lists Passed to markdown-response
markdown_cache_control private, max-age=3600 Cache-Control of Markdown responses
llms_cache_control public, max-age=43200, ... Cache-Control of /llms.txt and /llms-full.txt

What the store needs to provide

Templates. Discovery parses templates rather than rendering them, so every public page must define meta_title and meta_description blocks with static text, directly or through a layout it extends:

{% block meta_title %}About Snaps | Snapcraft{% endblock %}
{% block meta_description %}Snaps are app packages for desktop, cloud and IoT.{% endblock %}

A page opts out of llms.txt with <meta name="robots" content="noindex">, the same signal that keeps it out of search results. Layouts and partials (file names starting with _) are never listed.

Content. Markdown is extracted from <main id="main-content"> (falling back to <body>). Add data-md-strip to interactive chrome that reads badly as text: copy buttons, share widgets, charts.

Alternate link. Templates get markdown_path, the Markdown URL path of the current page or None for private pages:

{% if markdown_path %}
  <link rel="alternate" type="text/markdown" href="https://snapcraft.io{{ markdown_path }}" />
  <link rel="describedby" href="https://snapcraft.io/llms.txt" />
{% endif %}

Sitemap. app.extensions["store_llm"].sitemap_paths() returns the same pages, so the sitemap and llms.txt never drift.

Build step

Rendering every page for llms-full.txt is too slow to do on request, so generate both files at build time and the routes serve them from the static folder (falling back to rendering live when the files are missing, for development):

python3 -m canonicalwebteam.store_llm generate webapp.app:create_app

The argument is module:attribute, either a Flask app or a factory returning one. Run it from the repository root after the frontend build, for example in the Dockerfile and rockcraft.yaml, and ignore static/llms.txt and static/llms-full.txt in git. The command reports pages without a meta_description and pages that could not be rendered.

Customising the output

The files are rendered from the store_llm/llms.txt and store_llm/llms-full.txt templates. A store can override either by adding a template with the same name to its own templates/ folder; both receive site_name, and llms.txt receives summary and sections ([{"section", "links": [{"title", "url", "description"}]}]).

How pages are discovered

A route is listed when it takes no URL parameters, answers GET, is not private, its view calls render_template("....html") with a literal name and does not redirect, and the template (or a layout it extends) has a non-empty meta_title. Routes rendering the same template or sharing a title are listed once, under the shortest path (/store, not /explore). The homepage is left out: the header of llms.txt already introduces the site.

Download files

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

Source Distribution

canonicalwebteam_store_llm-0.1.0.tar.gz (17.0 kB view details)

Uploaded Source

Built Distribution

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

canonicalwebteam_store_llm-0.1.0-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

Details for the file canonicalwebteam_store_llm-0.1.0.tar.gz.

File metadata

File hashes

Hashes for canonicalwebteam_store_llm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 293fab652d01cd8a95d95eb743ac9c799bc60d1b7d18fb3ee792f8fae6bda3a0
MD5 9b7299b71a4274f0cf11827ea1818dab
BLAKE2b-256 cfb69157a55ba4f07293671d0cefa2e93db8172c84a4c361c720af886b3886cd

See more details on using hashes here.

Provenance

The following attestation bundles were made for canonicalwebteam_store_llm-0.1.0.tar.gz:

Publisher: publish.yaml on canonical/canonicalwebteam.store-llm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file canonicalwebteam_store_llm-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for canonicalwebteam_store_llm-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 56a1d765608f44cbd266db8fce5785a8f789ec9a87767a363deda02d961622c9
MD5 cfb3a3de6f61cba1f175a147f3aad52d
BLAKE2b-256 215804f55ffac351c7aa0c5fea6ccf1dd2f0d4f19604adc082d5a4d1cda148cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for canonicalwebteam_store_llm-0.1.0-py3-none-any.whl:

Publisher: publish.yaml on canonical/canonicalwebteam.store-llm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

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