Skip to content

Add reusable llms/markdown docs builder - #361

Merged
Azaya89 merged 18 commits into
mainfrom
build-llms
Aug 6, 2026
Merged

Add reusable llms/markdown docs builder#361
Azaya89 merged 18 commits into
mainfrom
build-llms

Conversation

@Azaya89

@Azaya89 Azaya89 commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

This PR:

Usage in individual repos

  • Add a local scripts/llmsconfig.py
  • define CONFIG = LlmsBuildConfig(...)
  • run nbsite build-llms --config scripts/llms_config.py

**Example script for hvPlot **
See holoviz/hvplot#1732

scripts/llms_config.py:
"""Example config for building hvPlot markdown docs and llms.txt.
"""

from __future__ import annotations

from pathlib import Path

from nbsite.scripts import LlmsBuildConfig, LlmsSection, MarkdownSource


ROOT = Path(__file__).parent.parent
DOC_DIR = ROOT / 'doc'
BUILTDOCS_DIR = ROOT / 'builtdocs'
OUTPUT_DIR = BUILTDOCS_DIR / 'markdown'
MARKDOWN_BASE_URL = '/markdown'


def _section_label(path: Path) -> str:
    if path.stem == 'index':
        return 'home' if path.parent == Path('.') else path.parent.as_posix().replace('-', ' ')
    return path.stem.replace('_', ' ')


def _api_label(path: Path) -> str:
    name = path.stem
    for prefix in ('hvplot.hvPlot.', 'hvplot.plotting.'):
        if name.startswith(prefix):
            name = name.removeprefix(prefix)
            break
    return name.replace('_', ' ')


CONFIG = LlmsBuildConfig(
    project_title='hvPlot',
    project_description=(
        'hvPlot is a high-level plotting API for the HoloViz ecosystem built on HoloViews. '
        'This file points to the generated markdown documentation selected for code-writing utility.'
    ),
    markdown_root=OUTPUT_DIR,
    llms_output_path=BUILTDOCS_DIR / 'llms.txt',
    markdown_base_url=MARKDOWN_BASE_URL,
    sources=(MarkdownSource(source_dir=DOC_DIR, output_dir=OUTPUT_DIR),),
    sections=(
        LlmsSection(
            title='Home',
            description='Top-level pages in the hvPlot docs tree.',
            path_prefix=Path('.'),
            label_builder=_section_label,
            path_filter=lambda path: len(path.parts) == 1,
        ),
        LlmsSection(
            title='Tutorials',
            description='Step-by-step guides to help you master hvPlot and explore the full HoloViz ecosystem.',
            path_prefix=Path('tutorials'),
        ),
        LlmsSection(
            title='Gallery',
            description='Example visualizations using hvPlot with different backends and datasets.',
            path_prefix=Path('gallery'),
        ),
        LlmsSection(
            title='Reference',
            description='API reference and pages that provide detailed information about hvPlot’s usage.',
            path_prefix=Path('ref'),
            label_builder=_section_label,
            path_filter=lambda path: not path.is_relative_to(Path('ref/api/manual')),
        ),
        LlmsSection(
            title='API',
            description='hvPlot plotting APIs.',
            path_prefix=Path('ref/api/manual'),
            label_builder=_api_label,
        ),
    ),
)

@Azaya89 Azaya89 self-assigned this Jul 27, 2026
@Azaya89
Azaya89 marked this pull request as draft July 27, 2026 19:12
@Azaya89
Azaya89 requested a review from Copilot July 28, 2026 22:26

This comment was marked as off-topic.

@Azaya89

Azaya89 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Added .rst conversion so it works with Holoviews and also stripped some left-over html tags from the converted markdown pages.

HoloViews config file:

holoviews/scripts/llms_config.py
"""Config for building HoloViews markdown docs and llms.txt
from the nbsite llms builder.
"""

from __future__ import annotations

from pathlib import Path

from nbsite.scripts import LlmsBuildConfig, LlmsSection, MarkdownSource

ROOT = Path(__file__).parent.parent
DOC_DIR = ROOT / "doc"
BUILTDOCS_DIR = ROOT / "builtdocs"
OUTPUT_DIR = BUILTDOCS_DIR / "markdown"
MARKDOWN_BASE_URL = "/markdown"


