diff --git a/.github/workflows/validate-docs-site.yaml b/.github/workflows/validate-docs-site.yaml index 35c48aefe5..194fed4651 100644 --- a/.github/workflows/validate-docs-site.yaml +++ b/.github/workflows/validate-docs-site.yaml @@ -184,10 +184,26 @@ jobs: sudo apt-get update sudo apt-get install -y pandoc + - name: Verify chatbot product map is up to date + run: | + python3 site/scripts/generate_chatbot_product_map.py + git diff --exit-code site/llm/chatbot-product-map.md site/llm/chatbot-product-map-frontend-snapshot.json + + - name: Test chatbot product map generator + run: python3 -m unittest discover -s site/scripts -p 'test_generate_chatbot_product_map.py' -v + - name: Validate LLM markdown render run: bash llm/render.sh && bash llm/clean.sh working-directory: site + - name: Verify LLM corpus includes product map and docs IA hub + run: | + test -f site/llm/_llm-output/chatbot-product-map.md + test -f site/llm/_llm-output/AGENTS.md + test -f site/llm/_llm-output/about/contributing/using-the-documentation.md + test ! -f site/llm/_llm-output/about/contributing/validmind-community.md + test ! -d site/llm/_llm-output/about/contributing/style-guide + # Release headroom and shrink before final lightweight steps & post-job - name: Release reserve & shrink if: always() diff --git a/AGENTS.md b/AGENTS.md index b1c662e0e7..271e8e1c83 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,16 @@ If you are an AI agent embedded in ValidMind, your capabilities are documented h This page describes what the assistant can and cannot do, including context-aware features and current limitations. +## Product UI mapping + +The in-app assistant (Valerie) also ingests **`chatbot-product-map.md`** in the LLM corpus. That file maps **platform routes** (for example `/settings/workflows`, `/model-inventory`, `/dashboard`) to documentation URLs and section hints. + +Route and help-link data from the product UI is vendored as **`site/llm/chatbot-product-map-frontend-snapshot.json`**. Regenerate it with `make -C site refresh-chatbot-product-map` when frontend routes or `helpLink` values change (requires a local `validmind/frontend` checkout). + +Use the map when the user’s question is tied to where they are in the product — especially **Settings**, where the UI groups features differently than the documentation sidebars (Configuration, Workflows, Inventory, and so on). + +For documentation organized by topic, continue to use **Using the documentation** (above) and the section table in this file. + ## File format Documentation is written in Quarto Markdown (`.qmd`). Key conventions: diff --git a/site/Makefile b/site/Makefile index ef896310fe..22f6027d50 100644 --- a/site/Makefile +++ b/site/Makefile @@ -13,7 +13,7 @@ SRC_ROOT := _source SRC_DIR := $(SRC_ROOT)/validmind-library # Define .PHONY target for help section -.PHONY: help add-copyright clean clone copy-installation copy-release-notes delete-demo-branch deploy-demo-branch deploy-prod deploy-staging docker-build docker-serve docker-site docker-site-lite docs-site execute generate-sitemap get-api-json get-source kind-serve kind-stop kind-restart kind-logs notebooks python-docs release-notes render-llm template-schema-docs verify-copyright yearly-releases +.PHONY: help add-copyright clean clone copy-installation copy-release-notes delete-demo-branch deploy-demo-branch deploy-prod deploy-staging docker-build docker-serve docker-site docker-site-lite docs-site execute generate-chatbot-product-map refresh-chatbot-product-map generate-sitemap get-api-json get-source kind-serve kind-stop kind-restart kind-logs notebooks python-docs release-notes render-llm template-schema-docs verify-copyright yearly-releases # Help section help: @@ -38,6 +38,8 @@ help: @echo " docker-site Get source, render site with Docker profile, execute notebooks" @echo " docker-site-lite Get source and render site with Docker profile (skips notebook execution)" @echo " docs-site Get all source files and render the production docs site with Quarto" + @echo " generate-chatbot-product-map Generate product-to-docs map (from committed frontend snapshot)" + @echo " refresh-chatbot-product-map Re-extract frontend snapshot + map (requires ../frontend)" @echo " generate-sitemap Generate a sitemap for the static HTML site" @echo " execute Execute a Jupyter Notebook or notebook directory" @echo " get-api-json Download Swagger JSON specs from ValidMind APIs into reference/" @@ -439,6 +441,14 @@ yearly-releases: git status | grep -v 'release-scripts/' quarto preview +# Generate product-to-documentation map for chatbot RAG (uses committed frontend snapshot) +generate-chatbot-product-map: + @python3 scripts/generate_chatbot_product_map.py + +# Refresh vendored frontend snapshot from a local validmind/frontend checkout +refresh-chatbot-product-map: + @python3 scripts/generate_chatbot_product_map.py --from-frontend + # Render site to GFM markdown for LLM ingestion render-llm: @echo "\nRendering site to GFM markdown for LLM ingestion ..." diff --git a/site/llm/README.md b/site/llm/README.md new file mode 100644 index 0000000000..83e6ca897b --- /dev/null +++ b/site/llm/README.md @@ -0,0 +1,54 @@ +# LLM corpus (markdown for RAG) + +This directory holds tooling that renders the documentation site to plain Markdown for ingestion into LanceDB and the in-app assistant (Valerie). + +## Output + +Rendered files land in `site/llm/_llm-output/` (gitignored). CI publishes that directory after `make render-llm`. Deployed environments only pick up new assistant context after the LanceDB artifact is rebuilt from a fresh render. + +## Render locally + +From `site/`: + +```bash +make render-llm +``` + +This runs `llm/render.sh` (temporary minimal `_quarto.yml`, Quarto → GFM) and `llm/clean.sh` (Pandoc cleanup). Equivalent to the **Validate LLM markdown render** step in `.github/workflows/validate-docs-site.yaml`. + +Excluded from the LLM render (among others): notebooks, internal pages, contributor style guide, and most of `about/contributing/`. **Included:** `about/contributing/using-the-documentation.qmd` (docs IA hub for agents). + +Copied into `_llm-output/` after render: + +| File | Source | +|------|--------| +| `AGENTS.md` | Repo root | +| `chatbot-product-map.md` | Generated (see below) | +| `about/contributing/using-the-documentation.md` | Quarto render | + +See also [`AGENTS.md`](../../AGENTS.md) for how agents should use the corpus. + +## Chatbot product map + +Valerie needs routes in the product UI (especially **Settings**) mapped to documentation URLs. Human docs sidebars are organized by topic (Configuration, Workflows, Inventory); the product groups features differently. + +| Artifact | Purpose | +|----------|---------| +| `chatbot-product-map-frontend-snapshot.json` | Vendored extract from `validmind/frontend` (Settings tree, sidebar nav, `helpLink` / docs URLs) | +| `chatbot-product-map.md` | Retrieval-oriented map: routes → primary/related docs + section headings from `.qmd` | + +### Why a frontend snapshot? + +CI does **not** check out `validmind/frontend` (private repo; cross-repo PAT scope). The snapshot is committed in this repo so pipelines can regenerate the map without frontend access. It may lag the live product until someone refreshes it locally. + +### Maintenance + +| Change | Command (from `site/`) | +|--------|-------------------------| +| Docs only (new `.qmd`, heading updates) | `make generate-chatbot-product-map` | +| Product routes or in-app help links | `make refresh-chatbot-product-map` (requires a sibling `../frontend` checkout) | + +Commit both `chatbot-product-map.md` and `chatbot-product-map-frontend-snapshot.json` when the snapshot changes. CI fails if either file is out of date after regeneration. + +Generator: `site/scripts/generate_chatbot_product_map.py` +Tests: `python3 -m unittest discover -s site/scripts -p 'test_generate_chatbot_product_map.py' -v` (from repo root). diff --git a/site/llm/_quarto.yml b/site/llm/_quarto.yml index fecfcafdef..b9322acf09 100644 --- a/site/llm/_quarto.yml +++ b/site/llm/_quarto.yml @@ -12,7 +12,8 @@ project: - "../**/*.qmd" - "!../notebooks/" - "!../404.qmd" - - "!../about/contributing/" + - "!../about/contributing/validmind-community.qmd" + - "!../about/contributing/style-guide/" - "!../about/deployment/" - "!../about/fine-print/" - "!../_site/" diff --git a/site/llm/chatbot-product-map-frontend-snapshot.json b/site/llm/chatbot-product-map-frontend-snapshot.json new file mode 100644 index 0000000000..4ee66823a6 --- /dev/null +++ b/site/llm/chatbot-product-map-frontend-snapshot.json @@ -0,0 +1,365 @@ +{ + "version": 1, + "generated_at": "2026-05-23T01:45:21.129000+00:00", + "settings": [ + { + "path": "/settings/profile", + "label": "Profile", + "group": "Your Account", + "primary_docs": [] + }, + { + "path": "/settings/theme-customization", + "label": "Theme Customization", + "group": "Your Account", + "primary_docs": [] + }, + { + "path": "/settings/organization", + "label": "Organization", + "group": "Organization", + "primary_docs": [] + }, + { + "path": "/settings/authentication", + "label": "Authentication", + "group": "Organization", + "primary_docs": [] + }, + { + "path": "/settings/email-notifications", + "label": "Email Notifications", + "group": "Organization", + "primary_docs": [] + }, + { + "path": "/settings/user-directory", + "label": "User Directory", + "group": "Users & Access", + "primary_docs": [ + { + "path": "/guide/configuration/managing-users.html", + "anchor": null + } + ] + }, + { + "path": "/settings/invitation", + "label": "Invite New Users", + "group": "Users & Access", + "primary_docs": [ + { + "path": "/guide/configuration/managing-users.html", + "anchor": null + } + ] + }, + { + "path": "/settings/roles", + "label": "Roles", + "group": "Users & Access", + "primary_docs": [ + { + "path": "/guide/configuration/managing-users.html", + "anchor": null + } + ] + }, + { + "path": "/settings/permissions", + "label": "Permissions", + "group": "Users & Access", + "primary_docs": [] + }, + { + "path": "/settings/groups", + "label": "Groups", + "group": "Users & Access", + "primary_docs": [] + }, + { + "path": "/settings/integrations/connections", + "label": "Connections", + "group": "Integrations", + "primary_docs": [] + }, + { + "path": "/settings/integrations/secrets", + "label": "Secrets", + "group": "Integrations", + "primary_docs": [] + }, + { + "path": "/settings/integrations/data-exports", + "label": "Analytics Exports", + "group": "Integrations", + "primary_docs": [] + }, + { + "path": "/settings/finding-types", + "label": "Artifact Types", + "group": "Artifacts", + "primary_docs": [] + }, + { + "path": "/settings/finding-severities", + "label": "Artifact Severities", + "group": "Artifacts", + "primary_docs": [] + }, + { + "path": "/settings/finding-custom-fields", + "label": "Artifact Fields", + "group": "Artifacts", + "primary_docs": [] + }, + { + "path": "/settings/regulations", + "label": "Regulations & Policies", + "group": "Governance", + "primary_docs": [] + }, + { + "path": "/settings/risk-areas", + "label": "Risk Areas & Validation Guidelines", + "group": "Governance", + "primary_docs": [] + }, + { + "path": "/settings/workflows", + "label": "Workflows", + "group": "Governance", + "primary_docs": [] + }, + { + "path": "/settings/workflow-states", + "label": "Workflow States", + "group": "Governance", + "primary_docs": [] + }, + { + "path": "/settings/attestation-templates", + "label": "Attestation Templates", + "group": "Governance", + "primary_docs": [] + }, + { + "path": "/settings/document-types", + "label": "Document Types", + "group": "Documents", + "primary_docs": [] + }, + { + "path": "/settings/templates", + "label": "Templates", + "group": "Documents", + "primary_docs": [] + }, + { + "path": "/settings/block-library", + "label": "Block Library", + "group": "Documents", + "primary_docs": [] + } + ], + "nav": [ + { + "path": "/dashboard", + "label": "Dashboard", + "group": "Main navigation", + "primary_docs": [] + }, + { + "path": "/validation-issues", + "label": "Validation Issues", + "group": "Main navigation", + "primary_docs": [] + }, + { + "path": "/attestations", + "label": "Attestations", + "group": "Main navigation", + "primary_docs": [] + }, + { + "path": "/artifacts", + "label": "Artifacts", + "group": "Main navigation", + "primary_docs": [] + }, + { + "path": "/workflows", + "label": "Workflows", + "group": "Main navigation", + "primary_docs": [] + }, + { + "path": "/analytics", + "label": "sidebar.analytics", + "group": "Main navigation", + "primary_docs": [] + }, + { + "path": "/settings", + "label": "Settings", + "group": "Main navigation", + "primary_docs": [] + } + ], + "file_links": { + "/settings": [ + { + "path": "/guide/configuration/managing-users.html", + "anchor": null + } + ], + "/settings/attestations": [ + { + "path": "/guide/attestation/manage-attestations.html", + "anchor": null + }, + { + "path": "/guide/shared/work-with-filters.html", + "anchor": null + } + ], + "/settings/authentication": [ + { + "path": "/installation/security/configure-single-sign-on-sso.html", + "anchor": null + } + ], + "/settings/block-library": [ + { + "path": "/guide/templates/manage-text-block-library.html", + "anchor": null + } + ], + "/settings/custom-fields": [ + { + "path": "/guide/model-inventory/manage-model-inventory-fields.html", + "anchor": null + } + ], + "/settings/document-types": [ + { + "path": "/guide/templates/manage-document-types.html", + "anchor": null + } + ], + "/settings/email-notifications": [ + { + "path": "/guide/configuration/manage-platform-notifications.html", + "anchor": "customize-email-notifications" + } + ], + "/settings/finding-custom-fields": [ + { + "path": "/guide/model-validation/manage-model-finding-fields.html", + "anchor": null + } + ], + "/settings/finding-severities": [ + { + "path": "/guide/model-validation/manage-artifact-severities.html", + "anchor": null + } + ], + "/settings/finding-types": [ + { + "path": "/guide/model-validation/manage-artifact-types.html", + "anchor": null + } + ], + "/settings/groups": [ + { + "path": "/guide/configuration/manage-groups.html", + "anchor": null + } + ], + "/settings/invitation": [ + { + "path": "/guide/configuration/manage-users.html", + "anchor": "manage-user-invitations" + } + ], + "/settings/organization": [ + { + "path": "/guide/configuration/managing-your-organization.html", + "anchor": null + } + ], + "/settings/permissions": [ + { + "path": "/guide/configuration/manage-permissions.html", + "anchor": null + } + ], + "/settings/primary-record-type-stages": [ + { + "path": "/guide/workflows/manage-model-stages.html", + "anchor": null + } + ], + "/settings/profile": [ + { + "path": "/guide/configuration/view-your-profile.html", + "anchor": null + } + ], + "/settings/regulation-policy": [ + { + "path": "/guide/templates/customize-virtual-document-validator.html", + "anchor": "add-or-edit-assessment-questions" + } + ], + "/settings/risk-areas": [ + { + "path": "/guide/model-validation/manage-validation-guidelines.html", + "anchor": null + } + ], + "/settings/roles": [ + { + "path": "/guide/configuration/manage-roles.html", + "anchor": null + } + ], + "/settings/stakeholders": [ + { + "path": "/guide/configuration/manage-model-stakeholder-types.html", + "anchor": null + } + ], + "/settings/statuses": [ + { + "path": "/guide/workflows/manage-model-stages.html", + "anchor": null + } + ], + "/settings/templates": [ + { + "path": "/guide/templates/customize-document-templates.html", + "anchor": null + } + ], + "/settings/user-directory": [ + { + "path": "/guide/configuration/manage-users.html", + "anchor": null + } + ], + "/settings/workflow-states": [ + { + "path": "/guide/workflows/workflow-states.html", + "anchor": null + } + ], + "/settings/workflows": [ + { + "path": "/guide/workflows/setting-up-workflows.html", + "anchor": null + } + ] + } +} diff --git a/site/llm/chatbot-product-map.md b/site/llm/chatbot-product-map.md new file mode 100644 index 0000000000..ad1e484cb6 --- /dev/null +++ b/site/llm/chatbot-product-map.md @@ -0,0 +1,580 @@ +# ValidMind product-to-documentation map + +> Auto-generated. Maps in-product routes to documentation URLs and key sections. +> For how documentation is organized by topic, see `AGENTS.md` and +> [Using the documentation](/about/contributing/using-the-documentation.html). + +## Settings + +### Main navigation + +#### `/settings` — Settings + +**Docs (primary):** + +- `/guide/configuration/managing-users.html` + - Sections: Key concepts; Key terms; Default roles; User management + +### Governance + +#### `/settings/attestation-templates` — Attestation Templates + +**Docs (related):** + +- `/guide/attestation/approve-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/manage-attestations.html` + - Sections: Prerequisites; Add attestation templates; Test attestation schedules; Edit attestation periods; Cancel attestation periods; View attestations dashboard; Progress; Responses +- `/guide/attestation/review-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/submit-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/working-with-attestations.html` + - Sections: Prerequisites; Key concepts; Where do I access attestations?; How does the attestation process work?; How are attestation submissions organized?; How do I create meaningful attestation questionnaires?; Manage attestations +- `/guide/templates/customize-document-checker.html` + - Sections: Prerequisites; Manage regulations and policies; Manage assessments; Default assessments provided by } cannot be edited, only cloned.; Add or clone assessments; Add or edit assessment questions; Add assessment questions; Edit assessment questions + +- *No direct help link in frontend; related docs inferred from keywords.* + +### Main navigation + +#### `/settings/attestations` — /settings/attestations + +**Docs (primary):** + +- `/guide/attestation/manage-attestations.html` + - Sections: Prerequisites; Add attestation templates; Test attestation schedules; Edit attestation periods; Cancel attestation periods; View attestations dashboard; Progress; Responses +- `/guide/shared/work-with-filters.html` + +**Docs (related):** + +- `/guide/attestation/approve-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/review-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/submit-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/working-with-attestations.html` + - Sections: Prerequisites; Key concepts; Where do I access attestations?; How does the attestation process work?; How are attestation submissions organized?; How do I create meaningful attestation questionnaires?; Manage attestations + +### Organization + +#### `/settings/authentication` — Authentication + +**Docs (primary):** + +- `/installation/security/configure-single-sign-on-sso.html` + - Sections: What is SSO?; Prerequisites; Step 1: Set up Microsoft Entra for SSO; Step 2: Contact ValidMind to enable SSO + +**Docs (related):** + +- `/guide/configuration/managing-your-organization.html` + - Sections: Prerequisites; Switch between organizations; Change names of organizations; Manage document defaults; Tracked changes; Numbered table and figure captions; Organization setup + +### Documents + +#### `/settings/block-library` — Block Library + +**Docs (primary):** + +- `/guide/templates/manage-text-block-library.html` + - Sections: Prerequisites; Add text blocks; Add existing text blocks to library; Duplicate text blocks; Edit text blocks; Delete text blocks + +**Docs (related):** + +- `/guide/templates/customize-document-checker.html` + - Sections: Prerequisites; Manage regulations and policies; Manage assessments; Default assessments provided by } cannot be edited, only cloned.; Add or clone assessments; Add or edit assessment questions; Add assessment questions; Edit assessment questions +- `/guide/templates/customize-document-templates.html` + - Sections: Prerequisites; Edit template outlines; Configure assessment options[^4]; Edit YAML templates; Template schema; Troubleshooting YAML templates; Add text blocks to templates; Add text blocks via template outlines +- `/guide/templates/manage-document-templates.html` + - Sections: Prerequisites; View document templates; Edit document template outlines; Swap document templates; View currently applied templates; Swap between templates; Duplicate document templates; Delete document templates +- `/guide/templates/manage-document-types.html` + - Sections: Prerequisites; Add document types; Edit or delete document types; Development, Validation, and Monitoring document types are stock types and cannot be deleted. +- `/guide/templates/manage-documents.html` + - Sections: Prerequisites; Add record documents; How do I get the best results when converting PDFs into editable documents?; How can I trust that the conversion is accurate?; Troubleshooting; My PDF conversion is stuck. What can I do?; Edit record documents; Delete record documents +- `/guide/templates/working-with-document-templates.html` + - Sections: What's next + +### Main navigation + +#### `/settings/custom-fields` — /settings/custom-fields + +**Docs (primary):** + +- `/guide/model-inventory/manage-model-inventory-fields.html` + - Note: no matching `.qmd` source found + +### Documents + +#### `/settings/document-types` — Document Types + +**Docs (primary):** + +- `/guide/templates/manage-document-types.html` + - Sections: Prerequisites; Add document types; Edit or delete document types; Development, Validation, and Monitoring document types are stock types and cannot be deleted. + +**Docs (related):** + +- `/guide/templates/customize-document-checker.html` + - Sections: Prerequisites; Manage regulations and policies; Manage assessments; Default assessments provided by } cannot be edited, only cloned.; Add or clone assessments; Add or edit assessment questions; Add assessment questions; Edit assessment questions +- `/guide/templates/customize-document-templates.html` + - Sections: Prerequisites; Edit template outlines; Configure assessment options[^4]; Edit YAML templates; Template schema; Troubleshooting YAML templates; Add text blocks to templates; Add text blocks via template outlines +- `/guide/templates/manage-document-templates.html` + - Sections: Prerequisites; View document templates; Edit document template outlines; Swap document templates; View currently applied templates; Swap between templates; Duplicate document templates; Delete document templates +- `/guide/templates/manage-documents.html` + - Sections: Prerequisites; Add record documents; How do I get the best results when converting PDFs into editable documents?; How can I trust that the conversion is accurate?; Troubleshooting; My PDF conversion is stuck. What can I do?; Edit record documents; Delete record documents +- `/guide/templates/manage-text-block-library.html` + - Sections: Prerequisites; Add text blocks; Add existing text blocks to library; Duplicate text blocks; Edit text blocks; Delete text blocks +- `/guide/templates/working-with-document-templates.html` + - Sections: What's next + +### Organization + +#### `/settings/email-notifications` — Email Notifications + +**Docs (primary):** + +- `/guide/configuration/manage-platform-notifications.html` (section: #customize-email-notifications) + - Sections: Prerequisites; View platform notifications; Review updates; Mark updates as read; Dismiss updates; Customize email notifications + +**Docs (related):** + +- `/guide/configuration/managing-your-organization.html` + - Sections: Prerequisites; Switch between organizations; Change names of organizations; Manage document defaults; Tracked changes; Numbered table and figure captions; Organization setup + +### Artifacts + +#### `/settings/finding-custom-fields` — Artifact Fields + +**Docs (primary):** + +- `/guide/model-validation/manage-model-finding-fields.html` + - Note: no matching `.qmd` source found + +**Docs (related):** + +- `/guide/templates/customize-document-checker.html` + - Sections: Prerequisites; Manage regulations and policies; Manage assessments; Default assessments provided by } cannot be edited, only cloned.; Add or clone assessments; Add or edit assessment questions; Add assessment questions; Edit assessment questions +- `/guide/templates/customize-document-templates.html` + - Sections: Prerequisites; Edit template outlines; Configure assessment options[^4]; Edit YAML templates; Template schema; Troubleshooting YAML templates; Add text blocks to templates; Add text blocks via template outlines +- `/guide/templates/manage-document-templates.html` + - Sections: Prerequisites; View document templates; Edit document template outlines; Swap document templates; View currently applied templates; Swap between templates; Duplicate document templates; Delete document templates +- `/guide/templates/manage-document-types.html` + - Sections: Prerequisites; Add document types; Edit or delete document types; Development, Validation, and Monitoring document types are stock types and cannot be deleted. +- `/guide/templates/manage-documents.html` + - Sections: Prerequisites; Add record documents; How do I get the best results when converting PDFs into editable documents?; How can I trust that the conversion is accurate?; Troubleshooting; My PDF conversion is stuck. What can I do?; Edit record documents; Delete record documents +- `/guide/templates/manage-text-block-library.html` + - Sections: Prerequisites; Add text blocks; Add existing text blocks to library; Duplicate text blocks; Edit text blocks; Delete text blocks + +#### `/settings/finding-severities` — Artifact Severities + +**Docs (primary):** + +- `/guide/model-validation/manage-artifact-severities.html` + - Note: no matching `.qmd` source found + +**Docs (related):** + +- `/guide/templates/customize-document-checker.html` + - Sections: Prerequisites; Manage regulations and policies; Manage assessments; Default assessments provided by } cannot be edited, only cloned.; Add or clone assessments; Add or edit assessment questions; Add assessment questions; Edit assessment questions +- `/guide/templates/customize-document-templates.html` + - Sections: Prerequisites; Edit template outlines; Configure assessment options[^4]; Edit YAML templates; Template schema; Troubleshooting YAML templates; Add text blocks to templates; Add text blocks via template outlines +- `/guide/templates/manage-document-templates.html` + - Sections: Prerequisites; View document templates; Edit document template outlines; Swap document templates; View currently applied templates; Swap between templates; Duplicate document templates; Delete document templates +- `/guide/templates/manage-document-types.html` + - Sections: Prerequisites; Add document types; Edit or delete document types; Development, Validation, and Monitoring document types are stock types and cannot be deleted. +- `/guide/templates/manage-documents.html` + - Sections: Prerequisites; Add record documents; How do I get the best results when converting PDFs into editable documents?; How can I trust that the conversion is accurate?; Troubleshooting; My PDF conversion is stuck. What can I do?; Edit record documents; Delete record documents +- `/guide/templates/manage-text-block-library.html` + - Sections: Prerequisites; Add text blocks; Add existing text blocks to library; Duplicate text blocks; Edit text blocks; Delete text blocks + +#### `/settings/finding-types` — Artifact Types + +**Docs (primary):** + +- `/guide/model-validation/manage-artifact-types.html` + - Note: no matching `.qmd` source found + +**Docs (related):** + +- `/guide/templates/customize-document-checker.html` + - Sections: Prerequisites; Manage regulations and policies; Manage assessments; Default assessments provided by } cannot be edited, only cloned.; Add or clone assessments; Add or edit assessment questions; Add assessment questions; Edit assessment questions +- `/guide/templates/customize-document-templates.html` + - Sections: Prerequisites; Edit template outlines; Configure assessment options[^4]; Edit YAML templates; Template schema; Troubleshooting YAML templates; Add text blocks to templates; Add text blocks via template outlines +- `/guide/templates/manage-document-templates.html` + - Sections: Prerequisites; View document templates; Edit document template outlines; Swap document templates; View currently applied templates; Swap between templates; Duplicate document templates; Delete document templates +- `/guide/templates/manage-document-types.html` + - Sections: Prerequisites; Add document types; Edit or delete document types; Development, Validation, and Monitoring document types are stock types and cannot be deleted. +- `/guide/templates/manage-documents.html` + - Sections: Prerequisites; Add record documents; How do I get the best results when converting PDFs into editable documents?; How can I trust that the conversion is accurate?; Troubleshooting; My PDF conversion is stuck. What can I do?; Edit record documents; Delete record documents +- `/guide/templates/manage-text-block-library.html` + - Sections: Prerequisites; Add text blocks; Add existing text blocks to library; Duplicate text blocks; Edit text blocks; Delete text blocks + +### Users & Access + +#### `/settings/groups` — Groups + +**Docs (primary):** + +- `/guide/configuration/manage-groups.html` + - Sections: Prerequisites; View group details; Add new groups; Remove groups; Add or remove group members + +**Docs (related):** + +- `/guide/configuration/manage-users.html` + - Sections: Prerequisites; View and search for users; Manage user invitations; Invite new users; Monitor user invitations; Manage user roles +- `/guide/configuration/managing-users.html` + - Sections: Key concepts; Key terms; Default roles; User management + +### Integrations + +#### `/settings/integrations/connections` — Connections + +**Docs (related):** + +- `/faq/faq-integrations.html` + - Sections: Which languages, libraries, and environments do you support?; Currently, we support **Python }** and the most popular AI/ML and data science libraries.; What test ingestion or modeling techniques are supported?; What large language model (LLM) features are offered?; What deployment options are supported by }?; Learn more +- `/guide/configuration/configure-aws-privatelink.html` + - Sections: Prerequisites; VPC service information; Steps; What's next +- `/guide/configuration/configure-azure-private-link.html` + - Sections: Prerequisites; VNet service information; Steps; What's next +- `/guide/configuration/configure-google-private-service-connect.html` + - Sections: Prerequisites; VPC service information; Configure your Google Cloud Platform project; Request access from }; Prepare your network for connection; Create an endpoint to connect to }; Steps; Create an endpoint to connect to the } authentication service +- `/guide/configuration/customize-your-dashboard.html` + - Sections: Prerequisites; Manage dashboards; Add dashboards; Edit or remove dashboards; Manage widgets; Arrange widgets; Add widgets; Remove widgets +- `/guide/configuration/manage-groups.html` + - Sections: Prerequisites; View group details; Add new groups; Remove groups; Add or remove group members + +- *No direct help link in frontend; related docs inferred from keywords.* + +#### `/settings/integrations/data-exports` — Analytics Exports + +**Docs (related):** + +- `/faq/faq-integrations.html` + - Sections: Which languages, libraries, and environments do you support?; Currently, we support **Python }** and the most popular AI/ML and data science libraries.; What test ingestion or modeling techniques are supported?; What large language model (LLM) features are offered?; What deployment options are supported by }?; Learn more +- `/faq/faq-reporting.html` + - Sections: What analytic features are offered by }?; Learn more +- `/guide/configuration/configure-aws-privatelink.html` + - Sections: Prerequisites; VPC service information; Steps; What's next +- `/guide/configuration/configure-azure-private-link.html` + - Sections: Prerequisites; VNet service information; Steps; What's next +- `/guide/configuration/configure-google-private-service-connect.html` + - Sections: Prerequisites; VPC service information; Configure your Google Cloud Platform project; Request access from }; Prepare your network for connection; Create an endpoint to connect to }; Steps; Create an endpoint to connect to the } authentication service +- `/guide/configuration/customize-your-dashboard.html` + - Sections: Prerequisites; Manage dashboards; Add dashboards; Edit or remove dashboards; Manage widgets; Arrange widgets; Add widgets; Remove widgets + +- *No direct help link in frontend; related docs inferred from keywords.* + +#### `/settings/integrations/secrets` — Secrets + +**Docs (related):** + +- `/faq/faq-integrations.html` + - Sections: Which languages, libraries, and environments do you support?; Currently, we support **Python }** and the most popular AI/ML and data science libraries.; What test ingestion or modeling techniques are supported?; What large language model (LLM) features are offered?; What deployment options are supported by }?; Learn more +- `/guide/configuration/configure-aws-privatelink.html` + - Sections: Prerequisites; VPC service information; Steps; What's next +- `/guide/configuration/configure-azure-private-link.html` + - Sections: Prerequisites; VNet service information; Steps; What's next +- `/guide/configuration/configure-google-private-service-connect.html` + - Sections: Prerequisites; VPC service information; Configure your Google Cloud Platform project; Request access from }; Prepare your network for connection; Create an endpoint to connect to }; Steps; Create an endpoint to connect to the } authentication service +- `/guide/configuration/customize-your-dashboard.html` + - Sections: Prerequisites; Manage dashboards; Add dashboards; Edit or remove dashboards; Manage widgets; Arrange widgets; Add widgets; Remove widgets +- `/guide/configuration/manage-groups.html` + - Sections: Prerequisites; View group details; Add new groups; Remove groups; Add or remove group members + +- *No direct help link in frontend; related docs inferred from keywords.* + +### Users & Access + +#### `/settings/invitation` — Invite New Users + +**Docs (primary):** + +- `/guide/configuration/managing-users.html` + - Sections: Key concepts; Key terms; Default roles; User management +- `/guide/configuration/manage-users.html` (section: #manage-user-invitations) + - Sections: Prerequisites; View and search for users; Manage user invitations; Invite new users; Monitor user invitations; Manage user roles + +### Organization + +#### `/settings/organization` — Organization + +**Docs (primary):** + +- `/guide/configuration/managing-your-organization.html` + - Sections: Prerequisites; Switch between organizations; Change names of organizations; Manage document defaults; Tracked changes; Numbered table and figure captions; Organization setup + +### Users & Access + +#### `/settings/permissions` — Permissions + +**Docs (primary):** + +- `/guide/configuration/manage-permissions.html` + - Sections: Prerequisites; Steps + +**Docs (related):** + +- `/guide/configuration/manage-users.html` + - Sections: Prerequisites; View and search for users; Manage user invitations; Invite new users; Monitor user invitations; Manage user roles +- `/guide/configuration/managing-users.html` + - Sections: Key concepts; Key terms; Default roles; User management + +### Main navigation + +#### `/settings/primary-record-type-stages` — /settings/primary-record-type-stages + +**Docs (primary):** + +- `/guide/workflows/manage-model-stages.html` + - Note: no matching `.qmd` source found + +### Your Account + +#### `/settings/profile` — Profile + +**Docs (primary):** + +- `/guide/configuration/view-your-profile.html` + - Note: no matching `.qmd` source found + +**Docs (related):** + +- `/guide/configuration/manage-your-profile.html` + - Sections: Prerequisites; Access your profile; Onboarding; User Interface Preferences; Terms; Localization; Access Keys; To revoke and regenerate keys, click **Revoke & Regenerate Keys**. +- `/guide/configuration/personalizing-validmind.html` + +### Main navigation + +#### `/settings/regulation-policy` — /settings/regulation-policy + +**Docs (primary):** + +- `/guide/templates/customize-virtual-document-validator.html` (section: #add-or-edit-assessment-questions) + - Note: no matching `.qmd` source found + +### Governance + +#### `/settings/regulations` — Regulations & Policies + +- *No direct help link; content may be covered under scattered guide sections.* + +#### `/settings/risk-areas` — Risk Areas & Validation Guidelines + +**Docs (primary):** + +- `/guide/model-validation/manage-validation-guidelines.html` + - Note: no matching `.qmd` source found + +### Users & Access + +#### `/settings/roles` — Roles + +**Docs (primary):** + +- `/guide/configuration/managing-users.html` + - Sections: Key concepts; Key terms; Default roles; User management +- `/guide/configuration/manage-roles.html` + - Sections: Prerequisites; Add or update roles; Two special default roles provided by } have unique characteristics.; Manage role permissions; Manage role users; Rename existing roles + +**Docs (related):** + +- `/guide/configuration/manage-users.html` + - Sections: Prerequisites; View and search for users; Manage user invitations; Invite new users; Monitor user invitations; Manage user roles + +### Main navigation + +#### `/settings/stakeholders` — /settings/stakeholders + +**Docs (primary):** + +- `/guide/configuration/manage-model-stakeholder-types.html` + - Note: no matching `.qmd` source found + +#### `/settings/statuses` — /settings/statuses + +**Docs (primary):** + +- `/guide/workflows/manage-model-stages.html` + - Note: no matching `.qmd` source found + +### Documents + +#### `/settings/templates` — Templates + +**Docs (primary):** + +- `/guide/templates/customize-document-templates.html` + - Sections: Prerequisites; Edit template outlines; Configure assessment options[^4]; Edit YAML templates; Template schema; Troubleshooting YAML templates; Add text blocks to templates; Add text blocks via template outlines + +**Docs (related):** + +- `/guide/templates/customize-document-checker.html` + - Sections: Prerequisites; Manage regulations and policies; Manage assessments; Default assessments provided by } cannot be edited, only cloned.; Add or clone assessments; Add or edit assessment questions; Add assessment questions; Edit assessment questions +- `/guide/templates/manage-document-templates.html` + - Sections: Prerequisites; View document templates; Edit document template outlines; Swap document templates; View currently applied templates; Swap between templates; Duplicate document templates; Delete document templates +- `/guide/templates/manage-document-types.html` + - Sections: Prerequisites; Add document types; Edit or delete document types; Development, Validation, and Monitoring document types are stock types and cannot be deleted. +- `/guide/templates/manage-documents.html` + - Sections: Prerequisites; Add record documents; How do I get the best results when converting PDFs into editable documents?; How can I trust that the conversion is accurate?; Troubleshooting; My PDF conversion is stuck. What can I do?; Edit record documents; Delete record documents +- `/guide/templates/manage-text-block-library.html` + - Sections: Prerequisites; Add text blocks; Add existing text blocks to library; Duplicate text blocks; Edit text blocks; Delete text blocks +- `/guide/templates/working-with-document-templates.html` + - Sections: What's next + +### Your Account + +#### `/settings/theme-customization` — Theme Customization + +- *No direct help link; content may be covered under scattered guide sections.* + +### Users & Access + +#### `/settings/user-directory` — User Directory + +**Docs (primary):** + +- `/guide/configuration/managing-users.html` + - Sections: Key concepts; Key terms; Default roles; User management +- `/guide/configuration/manage-users.html` + - Sections: Prerequisites; View and search for users; Manage user invitations; Invite new users; Monitor user invitations; Manage user roles + +### Governance + +#### `/settings/workflow-states` — Workflow States + +**Docs (primary):** + +- `/guide/workflows/workflow-states.html` + - Sections: Prerequisites; Manage workflow states + +**Docs (related):** + +- `/faq/faq-workflows.html` + - Sections: Can I customize workflows within }?; What statuses are available for use in workflows?; Can we work with disconnected workflows?; You can also leverage the } once you are ready to document a specific model for review and validation.; Learn more +- `/guide/integrations/integrations-examples/use-webhooks-with-workflows.html` + - Sections: Prerequisites; Start a workflow via webhook; a. Configure workflow in }; b. Start workflow from external system; Trigger a paused workflow to continue; a. Configure workflow in }; b. Trigger workflow to continue from external system +- `/guide/workflows/conditional-step-requirements.html` + - Sections: Prerequisites; Configure conditional requirements +- `/guide/workflows/configure-workflows.html` + - Sections: Prerequisites; Create custom workflows; 1. Add new workflows; 2. Configure workflow steps; 3. Link workflow together; Workflow steps relationship unclear on your canvas?; 4. Publish workflow; Clone existing workflows +- `/guide/workflows/introduction-to-workflows.html` + - Sections: Workflow elements; What's next +- `/guide/workflows/manage-record-stages.html` + - Sections: Prerequisites; Add record stages; Edit or delete record stages + +#### `/settings/workflows` — Workflows + +**Docs (primary):** + +- `/guide/workflows/setting-up-workflows.html` + - Sections: View, sort, and filter workflows; Sort workflows; Filter workflows; How do I create effective filters?; Set up workflows; What's next + +**Docs (related):** + +- `/faq/faq-workflows.html` + - Sections: Can I customize workflows within }?; What statuses are available for use in workflows?; Can we work with disconnected workflows?; You can also leverage the } once you are ready to document a specific model for review and validation.; Learn more +- `/guide/integrations/integrations-examples/use-webhooks-with-workflows.html` + - Sections: Prerequisites; Start a workflow via webhook; a. Configure workflow in }; b. Start workflow from external system; Trigger a paused workflow to continue; a. Configure workflow in }; b. Trigger workflow to continue from external system +- `/guide/workflows/conditional-step-requirements.html` + - Sections: Prerequisites; Configure conditional requirements +- `/guide/workflows/configure-workflows.html` + - Sections: Prerequisites; Create custom workflows; 1. Add new workflows; 2. Configure workflow steps; 3. Link workflow together; Workflow steps relationship unclear on your canvas?; 4. Publish workflow; Clone existing workflows +- `/guide/workflows/introduction-to-workflows.html` + - Sections: Workflow elements; What's next +- `/guide/workflows/manage-record-stages.html` + - Sections: Prerequisites; Add record stages; Edit or delete record stages + +## Main application + +#### `/analytics` — sidebar.analytics + +**Docs (related):** + +- `/faq/faq-reporting.html` + - Sections: What analytic features are offered by }?; Learn more +- `/guide/monitoring/enable-monitoring.html` + - Sections: Prerequisites; Steps; 1. Get monitoring code snippet; 2. Select monitoring template; 3. Run code snippet; A template must already be applied to your selected document to populate monitoring test results in the }.; What's next +- `/guide/monitoring/ongoing-monitoring.html` + - Sections: Monitoring scenarios; Ongoing monitoring plan; Key concepts; Design and implementation; Testing; Manage ongoing monitoring; Code samples; } Available tests +- `/guide/monitoring/review-monitoring-results.html` + - Sections: Prerequisites; Steps; Example monitoring test results; [} Satisfactory]; [} Requires Attention] +- `/guide/monitoring/set-thresholds-and-alerts.html` + - Sections: Prerequisites; Use a custom function; Set the `passed` parameter; Output examples; Alert notifications +- `/guide/monitoring/work-with-metrics-over-time.html` + - Sections: **Log metrics over time }**; Prerequisites; Add metrics over time; Add integration metrics; Use the global time range; View metric over time metadata + +- *No direct help link in frontend; related docs inferred from keywords.* + +#### `/artifacts` — Artifacts + +**Docs (related):** + +- `/guide/templates/customize-document-checker.html` + - Sections: Prerequisites; Manage regulations and policies; Manage assessments; Default assessments provided by } cannot be edited, only cloned.; Add or clone assessments; Add or edit assessment questions; Add assessment questions; Edit assessment questions +- `/guide/templates/customize-document-templates.html` + - Sections: Prerequisites; Edit template outlines; Configure assessment options[^4]; Edit YAML templates; Template schema; Troubleshooting YAML templates; Add text blocks to templates; Add text blocks via template outlines +- `/guide/templates/manage-document-templates.html` + - Sections: Prerequisites; View document templates; Edit document template outlines; Swap document templates; View currently applied templates; Swap between templates; Duplicate document templates; Delete document templates +- `/guide/templates/manage-document-types.html` + - Sections: Prerequisites; Add document types; Edit or delete document types; Development, Validation, and Monitoring document types are stock types and cannot be deleted. +- `/guide/templates/manage-documents.html` + - Sections: Prerequisites; Add record documents; How do I get the best results when converting PDFs into editable documents?; How can I trust that the conversion is accurate?; Troubleshooting; My PDF conversion is stuck. What can I do?; Edit record documents; Delete record documents +- `/guide/templates/manage-text-block-library.html` + - Sections: Prerequisites; Add text blocks; Add existing text blocks to library; Duplicate text blocks; Edit text blocks; Delete text blocks + +- *No direct help link in frontend; related docs inferred from keywords.* + +#### `/attestations` — Attestations + +**Docs (related):** + +- `/guide/attestation/approve-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/manage-attestations.html` + - Sections: Prerequisites; Add attestation templates; Test attestation schedules; Edit attestation periods; Cancel attestation periods; View attestations dashboard; Progress; Responses +- `/guide/attestation/review-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/submit-attestations.html` + - Sections: Prerequisites; Steps +- `/guide/attestation/working-with-attestations.html` + - Sections: Prerequisites; Key concepts; Where do I access attestations?; How does the attestation process work?; How are attestation submissions organized?; How do I create meaningful attestation questionnaires?; Manage attestations + +- *No direct help link in frontend; related docs inferred from keywords.* + +#### `/dashboard` — Dashboard + +**Docs (related):** + +- `/guide/configuration/customize-your-dashboard.html` + - Sections: Prerequisites; Manage dashboards; Add dashboards; Edit or remove dashboards; Manage widgets; Arrange widgets; Add widgets; Remove widgets + +- *No direct help link in frontend; related docs inferred from keywords.* + +#### `/validation-issues` — Validation Issues + +- *No direct help link; content may be covered under scattered guide sections.* + +#### `/workflows` — Workflows + +**Docs (related):** + +- `/faq/faq-workflows.html` + - Sections: Can I customize workflows within }?; What statuses are available for use in workflows?; Can we work with disconnected workflows?; You can also leverage the } once you are ready to document a specific model for review and validation.; Learn more +- `/guide/integrations/integrations-examples/use-webhooks-with-workflows.html` + - Sections: Prerequisites; Start a workflow via webhook; a. Configure workflow in }; b. Start workflow from external system; Trigger a paused workflow to continue; a. Configure workflow in }; b. Trigger workflow to continue from external system +- `/guide/workflows/conditional-step-requirements.html` + - Sections: Prerequisites; Configure conditional requirements +- `/guide/workflows/configure-workflows.html` + - Sections: Prerequisites; Create custom workflows; 1. Add new workflows; 2. Configure workflow steps; 3. Link workflow together; Workflow steps relationship unclear on your canvas?; 4. Publish workflow; Clone existing workflows +- `/guide/workflows/introduction-to-workflows.html` + - Sections: Workflow elements; What's next +- `/guide/workflows/manage-record-stages.html` + - Sections: Prerequisites; Add record stages; Edit or delete record stages + +- *No direct help link in frontend; related docs inferred from keywords.* + +## Documentation index (human-oriented) + +See `AGENTS.md` and `about/contributing/using-the-documentation.md` in the LLM corpus for guides organized by feature area (Configuration, Workflows, Inventory, etc.). diff --git a/site/llm/render.sh b/site/llm/render.sh index 78f690302f..e4ef0c4240 100755 --- a/site/llm/render.sh +++ b/site/llm/render.sh @@ -30,7 +30,8 @@ project: - "**/*.qmd" - "!notebooks/" - "!404.qmd" - - "!about/contributing/" + - "!about/contributing/validmind-community.qmd" + - "!about/contributing/style-guide/" - "!about/deployment/" - "!about/fine-print/" - "!llm/" @@ -51,6 +52,20 @@ EOF echo "=== Rendering site to GFM markdown ===" quarto render --to gfm +# AGENTS.md lives at the repo root so IDE/agent tooling finds it there, but it +# must also reach the LLM output so the docs chatbot can ingest it. +echo "" +echo "=== Generating chatbot product map ===" +python3 scripts/generate_chatbot_product_map.py + +echo "" +echo "=== Copying AGENTS.md from repo root into LLM output ===" +cp ../AGENTS.md llm/_llm-output/AGENTS.md + +echo "" +echo "=== Copying chatbot product map into LLM output ===" +cp llm/chatbot-product-map.md llm/_llm-output/chatbot-product-map.md + echo "" echo "=== Post-processing markdown files ===" bash llm/clean.sh diff --git a/site/scripts/__pycache__/generate_chatbot_product_map.cpython-314.pyc b/site/scripts/__pycache__/generate_chatbot_product_map.cpython-314.pyc new file mode 100644 index 0000000000..b898ef5f9d Binary files /dev/null and b/site/scripts/__pycache__/generate_chatbot_product_map.cpython-314.pyc differ diff --git a/site/scripts/__pycache__/test_generate_chatbot_product_map.cpython-314.pyc b/site/scripts/__pycache__/test_generate_chatbot_product_map.cpython-314.pyc new file mode 100644 index 0000000000..80669ed834 Binary files /dev/null and b/site/scripts/__pycache__/test_generate_chatbot_product_map.cpython-314.pyc differ diff --git a/site/scripts/generate_chatbot_product_map.py b/site/scripts/generate_chatbot_product_map.py new file mode 100644 index 0000000000..5df8f2c5e9 --- /dev/null +++ b/site/scripts/generate_chatbot_product_map.py @@ -0,0 +1,679 @@ +#!/usr/bin/env python3 +# Copyright © 2023-2026 ValidMind Inc. All rights reserved. +# Refer to the LICENSE file in the root of this repository for details. +# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial +""" +Generate a product-to-documentation map for the in-app chatbot (LanceDB / RAG). + +Correlates frontend routes and help links with documentation pages and headings. + +Usage (from documentation repo root): + # CI / default: build map from committed frontend snapshot (no frontend checkout) + python site/scripts/generate_chatbot_product_map.py + + # Refresh snapshot + map when frontend sources change (local frontend checkout) + python site/scripts/generate_chatbot_product_map.py --from-frontend + python site/scripts/generate_chatbot_product_map.py --from-frontend --frontend-root ../frontend +""" + +from __future__ import annotations + +import argparse +import json +import re +from dataclasses import dataclass, field +from datetime import datetime, timezone +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None # type: ignore + + +DOCS_URL_PATTERN = re.compile( + r"(?:VALIDMIND_DOCS_URL|docs\.validmind\.ai)" + r'[^`"\']*?' + r'(/(?:guide|get-started|developer|faq|support|training|about|reference)[^`"\')\s#]+)' + r"(?:#([a-zA-Z0-9_-]+))?" +) + +HELP_LINK_PATTERN = re.compile( + r"helpLink=\{?`(?:\$\{CONFIG\.VALIDMIND_DOCS_URL\}|https://docs\.validmind\.ai)" + r'(/[^`"\')\s#]+)(?:#([a-zA-Z0-9_-]+))?`?\}?' +) + +DOCUMENTATION_LINK_PATTERN = re.compile( + r'documentationLink=\{?`(?:\$\{CONFIG\.VALIDMIND_DOCS_URL\}|https://docs\.validmind\.ai)' + r'(/[^`"\')\s#]+)(?:#([a-zA-Z0-9_-]+))?`?\}?' +) + +LINK_PROP_PATTERN = re.compile( + r'link=\{?`(?:\$\{CONFIG\.VALIDMIND_DOCS_URL\}|https://docs\.validmind\.ai)' + r'(/[^`"\')\s#]+)(?:#([a-zA-Z0-9_-]+))?`?\}?' +) + +SETTING_GROUP_TITLE_PATTERN = re.compile( + r']*\btitle="([^"]+)"', +) + +SETTING_LINK_PATTERN = re.compile( + r']*\btitle="([^"]+)"[^>]*\bpath="([^"]+)"', +) + +ATTR_PATTERN = re.compile(r'(\w+)=["{`]([^"`}]+)["`}]') + +HEADING_PATTERN = re.compile(r"^(#{2,3})\s+(.+)$", re.MULTILINE) + +SIDEBAR_PATH_PATTERN = re.compile( + r"(?:path|documentationLink):\s*['\"](/[^'\"]+)['\"]" +) +SIDEBAR_LABEL_PATTERN = re.compile( + r"label:\s*(?:copy\([^)]+\)|['\"]([^'\"]+)['\"])" +) + +# Map settings link titles (lowercase) to likely guide doc path segments for related docs. +# Frontend help URLs that do not match published doc paths. +DOC_PATH_ALIASES: dict[str, str] = { + "/guide/model-workflows/setting-up-model-workflows.html": ( + "/guide/workflows/setting-up-workflows.html" + ), +} + +RELATED_DOC_PREFIXES = ( + "/guide/", + "/get-started/", + "/support/", + "/faq/faq-", + "/about/contributing/", +) + +RELATED_DOC_KEYWORDS: dict[str, list[str]] = { + "workflows": ["workflows", "model-workflows"], + "workflow": ["workflows", "model-workflows"], + "roles": ["configuration/manage-roles", "configuration/managing-users"], + "permissions": ["configuration/manage-permissions", "configuration/managing-users"], + "groups": ["configuration/manage-groups", "configuration/managing-users"], + "users": ["configuration/managing-users", "configuration/manage-users"], + "invitation": ["configuration/managing-users", "configuration/manage-users"], + "integrations": ["integrations", "configuration"], + "templates": ["templates", "model-documentation"], + "document": ["templates", "model-documentation"], + "inventory": ["inventory", "model-inventory"], + "finding": ["model-validation", "findings"], + "artifact": ["model-validation", "templates"], + "attestation": ["attestation"], + "regulation": ["templates/customize-virtual-document-validator", "model-validation"], + "risk": ["model-validation/manage-validation-guidelines"], + "authentication": ["configuration/managing-your-organization"], + "organization": ["configuration/managing-your-organization"], + "profile": ["configuration/manage-your-profile", "configuration/personalizing-validmind"], + "analytics": ["reporting", "monitoring"], + "dashboard": ["configuration/customize-your-dashboard"], +} + + +@dataclass +class DocRef: + path: str # URL path like /guide/foo/bar.html + anchor: str | None = None + + @property + def key(self) -> str: + return f"{self.path}#{self.anchor}" if self.anchor else self.path + + +@dataclass +class ProductRoute: + path: str + label: str + group: str | None = None + primary_docs: list[DocRef] = field(default_factory=list) + related_docs: list[DocRef] = field(default_factory=list) + notes: list[str] = field(default_factory=list) + + +def find_repo_root() -> Path: + current = Path(__file__).resolve() + for parent in current.parents: + if (parent / ".git").is_dir(): + return parent + return current.parent.parent.parent + + +def html_path_to_qmd(site_dir: Path, doc_path: str) -> Path | None: + """Map /guide/foo/bar.html -> site/guide/foo/bar.qmd""" + path = doc_path.strip() + if not path.startswith("/"): + path = "/" + path + if path.endswith(".html"): + path = path[:-5] + rel = path.lstrip("/") + ".qmd" + candidate = site_dir / rel + return candidate if candidate.is_file() else None + + +def extract_headings(qmd_path: Path, max_level: int = 3) -> list[str]: + text = qmd_path.read_text(encoding="utf-8") + headings: list[str] = [] + for match in HEADING_PATTERN.finditer(text): + level = len(match.group(1)) + if level > max_level: + continue + title = match.group(2).strip() + title = re.sub(r"\{[^}]*\}", "", title) + title = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", title) + title = re.sub(r"\s+", " ", title).strip() + if title: + headings.append(title) + return headings[:12] + + +def parse_doc_refs_from_text(text: str) -> list[DocRef]: + refs: list[DocRef] = [] + seen: set[str] = set() + + def add(path: str, anchor: str | None) -> None: + if not path.endswith(".html"): + path = path.rstrip("/") + ".html" + path = resolve_doc_path(path) + ref = DocRef(path=path, anchor=anchor) + if ref.key not in seen: + seen.add(ref.key) + refs.append(ref) + + for pattern in (HELP_LINK_PATTERN, DOCUMENTATION_LINK_PATTERN, LINK_PROP_PATTERN): + for m in pattern.finditer(text): + add(m.group(1), m.group(2)) + + for m in DOCS_URL_PATTERN.finditer(text): + add(m.group(1), m.group(2)) + + return refs + + +def resolve_doc_path(path: str) -> str: + return DOC_PATH_ALIASES.get(path, path) + + +def parse_settings_index(frontend_root: Path) -> list[ProductRoute]: + settings_file = frontend_root / "src/pages/Settings/index.tsx" + if not settings_file.is_file(): + return [] + + content = settings_file.read_text(encoding="utf-8") + routes: list[ProductRoute] = [] + seen_paths: set[str] = set() + + group_positions = [ + (m.start(), m.group(1)) + for m in SETTING_GROUP_TITLE_PATTERN.finditer(content) + ] + + def group_for_position(pos: int) -> str | None: + title = None + for gpos, gtitle in group_positions: + if gpos <= pos: + title = gtitle + else: + break + return title + + for link_match in SETTING_LINK_PATTERN.finditer(content): + title = link_match.group(1).strip() + path = link_match.group(2).strip() + if not path.startswith("/settings") or path in seen_paths: + continue + seen_paths.add(path) + pos = link_match.start() + # Group-level helpLink appears before SettingLinks in the same group. + window_start = max(0, pos - 1200) + window = content[window_start:pos] + group_help = parse_doc_refs_from_text(window) + route = ProductRoute( + path=path, + label=title, + group=group_for_position(pos), + ) + if group_help: + route.primary_docs.extend(group_help) + routes.append(route) + + return routes + + +def file_to_route_hint(file_path: Path) -> str | None: + """Infer product route from frontend page index files only.""" + if file_path.name != "index.tsx": + return None + parts = file_path.parts + if "pages" not in parts or "components" in parts: + return None + idx = parts.index("pages") + rest = parts[idx + 1 :] + if not rest: + return None + if rest[0] == "Settings": + if len(rest) == 1 or (len(rest) == 2 and rest[1] == "index.tsx"): + return "/settings" + # Settings/Workflows/index.tsx -> /settings/workflows + slug = rest[1].replace("_", "-") + # CamelCase to kebab + slug = re.sub(r"(? 1: + return None + if len(rest) > 1: + return None + return f"/{kebab}" + + +def scan_frontend_doc_links(frontend_root: Path) -> dict[str, list[DocRef]]: + """Map approximate product route -> doc refs from source files.""" + by_route: dict[str, list[DocRef]] = {} + src = frontend_root / "src" + if not src.is_dir(): + return by_route + + for path in list(src.rglob("*.tsx")) + list(src.rglob("*.ts")): + if "node_modules" in path.parts or ".test." in path.name: + continue + if path.name != "index.tsx" and "Settings" not in path.parts: + continue + text = path.read_text(encoding="utf-8", errors="ignore") + refs = parse_doc_refs_from_text(text) + if not refs: + continue + route_hint = file_to_route_hint(path) + if route_hint: + existing = by_route.setdefault(route_hint, []) + seen = {r.key for r in existing} + for ref in refs: + if ref.key not in seen: + seen.add(ref.key) + existing.append(ref) + return by_route + + +def parse_sidebar_nav(frontend_root: Path) -> list[ProductRoute]: + sidebar_file = frontend_root / "src/components/Sidebar/index.tsx" + if not sidebar_file.is_file(): + return [] + content = sidebar_file.read_text(encoding="utf-8") + routes: list[ProductRoute] = [] + # Match menu item objects with path and label + blocks = re.split(r"\{\s*key:\s*['\"]", content) + for block in blocks[1:]: + path_m = re.search(r"path:\s*['\"]([^'\"]+)['\"]", block) + if not path_m: + continue + path = path_m.group(1) + if not path or path == "": + continue + label_m = re.search(r"label:\s*(?:copy\(['\"]([^'\"]+)['\"]\)|['\"]([^'\"]+)['\"])", block) + label = (label_m.group(1) or label_m.group(2) or path) if label_m else path + doc_m = DOCUMENTATION_LINK_PATTERN.search(block) + route = ProductRoute(path=path, label=label, group="Main navigation") + if doc_m: + route.primary_docs.append(DocRef(path=doc_m.group(1), anchor=doc_m.group(2))) + routes.append(route) + return routes + + +def collect_all_doc_qmd_paths(site_dir: Path) -> list[str]: + """Return URL-style paths for all guide-related qmd files.""" + paths: list[str] = [] + for qmd in site_dir.rglob("*.qmd"): + rel = qmd.relative_to(site_dir).as_posix() + if rel.startswith(("internal/", "tests/", "notebooks/", "llm/")): + continue + url = "/" + rel[:-4] + ".html" + paths.append(url) + return sorted(paths) + + +def is_user_facing_doc(path: str) -> bool: + if "/_source/" in path: + return False + if any(part.startswith("_") for part in path.split("/") if part): + return False + return path.startswith(RELATED_DOC_PREFIXES) + + +def suggest_related_docs(route: ProductRoute, all_doc_paths: list[str]) -> list[DocRef]: + """Suggest related documentation based on route/title keywords.""" + haystack = f"{route.path} {route.label} {route.group or ''}".lower() + segments: set[str] = set() + for keyword, doc_segments in RELATED_DOC_KEYWORDS.items(): + if keyword in haystack: + segments.update(doc_segments) + + primary_paths = {d.path for d in route.primary_docs} + related: list[DocRef] = [] + for doc_path in all_doc_paths: + if doc_path in primary_paths or not is_user_facing_doc(doc_path): + continue + inner = doc_path.lower() + if any(seg in inner for seg in segments): + related.append(DocRef(path=doc_path)) + related.sort(key=lambda r: r.path) + return related[:6] + + +DEFAULT_SNAPSHOT_NAME = "chatbot-product-map-frontend-snapshot.json" + + +def doc_ref_to_dict(ref: DocRef) -> dict[str, str | None]: + return {"path": ref.path, "anchor": ref.anchor} + + +def doc_ref_from_dict(data: dict[str, str | None]) -> DocRef: + return DocRef(path=data["path"], anchor=data.get("anchor")) + + +def route_to_dict(route: ProductRoute) -> dict: + return { + "path": route.path, + "label": route.label, + "group": route.group, + "primary_docs": [doc_ref_to_dict(d) for d in route.primary_docs], + } + + +def route_from_dict(data: dict) -> ProductRoute: + return ProductRoute( + path=data["path"], + label=data["label"], + group=data.get("group"), + primary_docs=[doc_ref_from_dict(d) for d in data.get("primary_docs", [])], + ) + + +def extract_frontend_snapshot(frontend_root: Path) -> dict: + """Extract route/help-link data from frontend for vendoring in the docs repo.""" + settings = parse_settings_index(frontend_root) + nav = parse_sidebar_nav(frontend_root) + file_links = scan_frontend_doc_links(frontend_root) + return { + "version": 1, + "generated_at": datetime.now(timezone.utc).isoformat(), + "settings": [route_to_dict(r) for r in settings], + "nav": [route_to_dict(r) for r in nav], + "file_links": { + path: [doc_ref_to_dict(d) for d in refs] + for path, refs in sorted(file_links.items()) + }, + } + + +def load_frontend_snapshot(snapshot_path: Path) -> tuple[list[ProductRoute], list[ProductRoute], dict[str, list[DocRef]]]: + data = json.loads(snapshot_path.read_text(encoding="utf-8")) + settings = [route_from_dict(r) for r in data.get("settings", [])] + nav = [route_from_dict(r) for r in data.get("nav", [])] + file_links = { + path: [doc_ref_from_dict(d) for d in refs] + for path, refs in data.get("file_links", {}).items() + } + return settings, nav, file_links + + +def write_frontend_snapshot(snapshot_path: Path, payload: dict) -> None: + snapshot_path.parent.mkdir(parents=True, exist_ok=True) + snapshot_path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") + + +def resolve_frontend_root(repo_root: Path, explicit: Path | None) -> Path: + if explicit is not None: + candidate = explicit.resolve() + if candidate.is_dir(): + return candidate + raise SystemExit(f"Frontend root not found: {candidate}") + + for candidate in (repo_root / "frontend", repo_root.parent / "frontend"): + if candidate.is_dir(): + return candidate.resolve() + raise SystemExit( + "Frontend root not found. Use --from-frontend with a local checkout, or rely on " + f"the committed snapshot at site/llm/{DEFAULT_SNAPSHOT_NAME}." + ) + + +def merge_routes( + settings: list[ProductRoute], + nav: list[ProductRoute], + file_links: dict[str, list[DocRef]], +) -> dict[str, ProductRoute]: + by_path: dict[str, ProductRoute] = {} + + def get_or_add(route: ProductRoute) -> ProductRoute: + if route.path not in by_path: + by_path[route.path] = route + else: + existing = by_path[route.path] + if route.label and existing.label == route.path: + existing.label = route.label + if route.group and not existing.group: + existing.group = route.group + return by_path[route.path] + + settings_paths: set[str] = set() + for route in settings: + merged = get_or_add(route) + settings_paths.add(route.path) + for route in nav: + get_or_add(route) + + for path, refs in file_links.items(): + if path in settings_paths: + route = by_path[path] + seen = {d.key for d in route.primary_docs} + for ref in refs: + if ref.key not in seen: + seen.add(ref.key) + route.primary_docs.append(ref) + continue + if path not in by_path: + by_path[path] = ProductRoute(path=path, label=path, group="Main navigation") + route = by_path[path] + seen = {d.key for d in route.primary_docs} + for ref in refs: + if ref.key not in seen: + seen.add(ref.key) + route.primary_docs.append(ref) + + return by_path + + +def format_doc_line( + ref: DocRef, site_dir: Path, llm_output_dir: Path | None +) -> str: + qmd = html_path_to_qmd(site_dir, ref.path) + anchor_suffix = f" (section: #{ref.anchor})" if ref.anchor else "" + line = f"- `{ref.path}`{anchor_suffix}" + if qmd: + headings = extract_headings(qmd) + if headings: + line += f"\n - Sections: {'; '.join(headings[:8])}" + if llm_output_dir: + rel_md = qmd.relative_to(site_dir).with_suffix(".md").as_posix() + md_file = llm_output_dir / rel_md + if not md_file.is_file(): + line += "\n - Note: not yet in `_llm-output` (run `make render-llm`)" + else: + line += "\n - Note: no matching `.qmd` source found" + return line + + +def render_markdown( + routes: dict[str, ProductRoute], + site_dir: Path, + all_doc_paths: list[str], + llm_output_dir: Path | None, +) -> str: + lines = [ + "# ValidMind product-to-documentation map", + "", + "> Auto-generated. Maps in-product routes to documentation URLs and key sections.", + "> For how documentation is organized by topic, see `AGENTS.md` and", + "> [Using the documentation](/about/contributing/using-the-documentation.html).", + "", + ] + + settings_routes = sorted( + (r for r in routes.values() if r.path.startswith("/settings")), + key=lambda r: r.path, + ) + other_routes = sorted( + (r for r in routes.values() if not r.path.startswith("/settings")), + key=lambda r: r.path, + ) + + def render_section(title: str, section_routes: list[ProductRoute]) -> None: + if not section_routes: + return + lines.append(f"## {title}") + lines.append("") + current_group: str | None = None + for route in section_routes: + if title == "Settings" and route.group and route.group != current_group: + current_group = route.group + lines.append(f"### {current_group}") + lines.append("") + lines.append(f"#### `{route.path}` — {route.label}") + lines.append("") + if not route.primary_docs: + related = suggest_related_docs(route, all_doc_paths) + if related: + route.related_docs = related + route.notes.append( + "No direct help link in frontend; related docs inferred from keywords." + ) + else: + route.notes.append( + "No direct help link; content may be covered under scattered guide sections." + ) + if route.primary_docs: + lines.append("**Docs (primary):**") + lines.append("") + for ref in route.primary_docs: + lines.append(format_doc_line(ref, site_dir, llm_output_dir)) + lines.append("") + related = route.related_docs or suggest_related_docs(route, all_doc_paths) + # Exclude primary from related + primary_keys = {d.path for d in route.primary_docs} + related = [r for r in related if r.path not in primary_keys] + if related: + lines.append("**Docs (related):**") + lines.append("") + for ref in related[:6]: + lines.append(format_doc_line(ref, site_dir, llm_output_dir)) + lines.append("") + for note in route.notes: + lines.append(f"- *{note}*") + if route.notes: + lines.append("") + + render_section("Settings", settings_routes) + render_section("Main application", other_routes) + + lines.append("## Documentation index (human-oriented)") + lines.append("") + lines.append( + "See `AGENTS.md` and `about/contributing/using-the-documentation.md` in the " + "LLM corpus for guides organized by feature area (Configuration, Workflows, " + "Inventory, etc.)." + ) + lines.append("") + return "\n".join(lines) + + +def main() -> int: + parser = argparse.ArgumentParser(description="Generate chatbot product-to-docs map") + parser.add_argument( + "--from-frontend", + action="store_true", + help="Re-extract route/help-link data from frontend and update the committed snapshot", + ) + parser.add_argument( + "--frontend-root", + type=Path, + default=None, + help="Path to validmind/frontend (only with --from-frontend)", + ) + parser.add_argument( + "--snapshot", + type=Path, + default=None, + help=f"Frontend snapshot JSON (default: site/llm/{DEFAULT_SNAPSHOT_NAME})", + ) + parser.add_argument( + "--site-dir", + type=Path, + default=None, + help="Path to documentation site/ (default: /site)", + ) + parser.add_argument( + "--output", + type=Path, + default=None, + help="Output markdown path (default: site/llm/chatbot-product-map.md)", + ) + parser.add_argument( + "--json-output", + type=Path, + default=None, + help="Optional JSON output of merged routes (debug/tooling)", + ) + args = parser.parse_args() + + repo_root = find_repo_root() + site_dir = (args.site_dir or repo_root / "site").resolve() + snapshot_path = ( + args.snapshot or site_dir / "llm" / DEFAULT_SNAPSHOT_NAME + ).resolve() + output_path = (args.output or site_dir / "llm/chatbot-product-map.md").resolve() + llm_output = site_dir / "llm/_llm-output" + + if args.from_frontend: + frontend_root = resolve_frontend_root(repo_root, args.frontend_root) + payload = extract_frontend_snapshot(frontend_root) + write_frontend_snapshot(snapshot_path, payload) + print(f"Wrote {snapshot_path}") + settings, nav, file_links = load_frontend_snapshot(snapshot_path) + elif snapshot_path.is_file(): + settings, nav, file_links = load_frontend_snapshot(snapshot_path) + else: + raise SystemExit( + f"Frontend snapshot not found: {snapshot_path}\n" + "Run with --from-frontend and a local validmind/frontend checkout to create it." + ) + + routes = merge_routes(settings, nav, file_links) + all_doc_paths = collect_all_doc_qmd_paths(site_dir) + + md = render_markdown(routes, site_dir, all_doc_paths, llm_output if llm_output.is_dir() else None) + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(md, encoding="utf-8") + print(f"Wrote {output_path} ({len(routes)} routes)") + + if args.json_output: + payload = { + path: { + "label": r.label, + "group": r.group, + "primary_docs": [doc_ref_to_dict(d) for d in r.primary_docs], + "related_docs": [doc_ref_to_dict(d) for d in r.related_docs], + "notes": r.notes, + } + for path, r in sorted(routes.items()) + } + args.json_output.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") + print(f"Wrote {args.json_output}") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/site/scripts/test_generate_chatbot_product_map.py b/site/scripts/test_generate_chatbot_product_map.py new file mode 100644 index 0000000000..60e48dfb07 --- /dev/null +++ b/site/scripts/test_generate_chatbot_product_map.py @@ -0,0 +1,110 @@ +# Copyright © 2023-2026 ValidMind Inc. All rights reserved. +# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial + +"""Unit tests for generate_chatbot_product_map.py""" + +import unittest +from pathlib import Path + +import generate_chatbot_product_map as gen + + +class TestGenerateChatbotProductMap(unittest.TestCase): + def test_resolve_doc_path_alias(self) -> None: + self.assertEqual( + gen.resolve_doc_path( + "/guide/model-workflows/setting-up-model-workflows.html" + ), + "/guide/workflows/setting-up-workflows.html", + ) + + def test_parse_doc_refs_from_help_link(self) -> None: + text = ( + "helpLink={`${CONFIG.VALIDMIND_DOCS_URL}" + "/guide/configuration/managing-users.html`}" + ) + refs = gen.parse_doc_refs_from_text(text) + self.assertEqual(len(refs), 1) + self.assertEqual(refs[0].path, "/guide/configuration/managing-users.html") + + def test_html_path_to_qmd(self) -> None: + site = Path(__file__).resolve().parents[1] + qmd = gen.html_path_to_qmd(site, "/guide/workflows/setting-up-workflows.html") + self.assertIsNotNone(qmd) + self.assertEqual(qmd.name, "setting-up-workflows.qmd") + + def test_extract_headings(self) -> None: + qmd = ( + Path(__file__).resolve().parents[1] + / "guide/workflows/setting-up-workflows.qmd" + ) + headings = gen.extract_headings(qmd) + self.assertTrue(any("workflows" in h.lower() for h in headings)) + + def test_is_user_facing_doc(self) -> None: + self.assertTrue(gen.is_user_facing_doc("/guide/workflows/manage-workflow-tasks.html")) + self.assertFalse(gen.is_user_facing_doc("/_source/release-notes/foo.html")) + self.assertFalse(gen.is_user_facing_doc("/guide/workflows/_partial.html")) + + def test_collect_all_doc_qmd_paths_sorted(self) -> None: + site = Path(__file__).resolve().parents[1] + paths = gen.collect_all_doc_qmd_paths(site) + self.assertEqual(paths, sorted(paths)) + + def test_suggest_related_docs_sorted_and_stable(self) -> None: + site = Path(__file__).resolve().parents[1] + all_paths = gen.collect_all_doc_qmd_paths(site) + route = gen.ProductRoute( + path="/settings/templates", + label="Templates", + group="Configuration", + ) + first = gen.suggest_related_docs(route, all_paths) + second = gen.suggest_related_docs(route, all_paths) + self.assertEqual(first, second) + self.assertEqual([r.path for r in first], sorted(r.path for r in first)) + + def test_file_to_route_hint_settings_index(self) -> None: + self.assertEqual( + gen.file_to_route_hint(Path("src/pages/Settings/index.tsx")), + "/settings", + ) + self.assertEqual( + gen.file_to_route_hint(Path("src/pages/Settings/Workflows/index.tsx")), + "/settings/workflows", + ) + + def test_frontend_snapshot_roundtrip(self) -> None: + payload = { + "version": 1, + "settings": [ + { + "path": "/settings/workflows", + "label": "Workflows", + "group": "Governance", + "primary_docs": [ + { + "path": "/guide/workflows/setting-up-workflows.html", + "anchor": None, + } + ], + } + ], + "nav": [], + "file_links": {}, + } + site = Path(__file__).resolve().parents[1] + snapshot_path = site / "llm" / ".test-snapshot.json" + try: + gen.write_frontend_snapshot(snapshot_path, payload) + settings, nav, file_links = gen.load_frontend_snapshot(snapshot_path) + self.assertEqual(len(settings), 1) + self.assertEqual(settings[0].path, "/settings/workflows") + self.assertEqual(nav, []) + self.assertEqual(file_links, {}) + finally: + snapshot_path.unlink(missing_ok=True) + + +if __name__ == "__main__": + unittest.main()