Skip to content

Commit ef728e8

Browse files
S1M0N38claude
andcommitted
feat: Add llm_friendly_source option for LLM-compatible source rendering
Add a new configuration option `llm_friendly_source` that renders source code in a format more compatible with LLM-focused tools like mkdocs-llmstxt. When enabled along with `show_source`, this option: - Removes line numbers from source code blocks - Uses simple markdown-style code blocks instead of complex HTML tables - Improves compatibility with tools that convert documentation to LLM-friendly formats Changes: - Add `llm_friendly_source: bool = False` config option in PythonInputOptions - Update function and class templates to support LLM-friendly rendering - Add comprehensive documentation with usage examples - Add tests to verify both traditional and LLM-friendly rendering work correctly Fixes #299 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent a1bce97 commit ef728e8

File tree

5 files changed

+112
-3
lines changed

5 files changed

+112
-3
lines changed

docs/usage/configuration/general.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,62 @@ def some_function():
494494
<p>Docstring of the function.</p>
495495
////
496496
///
497+
498+
[](){#option-llm_friendly_source}
499+
## `llm_friendly_source`
500+
501+
- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }**
502+
<!-- - **:octicons-project-template-24: Template :material-null:** (contained in [`class.html`][class template] and [`function.html`][function template]) -->
503+
504+
When [`show_source`](#show_source) is enabled, render source code in a format more compatible with LLM-focused tools. This removes line numbers and uses simple markdown code blocks instead of complex HTML tables.
505+
506+
This is particularly useful when using tools like mkdocs-llmstxt that convert documentation to LLM-friendly text formats.
507+
508+
```yaml title="in mkdocs.yml (global configuration)"
509+
plugins:
510+
- mkdocstrings:
511+
handlers:
512+
python:
513+
options:
514+
show_source: true
515+
llm_friendly_source: true
516+
```
517+
518+
```md title="or in docs/some_page.md (local configuration)"
519+
::: path.to.object
520+
options:
521+
show_source: true
522+
llm_friendly_source: true
523+
```
524+
525+
/// admonition | Preview
526+
type: preview
527+
528+
//// tab | LLM-friendly source
529+
<h2><code>some_function()</code></h2>
530+
<p>Docstring of the function.</p>
531+
532+
///// details | Source code in `package/module.py`
533+
type: quote
534+
535+
```python
536+
def some_function():
537+
...
538+
```
539+
/////
540+
////
541+
542+
//// tab | Traditional source (with line numbers)
543+
<h2><code>some_function()</code></h2>
544+
<p>Docstring of the function.</p>
545+
546+
///// details | Source code in `package/module.py`
547+
type: quote
548+
549+
```python linenums="1"
550+
def some_function():
551+
...
552+
```
553+
/////
554+
////
555+
///

src/mkdocstrings_handlers/python/_internal/config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,14 @@ class PythonInputOptions:
841841
),
842842
] = True
843843

844+
llm_friendly_source: Annotated[
845+
bool,
846+
_Field(
847+
group="general",
848+
description="When show_source is enabled, render source code in a format more compatible with LLM-focused tools (no line numbers, simple markdown code blocks).",
849+
),
850+
] = False
851+
844852
show_submodules: Annotated[
845853
bool,
846854
_Field(

src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,13 @@ Context:
200200
{{ init.relative_filepath }}
201201
{%- endif -%}
202202
</code></summary>
203-
{{ init.source|highlight(language="python", linestart=init.lineno or 0, linenums=True) }}
203+
{% if config.llm_friendly_source %}
204+
<div class="language-python highlight">
205+
<pre><code class="language-python">{{ init.source }}</code></pre>
206+
</div>
207+
{% else %}
208+
{{ init.source|highlight(language="python", linestart=init.lineno or 0, linenums=True) }}
209+
{% endif %}
204210
</details>
205211
{% endwith %}
206212
{% endif %}
@@ -213,7 +219,13 @@ Context:
213219
{{ class.relative_filepath }}
214220
{%- endif -%}
215221
</code></summary>
216-
{{ class.source|highlight(language="python", linestart=class.lineno or 0, linenums=True) }}
222+
{% if config.llm_friendly_source %}
223+
<div class="language-python highlight">
224+
<pre><code class="language-python">{{ class.source }}</code></pre>
225+
</div>
226+
{% else %}
227+
{{ class.source|highlight(language="python", linestart=class.lineno or 0, linenums=True) }}
228+
{% endif %}
217229
</details>
218230
{% endif %}
219231
{% endif %}

src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,13 @@ Context:
153153
{{ function.relative_filepath }}
154154
{%- endif -%}
155155
</code></summary>
156-
{{ function.source|highlight(language="python", linestart=function.lineno or 0, linenums=True) }}
156+
{% if config.llm_friendly_source %}
157+
<div class="language-python highlight">
158+
<pre><code class="language-python">{{ function.source }}</code></pre>
159+
</div>
160+
{% else %}
161+
{{ function.source|highlight(language="python", linestart=function.lineno or 0, linenums=True) }}
162+
{% endif %}
157163
</details>
158164
{% endif %}
159165
{% endblock source %}

tests/test_handler.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,30 @@ def function(self):
176176
assert handler.render(module, PythonOptions(show_source=True))
177177

178178

179+
def test_llm_friendly_source_rendering(handler: PythonHandler) -> None:
180+
"""Test LLM-friendly source code rendering."""
181+
code = dedent(
182+
"""
183+
class Example:
184+
'''Example class.'''
185+
186+
def method(self):
187+
'''Example method.'''
188+
return "hello"
189+
""",
190+
)
191+
with temporary_visited_module(code) as module:
192+
# Test traditional rendering (with line numbers)
193+
traditional_html = handler.render(module["Example"], PythonOptions(show_source=True, llm_friendly_source=False))
194+
assert "linenums" in traditional_html or "lineno" in traditional_html
195+
196+
# Test LLM-friendly rendering (without line numbers, simple code blocks)
197+
llm_friendly_html = handler.render(module["Example"], PythonOptions(show_source=True, llm_friendly_source=True))
198+
assert '<pre><code class="language-python">' in llm_friendly_html
199+
assert "linenums" not in llm_friendly_html
200+
assert "lineno" not in llm_friendly_html
201+
202+
179203
def test_give_precedence_to_user_paths() -> None:
180204
"""Assert user paths take precedence over default paths."""
181205
last_sys_path = sys.path[-1]

0 commit comments

Comments
 (0)