def _section_label(path: Path) -> str:
    if path.stem == "index":
        return "home" if path.parent == Path(".") else path.parent.as_posix().replace("-", " ")
    return path.stem.replace("_", " ")


def _api_label(path: Path) -> str:
    name = path.stem
    for prefix in (
        "holoviews.element.",
        "holoviews.core.",
        "holoviews.ipython.",
        "holoviews.plotting.",
        "holoviews.operation.",
        "holoviews.util.",
    ):
        if name.startswith(prefix):
            name = name.removeprefix(prefix)
            break
    return name.replace("_", " ")


CONFIG = LlmsBuildConfig(
    project_title="HoloViews",
    project_description=(
        "HoloViews is an open-source Python library designed to make data analysis"
        " and visualization seamless and simple. \n"
        "This file points to the selected markdown documentation for code-writing utility."
    ),
    markdown_root=OUTPUT_DIR,
    llms_output_path=BUILTDOCS_DIR / "llms.txt",
    markdown_base_url=MARKDOWN_BASE_URL,
    sources=(
        MarkdownSource(
            source_dir=DOC_DIR,
            output_dir=OUTPUT_DIR,
            rendered_source_dir=BUILTDOCS_DIR,
        ),
    ),
    sections=(
        LlmsSection(
            title="Home",
            description="Top-level pages in the HoloViews docs tree.",
            path_prefix=Path("."),
            label_builder=_section_label,
            path_filter=lambda path: len(path.parts) == 1,
        ),
        LlmsSection(
            title="Getting Started",
            description="Step-by-step guides to get you using HoloViews productively as quickly as possible.",
            path_prefix=Path("getting_started"),
        ),
        LlmsSection(
            title="User Guide",
            description="Key concepts that will help you use HoloViews in your work.",
            path_prefix=Path("user_guide"),
        ),
        LlmsSection(
            title="Gallery",
            description="Example visualizations using HoloViews with different backends and datasets.",
            path_prefix=Path("gallery"),
        ),
        LlmsSection(
            title="Reference Gallery",
            description="More gallery examples using different Holoviews element types.",
            path_prefix=Path("reference"),
            label_builder=_section_label,
        ),
        LlmsSection(
            title="API",
            description="HoloViews plotting APIs.",
            path_prefix=Path("reference_manual"),
            label_builder=_api_label,
        ),
    ),
)

@ahuang11

Copy link
Copy Markdown

Can you show some output for holoviews?

Also can you clarify why rst is needed

@Azaya89

Azaya89 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Can you show some output for holoviews?

holoviews/builtdocs/llms.txt

HoloViews

HoloViews is an open-source Python library designed to make data analysis and visualization seamless and simple.
This file points to the selected markdown documentation for code-writing utility.

All documentation is available as markdown files under /markdown/.

Home

Top-level pages in the HoloViews docs tree.

Getting Started

Step-by-step guides to get you using HoloViews productively as quickly as possible.

User Guide

Key concepts that will help you use HoloViews in your work.

Gallery

Example visualizations using HoloViews with different backends and datasets.

Reference Gallery

More gallery examples using different Holoviews element types.

API

HoloViews plotting APIs.

Also can you clarify why rst is needed

For HoloViews, the API section is generated via .rst files which are first generated when the docs-build command is run:

[feature.doc.tasks]
_docs-generate-rst = 'nbsite generate-rst --org holoviz --project-name holoviews'
_docs-refmanual = 'sphinx-apidoc -e -o doc/reference_manual/ holoviews/ holoviews/tests --ext-autodoc --ext-intersphinx'
_docs-generate = 'nbsite build --what=html --output=builtdocs --org holoviz --project-name holoviews'

[feature.doc.tasks.docs-build]
depends-on = ['_docs-generate-rst', '_docs-refmanual', '_docs-generate']

@Azaya89

Azaya89 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Here's a zip file of the generated markdown directory.

holoviews/builtdocs/markdown.zip

@ahuang11

Copy link
Copy Markdown

Hmm a couple issues I noticed immediately:

  1. The llms.txt takes too much tokens. Better curation is necessary; see https://panel-material-ui.holoviz.org/llms.txt for example (it doesn't list every single item). Also most tool calls read a limited number of lines (i.e. the first 100 lines) so keep that in mind.

  2. There's still a ton of JS junk; try to think whether LLMs can utilize this info or not.

