feat(backend): Mermaid diagrams for chapter index pages - #346
Closed
arozumenko wants to merge 1 commit into
Closed
Conversation
Add three new deterministic diagram methods to DiagramGenerator: - generate_class_diagram: Mermaid classDiagram with stereotypes, member visibility, and intra-cluster inheritance edges (cap 8 nodes) - generate_data_model_diagram: Mermaid erDiagram for Pydantic/SQLAlchemy/ dataclass models with composition edges (cap 8 nodes) - generate_cluster_context_diagram: graph LR with a subgraph for the cluster's own nodes plus external cluster nodes when cross-cluster info is available WikiContentWriter._inject_cluster_diagrams injects all three sections into every chapter _index.md after ## Overview, before ## Key Components. 38 new unit tests cover happy paths, edge cases (empty cluster, <2 nodes, >8 nodes cap, storage errors, abstract detection, visibility prefixes). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds deterministic Mermaid diagram generation to the backend wiki writer so chapter index pages can include auto-generated visualizations (without LLM calls) for architecture/context, class structure, and data-model relationships.
Changes:
- Added three new diagram generators to
DiagramGenerator:classDiagram,erDiagram, and a cluster contextgraph LRwithsubgraph. - Injected generated diagram sections into chapter
_index.mdduringgenerate_chapter(). - Expanded unit test coverage for the new diagram generators.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
backend/app/core/wiki_content_writer/diagram_generator.py |
Implements the new deterministic Mermaid diagram builders (class/data-model/context) and related helpers. |
backend/app/core/wiki_content_writer/writer_agent.py |
Injects the new diagram sections into chapter index markdown after index generation. |
backend/tests/unit/wiki_content_writer/test_diagram_generator.py |
Adds unit tests for the new diagram generation methods and their edge cases. |
Comment on lines
+299
to
+307
| # Collect member nodes (methods, properties, fields) whose parent_symbol | ||
| # matches one of the selected class nodes. | ||
| class_names: set[str] = set(class_name_to_id) | ||
| member_map: dict[str, list[dict]] = {_node_name(n): [] for n in class_nodes} | ||
| for n in all_nodes: | ||
| if (n.get("symbol_type") or n.get("kind", "")) in _MEMBER_TYPES: | ||
| parent = n.get("parent_symbol") | ||
| if parent and parent in class_names: | ||
| member_map[parent].append(n) |
Comment on lines
+675
to
+679
| # Inheritance edges: Child --|> Parent | ||
| for src_id, tgt_id in inheritance_edges: | ||
| src_name = _safe_id(id_to_name[src_id]) | ||
| tgt_name = _safe_id(id_to_name[tgt_id]) | ||
| lines.append(f" {src_name} --|> {tgt_name} : implements") |
Comment on lines
+436
to
+447
| # Build a node_id → external cluster_id lookup if cross-cluster info given. | ||
| node_to_ext_cluster: dict[str, int] = {} | ||
| if all_cluster_ids and cluster_titles: | ||
| for ext_cid in all_cluster_ids: | ||
| if ext_cid == cluster_id: | ||
| continue | ||
| try: | ||
| ext_nodes = self.storage.get_nodes_by_cluster(ext_cid) | ||
| except Exception: | ||
| ext_nodes = [] | ||
| for en in ext_nodes: | ||
| node_to_ext_cluster[en["node_id"]] = ext_cid |
Comment on lines
+729
to
+737
| # External cluster nodes (one node per neighbouring cluster). | ||
| seen_ext: set[int] = set() | ||
| for _src_nid, ext_cid in cross_cluster_edges: | ||
| if ext_cid not in seen_ext: | ||
| seen_ext.add(ext_cid) | ||
| ext_title = cluster_titles.get(ext_cid) or f"cluster_{ext_cid}" | ||
| ext_safe = _safe_id(ext_title[:_MAX_LABEL_CHARS]) | ||
| ext_label = _sanitize_label(ext_title[:_MAX_LABEL_CHARS]) | ||
| lines.append(f' {ext_safe}["{ext_label}"]') |
Comment on lines
+855
to
+859
| """Inject Mermaid diagrams into a chapter _index.md after ## Overview. | ||
|
|
||
| Inserts Architecture / Class Structure / Data Model sections before | ||
| ``## Key Components`` (or ``## Sub-pages`` when Key Components is | ||
| absent). Returns the original markdown unchanged on any error. |
Comment on lines
+836
to
+839
| # ── 4. Inject cluster diagrams into the chapter index ───────── | ||
| chapter_index_md = self._inject_cluster_diagrams( | ||
| chapter_index_md, chapter_spec.cluster_id, chapter_spec.chapter_title | ||
| ) |
| nodes_c2 = [_class_node("n_ext", "WikiService", "class")] | ||
|
|
||
| # Storage returns different nodes for different cluster IDs | ||
| call_count = {"count": 0} |
Owner
Author
|
Superseded by #347 which contains a corrected implementation. Closing to keep a single PR per feature. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DiagramGenerator(no LLM calls):generate_class_diagram(MermaidclassDiagram),generate_data_model_diagram(MermaiderDiagram), andgenerate_cluster_context_diagram(Mermaidgraph LRwith subgraph)WikiContentWriter._inject_cluster_diagramsinserts all three sections into every chapter_index.mdafter## Overviewand before## Key Components(or## Sub-pageswhen Key Components is absent)test_diagram_generator.pycovering happy paths, edge cases, and error handlingDetails
generate_class_diagram(cluster_id)class,interface,struct,enum,trait)<<interface>>,<<enumeration>>,<<abstract>>stereotypes+/-visibility prefixes--|>is_architectural=1nodes when over cap""when fewer than 2 class-like nodesgenerate_data_model_diagram(cluster_id)BaseModel,@dataclass, SQLAlchemydb.Model/DeclarativeBase,@Entity, GORM tags, etc.erDiagramwithcomposition/referencesrelationship edges""when fewer than 2 detectedgenerate_cluster_context_diagram(cluster_id, cluster_title, all_cluster_ids, cluster_titles)subgraphblockall_cluster_ids=[](first pass), shows intra-cluster structure onlyInjection in
generate_chapter_inject_cluster_diagramscalled after_generate_chapter_indexcompletesTest plan
TestGenerateClassDiagram,TestGenerateDataModelDiagram,TestGenerateClusterContextDiagrampython -m py_compileclean on both changed files🤖 Generated with Claude Code