feat: Add llms-full.txt#364
Conversation
Design for issue #363. Covers file formats, generation script, QMD content cleaning, build integration, and CI verification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Layouts: use top-level pages only, skip anchor links - Templates: add directory scanning special case - Section name cleaning: strip Quarto markup, HTML, image syntax - API docs: include all individual function pages (matches Pydantic pattern) - _quarto.yml parsing: document four entry formats and recursive walker Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9 tasks covering: QMD cleaning, section name parsing, sidebar walking, URL generation, site structure builder, output generators, main entry point, build integration (Makefile + CI), and end-to-end verification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements clean_qmd_content() in scripts/generate_llms_txt.py that strips Quarto-specific markup (YAML frontmatter, div fences, raw HTML blocks, Shinylive directives) from .qmd files, leaving clean prose and standard markdown code blocks. Includes 12 passing unit tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous assertion was always true due to substring matching with trailing newline differences. Now directly checks no triple newlines remain and verifies the expected output format. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…er, and URL generation helpers Implements Tasks 2, 3, and 4 for the llms.txt generator: - clean_section_name(): strips Quarto/HTML/bold markup from sidebar section names - extract_title(): extracts title/pagetitle from .qmd YAML frontmatter (regex fallback when PyYAML unavailable) - SidebarEntry dataclass + walk_sidebar(): recursively walks _quarto.yml sidebar contents - file_path_to_url(): converts file paths to site URLs with proper index handling All 31 tests pass (12 existing + 19 new). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Page, Subsection, Section dataclasses plus build_site_structure(), _load_page(), _build_templates_section(), and _build_api_sections() to scripts/generate_llms_txt.py. Adds three new tests covering section discovery, template scanning, and anchor-link skipping (34 tests total, all passing). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…files Implements Tasks 6 and 7: - Add BASE_URL and SITE_DESCRIPTION constants - Add _write_header(), generate_llms_txt(), generate_llms_full_txt() output writers - Add main() entry point that generates llms.txt and llms-full.txt from _quarto.yml - Fix _load_page() to skip directory paths (is_file() guard) - Add 4 new tests for output writers (38 total, all passing) - Generate llms.txt (8,553 bytes) and llms-full.txt (573,877 bytes) from live site Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Makefile: new llms-txt target (depends on quartodoc), added to all - _quarto.yml: llms.txt and llms-full.txt as project resources - CI: verify llms.txt is up to date after build, fail with instructions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous regex only matched ::: with {.class} syntax but missed
::: callout-note style fences (no braces). Now matches any line
starting with 3+ colons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These were working documents used during design and implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The layouts sidebar uses anchor hrefs (e.g. /layouts/navbars/index.html#section) for all sub-pages, which caused walk_sidebar to skip them entirely. Fix resolves fragment hrefs to their parent page path and deduplicates, so Navbars, Sidebars, Tabs, Panels & Cards, and Arrange Elements now appear in both llms.txt and llms-full.txt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _load_page now resolves trailing-slash hrefs (e.g. components/) to index.qmd, fixing the Components index page being silently dropped - Rename test_walk_skips_anchor_links to test_walk_resolves_fragment_hrefs and correct its assertion to match actual behavior (resolves to parent page rather than skipping) - Update integration test to verify deduplication of multiple fragment hrefs pointing to the same parent page - Regenerate llms.txt and llms-full.txt (113 pages, +1 Components index) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Drop base_url parameter from file_path_to_url; use BASE_URL directly - Extract API_SUBSECTIONS constant (consistent with SIDEBAR_DISPLAY_NAMES) - Remove seen_subsections list; dict preserves insertion order in Python 3.7+ - Inline _write_header into HEADER constant - Simplify pages_list loop to walrus comprehension in _build_api_sections - Remove narrating inline comments Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Picks up new toolbar/card components, updated layout section names, and updates py-shiny submodule to latest. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Picks up new OpenTelemetry docs from main. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Design for issue #363. Covers file formats, generation script, QMD content cleaning, build integration, and CI verification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Layouts: use top-level pages only, skip anchor links - Templates: add directory scanning special case - Section name cleaning: strip Quarto markup, HTML, image syntax - API docs: include all individual function pages (matches Pydantic pattern) - _quarto.yml parsing: document four entry formats and recursive walker Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9 tasks covering: QMD cleaning, section name parsing, sidebar walking, URL generation, site structure builder, output generators, main entry point, build integration (Makefile + CI), and end-to-end verification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements clean_qmd_content() in scripts/generate_llms_txt.py that strips Quarto-specific markup (YAML frontmatter, div fences, raw HTML blocks, Shinylive directives) from .qmd files, leaving clean prose and standard markdown code blocks. Includes 12 passing unit tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous assertion was always true due to substring matching with trailing newline differences. Now directly checks no triple newlines remain and verifies the expected output format. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…er, and URL generation helpers Implements Tasks 2, 3, and 4 for the llms.txt generator: - clean_section_name(): strips Quarto/HTML/bold markup from sidebar section names - extract_title(): extracts title/pagetitle from .qmd YAML frontmatter (regex fallback when PyYAML unavailable) - SidebarEntry dataclass + walk_sidebar(): recursively walks _quarto.yml sidebar contents - file_path_to_url(): converts file paths to site URLs with proper index handling All 31 tests pass (12 existing + 19 new). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Page, Subsection, Section dataclasses plus build_site_structure(), _load_page(), _build_templates_section(), and _build_api_sections() to scripts/generate_llms_txt.py. Adds three new tests covering section discovery, template scanning, and anchor-link skipping (34 tests total, all passing). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…files Implements Tasks 6 and 7: - Add BASE_URL and SITE_DESCRIPTION constants - Add _write_header(), generate_llms_txt(), generate_llms_full_txt() output writers - Add main() entry point that generates llms.txt and llms-full.txt from _quarto.yml - Fix _load_page() to skip directory paths (is_file() guard) - Add 4 new tests for output writers (38 total, all passing) - Generate llms.txt (8,553 bytes) and llms-full.txt (573,877 bytes) from live site Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Makefile: new llms-txt target (depends on quartodoc), added to all - _quarto.yml: llms.txt and llms-full.txt as project resources - CI: verify llms.txt is up to date after build, fail with instructions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous regex only matched ::: with {.class} syntax but missed
::: callout-note style fences (no braces). Now matches any line
starting with 3+ colons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These were working documents used during design and implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The layouts sidebar uses anchor hrefs (e.g. /layouts/navbars/index.html#section) for all sub-pages, which caused walk_sidebar to skip them entirely. Fix resolves fragment hrefs to their parent page path and deduplicates, so Navbars, Sidebars, Tabs, Panels & Cards, and Arrange Elements now appear in both llms.txt and llms-full.txt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _load_page now resolves trailing-slash hrefs (e.g. components/) to index.qmd, fixing the Components index page being silently dropped - Rename test_walk_skips_anchor_links to test_walk_resolves_fragment_hrefs and correct its assertion to match actual behavior (resolves to parent page rather than skipping) - Update integration test to verify deduplication of multiple fragment hrefs pointing to the same parent page - Regenerate llms.txt and llms-full.txt (113 pages, +1 Components index) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Drop base_url parameter from file_path_to_url; use BASE_URL directly - Extract API_SUBSECTIONS constant (consistent with SIDEBAR_DISPLAY_NAMES) - Remove seen_subsections list; dict preserves insertion order in Python 3.7+ - Inline _write_header into HEADER constant - Simplify pages_list loop to walrus comprehension in _build_api_sections - Remove narrating inline comments Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Picks up new toolbar/card components, updated layout section names, and updates py-shiny submodule to latest. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Picks up new OpenTelemetry docs from main. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nsumption
- Remove llms.txt generation (now handled natively by Quarto)
- Rename make target to llms-full-txt and update CI to match
- Remove /llms.txt from _quarto.yml resources
Content quality improvements to generate_llms_txt.py:
- Preserve <placeholder> syntax in prose (e.g. <name>, <checkbox_id>)
- Resolve all relative/.qmd links to absolute URLs
- Strip authoring template headings (Details, Variations)
- Detect nav hub pages by pagedescription: frontmatter; replace body
with description only (fixes layouts/components index nav chrome)
- Truncate input component pages to first paragraph + See also links
- Strip multi-line HTML comments
- Buffer code blocks; drop hidden blocks (echo/include: false)
- Convert space-variant shinylive fences (``` {shinylive-python})
- Strip Show code/Hide code fold labels
- Add 33 new tests covering all improvements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| - name: Verify llms-full.txt is up to date | ||
| run: | | ||
| make llms-full-txt QUARTO_PATH=quarto | ||
| if ! git diff --exit-code llms-full.txt; then | ||
| echo "" | ||
| echo "ERROR: llms-full.txt is out of date." | ||
| echo "Run 'make llms-full-txt' locally and commit the updated file." | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
I think it probably makes sense for this to come earlier in the workflow. In particular, make all takes a long time -- It would be frustrating to wait for the site to be built only for it to err.
| @@ -0,0 +1,613 @@ | |||
| import sys | |||
There was a problem hiding this comment.
It'd be helpful to have a docstring at the top of the file here to explain the intention behind the tests, when to run them, etc.
| ## Generate llms-full.txt | ||
| .PHONY: llms-full-txt | ||
| llms-full-txt: $(PYBIN) quartodoc | ||
| . $(PYBIN)/activate && python scripts/generate_llms_txt.py |
There was a problem hiding this comment.
Do we not also want to run the tests after generation?
| if ! git diff --exit-code llms-full.txt; then | ||
| echo "" | ||
| echo "ERROR: llms-full.txt is out of date." | ||
| echo "Run 'make llms-full-txt' locally and commit the updated file." |
There was a problem hiding this comment.
To me, llms-full.txt seems like more of a build artifact than a source artifact. I'd personally prefer that it be built at render time and not checked into source.
It's not that I don't see the utility in tracking changes. If we were using a gh-pages branch, we'd have a copy of changes, which is nice.
But I have a strong aversion to having CI fail for things that could be automated. I have come to accept that we do this for build assets for reasons but it's not a pattern I'd recommend we follow for this file in particular.
| #### Action Button | ||
|
|
||
| An action button appears as a button and has a value that increments each time the user presses the button. | ||
|
|
||
| See also: [Action Link](https://shiny.posit.co/py/components/inputs/action-link/) | ||
|
|
||
| --- | ||
| #### Action Link | ||
|
|
||
| An action link appears as a link in your app and has a value that increments each time the user presses the link. | ||
|
|
||
| See also: [Action Button](https://shiny.posit.co/py/components/inputs/action-button/) |
There was a problem hiding this comment.
This whole section surprised me a bit. I would have expected a section like this to include code snippets showing the actual code to create the inputs.
Also the links seem reasonable, but they point to non-LLM-oriented pages. If we're making this whole system of documentation easier to navigate for LLMs, I'd think we'd prefer to link to LLM-ready pages.
I'm not saying that we have to have an LLM-ready markdown file for py/components/inputs/action-button/, but given how little other information is included in this section, we're basically telling the model to go read that file. So there's an asymmetry here.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
||
|
|
||
| def clean_qmd_content(content: str, file_path: str = "") -> str: | ||
| """Strip Quarto-specific markup from .qmd content, keeping prose and code.""" |
There was a problem hiding this comment.
Did you look into using pandoc to handle this conversion? Quarto's markdown is really just pandoc markdown, and pandoc can handle moving between markdown variants.
It's more work, but I think much of this could be done through a Lua filter if the goal is to suppress Quarto-specific elements. Just in general, that stack is much better set up to handle parsing and rewriting markdown.
Frankly, I'm confident this function works right now but I wouldn't want to maintain it at all. Claude changes the calculus and all, but my larger worry than this particular function is that this code blends low-importance mechanical transformations with high-level assumptions about how we've laid out our docs, etc.
Relatedly, did you look into using something like markdownify or markdownify-rs? They work on HTML, but if this code moved away from dealing with the low-level details of parsing our source content I think it'd be more maintainable for us in the long run (not to mention more repeatable if we do something like this in other repos).
At one more level of abstraction higher, if we feel that this overall process is important I think it's worth looking into how to do this inside of Quarto. This kind of extension would be useful anywhere else in the ecosystem, like in quartodoc or Great Docs, and it'd probably be a lot easier to write and maintain if it were closer to the Quarto toolchain (i.e. pandoc and Lua filters).
OTOH, if we're just trying to get this done it seems like using something else that handles the HTML parsing for us would be beneficial even if the output isn't as tight as we'd like.
…re build Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes #363
Auto-generated
llms-full.txtfollowing the llmstxt.org spec, so LLMs can discover and consume Shiny for Python documentation.Key decisions
llms-full.txt(572 KB full content) — includes cleaned page content for RAG/coding assistant use, matching the pattern used by Pydantic and others_quarto.ymlis the source of truth for site structure and page ordering; templates discovered by directory scan (not in sidebars)@dataclassfor site structure types — auto-generates__init__,__repr__, and structural__eq__; the last is essential for test assertions likeassert entries == [SidebarEntry(...)].NamedTuplewas the alternative but feels semantically odd for types with mutablelistfields.Usage
Test plan
python3 -m pytest tests/test_generate_llms_txt.py)llms-full.txthas cleaned content (no:::, no frontmatter, no raw HTML)Implementation plan
File Structure
scripts/generate_llms_txt.pytests/test_generate_llms_txt.pyMakefileall_quarto.ymlllms-full.txtto resources (llms.txtis generated live from quarto from previous PR.github/workflows/deploy-docs.ymlllms-full.txt