image

@Azaya89

Azaya89 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author
  1. The llms.txt takes too much tokens. Better curation is necessary; see panel-material-ui.holoviz.org/llms.txt for example (it doesn't list every single item). Also most tool calls read a limited number of lines (i.e. the first 100 lines) so keep that in mind.

This can be easily resolved by deciding what goes into each repo's config file LlmsSection.

  1. There's still a ton of JS junk; try to think whether LLMs can utilize this info or not.

OK, will try to fix that. Also important to note here that this is a specifically HoloViews problem.

@ahuang11

ahuang11 commented Jul 30, 2026

Copy link
Copy Markdown

This can be easily resolved by deciding what goes into each repo's config file LlmsSection.

Okay if you can update it accordingly!

this is a specifically HoloViews problem.

So Panel / hvPlot doesn't encounter this?

Thanks!

@Azaya89

Azaya89 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

this is a specifically HoloViews problem.

So Panel / hvPlot doesn't encounter this?

No. here's Panel's markdown zip:
panel/markdown.zip

@Azaya89

Azaya89 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

This can be easily resolved by deciding what goes into each repo's config file LlmsSection.

Okay if you can update it accordingly!

panel/builtdocs/llms.txt

holoviews/builtdocs/llms.txt

hvplot/builtdocs/llms.txt

All below 100 lines now.

@Azaya89

Azaya89 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Fixed the HoloViews markdown files

holoviews/builtdocs/markdown.zip

@ahuang11

ahuang11 commented Jul 31, 2026

Copy link
Copy Markdown

I think the llms.txt should have a sentence saying not all are shown, like I think is only a subset of the many charts hvplot supports.

image

More can be found by ...

Detailed reference documentation for every Panel Material UI component, organized by category.

- [Widgets](/markdown/reference/widgets/index.md): Interactive input widgets (Button, Select, Slider, TextInput, DatePicker, etc.)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (3)

nbsite/scripts/_build_llms_txt.py:585

  • _build_url_pattern_body() builds rels with str(Path(...)), which will use OS-specific separators (e.g. backslashes on Windows). Since this content is emitted into llms.txt as URL/path examples, it should be stable and use forward slashes regardless of platform. Use .as_posix() when computing the relative path string.
    def _rel(path: Path) -> str:
        try:
            return str(path.relative_to(section.path_prefix).with_suffix(""))
        except ValueError:
            return path.stem

nbsite/main.py:28

  • _load_config_object() will raise a bare AttributeError if the requested config attribute is missing from the loaded module. For a CLI, this makes the error harder to interpret. Catch the missing-attribute case and raise a ValueError with a clear message including the module/path and attribute name.
    config = getattr(module, attr)
    return config() if callable(config) else config

nbsite/scripts/_build_llms_txt.py:548

  • generate_index_pages() passes absolute paths to category.label_builder(), but the label builder contract elsewhere (e.g. section label builders) appears to receive paths relative to markdown_root. With a common label builder like path.parent == Path('.') (as in the PR description example), absolute paths will produce incorrect labels. Pass md_file relative to markdown_root into the label builder for consistent behavior.
        for md_file in md_files:
            rel_md = md_file.relative_to(markdown_root).as_posix()
            lines.append(f"- [{category.label_builder(md_file)}]({markdown_base_url}/{rel_md})")

"style",
)

MARKDOWN_STRIP_TAGS = (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these be imported from an standard html lib?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately there's no single package that can replace the values in the constants.

Comment on lines +211 to +225
def _pandoc_command(
input_format: str,
output_path: Path,
input_path: Path,
) -> list[str]:
return [
"pandoc",
"-f",
input_format,
"-t",
"gfm",
"-o",
str(output_path),
str(input_path),
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just import pandoc?

https://boisgera.github.io/pandoc/api/

@Azaya89
Azaya89 requested a review from ahuang11 August 5, 2026 22:46
@Azaya89
Azaya89 merged commit 2b78dbb into main Aug 6, 2026
11 checks passed
@Azaya89
Azaya89 deleted the build-llms branch August 6, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants