@@ -40,6 +40,10 @@ class MethodInfo:
4040 params : List [ParamInfo ] = field (default_factory = list )
4141 raises : List [Tuple [str , str ]] = field (default_factory = list )
4242 examples : List [str ] = field (default_factory = list )
43+ notes : str = "" # Notes/More Information section
44+ see_also : List [Tuple [str , str ]] = field (default_factory = list ) # (name, description)
45+ source_file : str = "" # Relative path to source file
46+ source_line : int = 0 # Line number in source
4347 is_async : bool = False
4448 is_static : bool = False
4549 is_classmethod : bool = False
@@ -55,6 +59,10 @@ class ClassInfo:
5559 class_methods : List [MethodInfo ] = field (default_factory = list )
5660 properties : List [ParamInfo ] = field (default_factory = list )
5761 examples : List [str ] = field (default_factory = list )
62+ notes : str = "" # Notes/More Information section
63+ see_also : List [Tuple [str , str ]] = field (default_factory = list ) # (name, description)
64+ source_file : str = "" # Relative path to source file
65+ source_line : int = 0 # Line number in source
5866
5967
6068@dataclass
@@ -67,6 +75,10 @@ class FunctionInfo:
6775 params : List [ParamInfo ] = field (default_factory = list )
6876 raises : List [Tuple [str , str ]] = field (default_factory = list )
6977 examples : List [str ] = field (default_factory = list )
78+ notes : str = "" # Notes/More Information section
79+ see_also : List [Tuple [str , str ]] = field (default_factory = list ) # (name, description)
80+ source_file : str = "" # Relative path to source file
81+ source_line : int = 0 # Line number in source
7082 is_async : bool = False
7183
7284
@@ -604,6 +616,77 @@ def render_related_section(name: str, max_items: int = 5, package: str = "python
604616"""
605617
606618
619+ def render_source_link (source_file : str , source_line : int , github_repo : str ) -> str :
620+ """Render a 'View Source on GitHub' link.
621+
622+ Args:
623+ source_file: Relative path to source file
624+ source_line: Line number in source
625+ github_repo: Base GitHub repo URL
626+
627+ Returns:
628+ MDX string with source link, or empty string if no source info
629+ """
630+ if not source_file or not github_repo :
631+ return ""
632+
633+ line_anchor = f"#L{ source_line } " if source_line > 0 else ""
634+ github_url = f"{ github_repo } /{ source_file } { line_anchor } "
635+
636+ return f"""
637+ ## Source
638+
639+ <Card title="View on GitHub" icon="github" href="{ github_url } ">
640+ `{ source_file } ` at line { source_line }
641+ </Card>
642+ """
643+
644+
645+ def render_notes_section (notes : str ) -> str :
646+ """Render a Notes/More Information section.
647+
648+ Args:
649+ notes: Notes text from docstring
650+
651+ Returns:
652+ MDX string with notes section, or empty string if no notes
653+ """
654+ if not notes :
655+ return ""
656+
657+ return f"""
658+ ## Notes
659+
660+ { notes }
661+ """
662+
663+
664+ def render_see_also_section (see_also : list ) -> str :
665+ """Render a See Also section with cross-references.
666+
667+ Args:
668+ see_also: List of (name, description) tuples
669+
670+ Returns:
671+ MDX string with see also section, or empty string if none
672+ """
673+ if not see_also :
674+ return ""
675+
676+ items = []
677+ for name , desc in see_also :
678+ if desc :
679+ items .append (f"- **`{ name } `**: { desc } " )
680+ else :
681+ items .append (f"- **`{ name } `**" )
682+
683+ return f"""
684+ ## See Also
685+
686+ { chr (10 ).join (items )}
687+ """
688+
689+
607690# =============================================================================
608691# CONFIGURATION
609692# =============================================================================
@@ -829,6 +912,9 @@ def parse_module(self, module_path: str) -> Optional[ModuleInfo]:
829912 source = file_path .read_text ()
830913 tree = ast .parse (source )
831914
915+ # Calculate relative source file path for GitHub links
916+ source_file_rel = str (file_path .relative_to (self .package_path .parent ))
917+
832918 module_short_name = module_path .split ("." )[- 1 ]
833919 info = ModuleInfo (
834920 name = module_path ,
@@ -840,10 +926,17 @@ def parse_module(self, module_path: str) -> Optional[ModuleInfo]:
840926 for node in tree .body :
841927 if isinstance (node , ast .ClassDef ) and not node .name .startswith ("_" ):
842928 class_info = self ._parse_class (node )
843- if class_info : info .classes .append (class_info )
929+ if class_info :
930+ class_info .source_file = source_file_rel
931+ # Also set source_file on methods
932+ for method in class_info .methods + class_info .class_methods :
933+ method .source_file = source_file_rel
934+ info .classes .append (class_info )
844935 elif isinstance (node , (ast .FunctionDef , ast .AsyncFunctionDef )) and not node .name .startswith ("_" ):
845936 func_info = self ._parse_function (node )
846- if func_info : info .functions .append (func_info )
937+ if func_info :
938+ func_info .source_file = source_file_rel
939+ info .functions .append (func_info )
847940 elif isinstance (node , ast .Assign ):
848941 for target in node .targets :
849942 if isinstance (target , ast .Name ) and target .id .isupper ():
@@ -863,7 +956,10 @@ def _parse_class(self, node: ast.ClassDef) -> Optional[ClassInfo]:
863956 name = node .name ,
864957 docstring = parsed_doc ["description" ],
865958 bases = bases ,
866- examples = parsed_doc ["examples" ]
959+ examples = parsed_doc ["examples" ],
960+ notes = parsed_doc ["notes" ],
961+ see_also = parsed_doc ["see_also" ],
962+ source_line = node .lineno ,
867963 )
868964
869965 for item in node .body :
@@ -911,6 +1007,9 @@ def _parse_method(self, node) -> Optional[MethodInfo]:
9111007 params = params ,
9121008 raises = parsed_doc ["raises" ],
9131009 examples = parsed_doc ["examples" ],
1010+ notes = parsed_doc ["notes" ],
1011+ see_also = parsed_doc ["see_also" ],
1012+ source_line = node .lineno ,
9141013 is_async = is_async ,
9151014 is_static = is_static ,
9161015 is_classmethod = is_classmethod ,
@@ -941,6 +1040,9 @@ def _parse_function(self, node) -> Optional[FunctionInfo]:
9411040 params = params ,
9421041 raises = parsed_doc ["raises" ],
9431042 examples = parsed_doc ["examples" ],
1043+ notes = parsed_doc ["notes" ],
1044+ see_also = parsed_doc ["see_also" ],
1045+ source_line = node .lineno ,
9441046 is_async = is_async ,
9451047 )
9461048 except Exception :
@@ -954,15 +1056,17 @@ def _parse_docstring(self, docstring: str) -> Dict[str, Any]:
9541056 "returns" : "" ,
9551057 "returns_type" : "" ,
9561058 "raises" : [],
957- "examples" : []
1059+ "examples" : [],
1060+ "notes" : "" ,
1061+ "see_also" : []
9581062 }
9591063
9601064 if not docstring :
9611065 return result
9621066
9631067 # More robust splitting: only match sections at the start of original lines
9641068 # This prevents picking up 'Examples:' inside an 'Args' description
965- section_pattern = r'\n\s*(Args|Parameters|Returns|Raises|Example|Examples|Usage):?\s*\n'
1069+ section_pattern = r'\n\s*(Args|Parameters|Returns|Raises|Example|Examples|Usage|Notes?|See Also|Warning|Caution ):?\s*\n'
9661070 sections = re .split (section_pattern , '\n ' + docstring )
9671071 result ["description" ] = sections [0 ].strip ()
9681072
@@ -1002,6 +1106,23 @@ def _parse_docstring(self, docstring: str) -> Dict[str, Any]:
10021106 # Strip existing triple backticks if they wrap the entire example
10031107 content = re .sub (r'^```[a-z]*\n?(.*?)\n?```$' , r'\1' , content , flags = re .DOTALL )
10041108 result ["examples" ].append (content .strip ())
1109+
1110+ elif section_name in ("note" , "notes" , "warning" , "caution" ):
1111+ result ["notes" ] = section_content .strip ()
1112+
1113+ elif section_name == "see also" :
1114+ # Parse "See Also" section - format: "name : description" or just "name"
1115+ see_also_lines = section_content .strip ().split ('\n ' )
1116+ for line in see_also_lines :
1117+ line = line .strip ()
1118+ if not line :
1119+ continue
1120+ # Match "name : description" or "name: description"
1121+ match = re .match (r'^(\w+(?:\.\w+)*)\s*:?\s*(.*)$' , line )
1122+ if match :
1123+ name = match .group (1 ).strip ()
1124+ desc = match .group (2 ).strip () if match .group (2 ) else ""
1125+ result ["see_also" ].append ((name , desc ))
10051126
10061127 return result
10071128
@@ -1513,6 +1634,22 @@ def _render_class_page(self, cls: ClassInfo, module_info: ModuleInfo) -> str:
15131634 else :
15141635 lines .append (f"```{ lang } \n { cls .examples [0 ]} \n ```\n " )
15151636
1637+ # Add Notes section if available
1638+ notes_section = render_notes_section (cls .notes )
1639+ if notes_section :
1640+ lines .append (notes_section )
1641+
1642+ # Add See Also section if available
1643+ see_also_section = render_see_also_section (cls .see_also )
1644+ if see_also_section :
1645+ lines .append (see_also_section )
1646+
1647+ # Add Source link
1648+ github_repo = self .config .get ("github_repo" , "" )
1649+ source_section = render_source_link (cls .source_file , cls .source_line , github_repo )
1650+ if source_section :
1651+ lines .append (source_section )
1652+
15161653 # Add related documentation section
15171654 related_section = render_related_section (cls .name , max_items = 5 , package = self .package_name )
15181655 if related_section :
@@ -1623,6 +1760,22 @@ def _render_function_page(self, func: FunctionInfo, module_info: ModuleInfo, cls
16231760 dedented_ex = textwrap .dedent (func .examples [0 ])
16241761 lines .append (f"```python\n { dedented_ex } \n ```\n " )
16251762
1763+ # Add Notes section if available
1764+ notes_section = render_notes_section (func .notes )
1765+ if notes_section :
1766+ lines .append (notes_section )
1767+
1768+ # Add See Also section if available
1769+ see_also_section = render_see_also_section (func .see_also )
1770+ if see_also_section :
1771+ lines .append (see_also_section )
1772+
1773+ # Add Source link
1774+ github_repo = self .config .get ("github_repo" , "" )
1775+ source_section = render_source_link (func .source_file , func .source_line , github_repo )
1776+ if source_section :
1777+ lines .append (source_section )
1778+
16261779 # Add related documentation section
16271780 related_section = render_related_section (func .name , max_items = 5 , package = self .package_name )
16281781 if related_section :
@@ -1743,6 +1896,7 @@ def __init__(
17431896 "badge_color" : "blue" ,
17441897 "badge_text" : "AI Agent" ,
17451898 "title_suffix" : " • AI Agent SDK" ,
1899+ "github_repo" : "https://github.com/MervinPraison/PraisonAI/blob/main/src/praisonai-agents" ,
17461900 },
17471901 "praisonai" : {
17481902 "source" : base_src / "src/praisonai/praisonai" ,
@@ -1751,6 +1905,7 @@ def __init__(
17511905 "badge_color" : "purple" ,
17521906 "badge_text" : "AI Agents Framework" ,
17531907 "title_suffix" : " • AI Agents Framework" ,
1908+ "github_repo" : "https://github.com/MervinPraison/PraisonAI/blob/main/src/praisonai" ,
17541909 },
17551910 "typescript" : {
17561911 "source" : base_src / "src/praisonai-ts/src" ,
@@ -1759,6 +1914,7 @@ def __init__(
17591914 "badge_color" : "green" ,
17601915 "badge_text" : "TypeScript AI Agent" ,
17611916 "title_suffix" : " • TypeScript AI Agent SDK" ,
1917+ "github_repo" : "https://github.com/MervinPraison/PraisonAI/blob/main/src/praisonai-ts/src" ,
17621918 },
17631919 "rust" : {
17641920 "source" : base_src / "src/praisonai-rust" ,
@@ -1768,6 +1924,7 @@ def __init__(
17681924 "badge_text" : "Rust AI Agent SDK" ,
17691925 "title_suffix" : " • Rust AI Agent SDK" ,
17701926 "language" : "rust" ,
1927+ "github_repo" : "https://github.com/ARC-Solutions/praisonai-rust/blob/main" ,
17711928 },
17721929 }
17731930
0 commit comments