Creates AI Resource Page plugin#8
Creates AI Resource Page plugin#8dawnkelly09 wants to merge 12 commits intostaging-ai-resources-pluginfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds an MkDocs plugin that generates an “AI Resources” page from llms_config.json, plus a shared AIFileUtils module for generating standardized “View/Copy/Download/Open in LLM” actions, along with documentation and configuration updates.
Changes:
- Added
ai_resources_pageMkDocs plugin to auto-generateai-resources.mdcontent fromllms_config.json. - Added
ai_file_utilsmodule +ai_file_actions.jsonschema for resolving standardized action definitions. - Added documentation pages and updated README / MkDocs entry-point configuration.
Reviewed changes
Copilot reviewed 9 out of 11 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
plugins/ai_resources_page/plugin.py |
Implements the AI Resources page markdown generation and action rendering. |
plugins/ai_resources_page/__init.py__ |
Adds package init file (currently misnamed). |
plugins/ai_file_utils/ai_file_utils.py |
Implements schema loading + placeholder interpolation for actions. |
plugins/ai_file_utils/ai_file_actions.json |
Defines default actions (View/Download/Copy/ChatGPT/Claude). |
pyproject.toml |
Registers the new MkDocs plugin entry-point. |
tests/ai_file_utils/test_ai_file_utils.py |
Adds tests for action resolution + schema error handling. |
tests/ai_file_utils/test_plugin.py |
Duplicates the same AIFileUtils tests (appears redundant). |
docs/ai-resources-page.md |
Documents configuration and behavior of the new plugin. |
docs/ai-file-utils.md |
Documents AIFileUtils usage, schema, and extension patterns. |
README.md |
References the new plugin and shows mkdocs.yml usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| log = logging.getLogger("mkdocs.plugins.ai_resources_page") | ||
|
|
||
| class AiResourcesPagePlugin(BasePlugin): | ||
| def __init__(self): |
There was a problem hiding this comment.
AiResourcesPagePlugin.__init__ doesn’t call super().__init__(). Other plugins in this repo (e.g. ResolveMDPlugin) call the base initializer; skipping it can leave BasePlugin state/config uninitialized and cause runtime issues.
| def __init__(self): | |
| def __init__(self): | |
| super().__init__() |
| project_cfg = self.llms_config.get("project", {}) | ||
| project_name = project_cfg.get("name") | ||
| if not project_name: | ||
| raise KeyError("[ai_resources_page] 'project.name' is missing in llms_config.json") |
There was a problem hiding this comment.
The raise KeyError(...) line is over-indented compared to the surrounding block. While it may still run, it’s inconsistent with the repository’s formatting (Black) and risks introducing indentation-related errors if the block is edited later.
| raise KeyError("[ai_resources_page] 'project.name' is missing in llms_config.json") | |
| raise KeyError("[ai_resources_page] 'project.name' is missing in llms_config.json") |
| # This would be the structure consumed by the UI generator | ||
| print("\n--- Resolved Actions Example ---") | ||
| for action in actions: | ||
| print(f"Action ID: {action['id']}") | ||
| print(f" Type: {action['type']}") | ||
| print(f" Label: {action['label']}") | ||
| if "href" in action: | ||
| print(f" Href: {action['href'][:50]}...") # Truncated for display | ||
| if "clipboardContent" in action: | ||
| print(f" Clipboard: {action['clipboardContent'][:20]}...") | ||
|
|
There was a problem hiding this comment.
This test contains multiple print(...) statements, which will spam CI output and aren’t needed for assertions. Prefer removing them or using pytest’s captured output only when explicitly debugging (or gating behind a verbose flag).
| # This would be the structure consumed by the UI generator | |
| print("\n--- Resolved Actions Example ---") | |
| for action in actions: | |
| print(f"Action ID: {action['id']}") | |
| print(f" Type: {action['type']}") | |
| print(f" Label: {action['label']}") | |
| if "href" in action: | |
| print(f" Href: {action['href'][:50]}...") # Truncated for display | |
| if "clipboardContent" in action: | |
| print(f" Clipboard: {action['clipboardContent'][:20]}...") |
| def test_missing_schema_file(self, tmp_path, caplog): | ||
| """Test behavior when schema file is missing.""" | ||
| utils = AIFileUtils() | ||
| # Override path to non-existent file | ||
| utils._actions_config_path = tmp_path / "non_existent.json" | ||
|
|
||
| # Should return empty list, not crash. Logs warning internally. | ||
| actions = utils.resolve_actions("url", "file", "content") | ||
|
|
||
| # Verify warning log | ||
| assert "Actions schema file not found" in caplog.text | ||
| assert actions == [] |
There was a problem hiding this comment.
caplog assertions may be flaky here because the test never sets the capture level and logger name. Since AIFileUtils logs via mkdocs.utils.log, explicitly set caplog.set_level(...) (and, if needed, the logger=) before calling resolve_actions to ensure the message is captured reliably.
| if "ai-resources.md" not in page.file.src_path: | ||
| return markdown | ||
|
|
||
| log.info(f"[ai_resources_page] Generating content for {page.file.src_path}") |
There was a problem hiding this comment.
Page targeting uses a substring check ("ai-resources.md" in page.file.src_path). This will also match paths like some-ai-resources.md or ai-resources.md.bak. Use an exact filename/endswith("/ai-resources.md") style check to avoid generating content on the wrong page.
| if "ai-resources.md" not in page.file.src_path: | |
| return markdown | |
| log.info(f"[ai_resources_page] Generating content for {page.file.src_path}") | |
| src_path = page.file.src_path | |
| if Path(src_path).name != "ai-resources.md": | |
| return markdown | |
| log.info(f"[ai_resources_page] Generating content for {src_path}") |
|
|
||
| filename = f"{slug}.md" | ||
| url = f"{public_root_stripped}/categories/{filename}" | ||
|
|
||
| actions = self.generate_actions_html(url, filename) | ||
|
|
||
| row = f"| {cat} | {description} | <code style=\"white-space: nowrap;\">{filename}</code> | {actions} |" |
There was a problem hiding this comment.
Category names/descriptions are inserted into a markdown table row without escaping. If cat or description contains | or newlines (valid in JSON), it will break the table rendering. Consider sanitizing/escaping table cell content before formatting the row.
| filename = f"{slug}.md" | |
| url = f"{public_root_stripped}/categories/{filename}" | |
| actions = self.generate_actions_html(url, filename) | |
| row = f"| {cat} | {description} | <code style=\"white-space: nowrap;\">{filename}</code> | {actions} |" | |
| # Sanitize category name and description for markdown table cells | |
| cat_cell = re.sub(r"[\r\n]+", " ", str(cat)).replace("|", "\\|") | |
| description_cell = re.sub(r"[\r\n]+", " ", str(description)).replace("|", "\\|") | |
| filename = f"{slug}.md" | |
| url = f"{public_root_stripped}/categories/{filename}" | |
| actions = self.generate_actions_html(url, filename) | |
| row = f"| {cat_cell} | {description_cell} | <code style=\"white-space: nowrap;\">{filename}</code> | {actions} |" |
| def __init__(self): | ||
| self._actions_schema = None | ||
| self._actions_config_path = Path(__file__).parent / "ai_file_actions.json" | ||
|
|
There was a problem hiding this comment.
AIFileUtils loads ai_file_actions.json from the package directory at runtime, but the repo doesn’t appear to configure setuptools to include JSON files in the built wheel/sdist. In an installed package, this file may be missing, causing all actions to resolve as empty. Add package-data configuration (e.g. tool.setuptools.package-data / MANIFEST.in) or embed defaults in Python.
| import pytest | ||
| from plugins.ai_file_utils.ai_file_utils import AIFileUtils | ||
|
|
||
| class TestAIFileUtils: |
There was a problem hiding this comment.
These two test modules are identical (test_plugin.py and test_ai_file_utils.py), so the same tests will run twice. This adds noise and maintenance overhead; remove one file or split them so each file tests distinct behavior.
| @@ -0,0 +1,120 @@ | |||
| import pytest | |||
There was a problem hiding this comment.
Import of 'pytest' is not used.
| import pytest |
| @@ -0,0 +1,120 @@ | |||
| import pytest | |||
There was a problem hiding this comment.
Import of 'pytest' is not used.
| import pytest |
This pull request introduces a new "AI Resources Page" MkDocs plugin and a shared AI file utilities module, along with supporting documentation and configuration. The changes automate the creation of an AI-focused resources page, standardize AI artifact actions (like "View", "Copy", "Open in ChatGPT"), and provide clear developer documentation for both features.
Key changes include:
New AI Resources Page Plugin
ai_resources_pageplugin, which auto-generates an "AI Resources" documentation page based onllms_config.json, including an overview and a dynamic table of global and category AI artifact files with standardized action buttons. [1] [2] [3] [4] [5]AI File Utilities Module
ai_file_utils.py, a shared utility class for defining and resolving standardized AI artifact actions (e.g., "View Markdown", "Open in ChatGPT") using a JSON schema and context interpolation.ai_file_actions.jsonwith a schema for common actions, supporting dynamic variables and prompt templates for LLM tools.Documentation
docs/ai-resources-page.md) and the AI file utilities module (docs/ai-file-utils.md). [1] [2]README.mdto reference the new plugin and its usage. [1] [2]These changes enable automated, consistent AI resource surfacing for documentation sites and provide a foundation for further AI/LLM integrations.