mirror of
https://github.com/hwchase17/langchain.git
synced 2025-06-22 06:39:52 +00:00
Update api references (#8646)
Update API reference documentation. This PR will pick up a number of missing classes, it also applies selective formatting based on the class / object type.
This commit is contained in:
parent
8374367de2
commit
003e1ca9a0
@ -100,6 +100,9 @@ extensions = [
|
|||||||
]
|
]
|
||||||
source_suffix = [".rst"]
|
source_suffix = [".rst"]
|
||||||
|
|
||||||
|
# some autodoc pydantic options are repeated in the actual template.
|
||||||
|
# potentially user error, but there may be bugs in the sphinx extension
|
||||||
|
# with options not being passed through correctly (from either the location in the code)
|
||||||
autodoc_pydantic_model_show_json = False
|
autodoc_pydantic_model_show_json = False
|
||||||
autodoc_pydantic_field_list_validators = False
|
autodoc_pydantic_field_list_validators = False
|
||||||
autodoc_pydantic_config_members = False
|
autodoc_pydantic_config_members = False
|
||||||
@ -112,13 +115,6 @@ autodoc_member_order = "groupwise"
|
|||||||
autoclass_content = "both"
|
autoclass_content = "both"
|
||||||
autodoc_typehints_format = "short"
|
autodoc_typehints_format = "short"
|
||||||
|
|
||||||
autodoc_default_options = {
|
|
||||||
"members": True,
|
|
||||||
"show-inheritance": True,
|
|
||||||
"inherited-members": "BaseModel",
|
|
||||||
"undoc-members": True,
|
|
||||||
"special-members": "__call__",
|
|
||||||
}
|
|
||||||
# autodoc_typehints = "description"
|
# autodoc_typehints = "description"
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ["templates"]
|
templates_path = ["templates"]
|
||||||
|
@ -1,49 +1,209 @@
|
|||||||
"""Script for auto-generating api_reference.rst"""
|
"""Script for auto-generating api_reference.rst."""
|
||||||
import glob
|
import importlib
|
||||||
import re
|
import inspect
|
||||||
|
import typing
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import TypedDict, Sequence, List, Dict, Literal, Union
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
ROOT_DIR = Path(__file__).parents[2].absolute()
|
ROOT_DIR = Path(__file__).parents[2].absolute()
|
||||||
|
HERE = Path(__file__).parent
|
||||||
|
|
||||||
PKG_DIR = ROOT_DIR / "libs" / "langchain" / "langchain"
|
PKG_DIR = ROOT_DIR / "libs" / "langchain" / "langchain"
|
||||||
EXP_DIR = ROOT_DIR / "libs" / "experimental" / "langchain_experimental"
|
EXP_DIR = ROOT_DIR / "libs" / "experimental" / "langchain_experimental"
|
||||||
WRITE_FILE = Path(__file__).parent / "api_reference.rst"
|
WRITE_FILE = HERE / "api_reference.rst"
|
||||||
EXP_WRITE_FILE = Path(__file__).parent / "experimental_api_reference.rst"
|
EXP_WRITE_FILE = HERE / "experimental_api_reference.rst"
|
||||||
|
|
||||||
|
|
||||||
def load_members(dir: Path) -> dict:
|
ClassKind = Literal["TypedDict", "Regular", "Pydantic", "enum"]
|
||||||
members: dict = {}
|
|
||||||
for py in glob.glob(str(dir) + "/**/*.py", recursive=True):
|
|
||||||
module = py[len(str(dir)) + 1 :].replace(".py", "").replace("/", ".")
|
|
||||||
top_level = module.split(".")[0]
|
|
||||||
if top_level not in members:
|
|
||||||
members[top_level] = {"classes": [], "functions": []}
|
|
||||||
with open(py, "r") as f:
|
|
||||||
for line in f.readlines():
|
|
||||||
cls = re.findall(r"^class ([^_].*)\(", line)
|
|
||||||
members[top_level]["classes"].extend([module + "." + c for c in cls])
|
|
||||||
func = re.findall(r"^def ([^_].*)\(", line)
|
|
||||||
afunc = re.findall(r"^async def ([^_].*)\(", line)
|
|
||||||
func_strings = [module + "." + f for f in func + afunc]
|
|
||||||
members[top_level]["functions"].extend(func_strings)
|
|
||||||
return members
|
|
||||||
|
|
||||||
|
|
||||||
def construct_doc(pkg: str, members: dict) -> str:
|
class ClassInfo(TypedDict):
|
||||||
|
"""Information about a class."""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""The name of the class."""
|
||||||
|
qualified_name: str
|
||||||
|
"""The fully qualified name of the class."""
|
||||||
|
kind: ClassKind
|
||||||
|
"""The kind of the class."""
|
||||||
|
is_public: bool
|
||||||
|
"""Whether the class is public or not."""
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionInfo(TypedDict):
|
||||||
|
"""Information about a function."""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""The name of the function."""
|
||||||
|
qualified_name: str
|
||||||
|
"""The fully qualified name of the function."""
|
||||||
|
is_public: bool
|
||||||
|
"""Whether the function is public or not."""
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleMembers(TypedDict):
|
||||||
|
"""A dictionary of module members."""
|
||||||
|
|
||||||
|
classes_: Sequence[ClassInfo]
|
||||||
|
functions: Sequence[FunctionInfo]
|
||||||
|
|
||||||
|
|
||||||
|
def _load_module_members(module_path: str, namespace: str) -> ModuleMembers:
|
||||||
|
"""Load all members of a module.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
module_path: Path to the module.
|
||||||
|
namespace: the namespace of the module.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: A list of loaded module objects.
|
||||||
|
"""
|
||||||
|
classes_: List[ClassInfo] = []
|
||||||
|
functions: List[FunctionInfo] = []
|
||||||
|
module = importlib.import_module(module_path)
|
||||||
|
for name, type_ in inspect.getmembers(module):
|
||||||
|
if not hasattr(type_, "__module__"):
|
||||||
|
continue
|
||||||
|
if type_.__module__ != module_path:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if inspect.isclass(type_):
|
||||||
|
if type(type_) == typing._TypedDictMeta: # type: ignore
|
||||||
|
kind: ClassKind = "TypedDict"
|
||||||
|
elif issubclass(type_, Enum):
|
||||||
|
kind = "enum"
|
||||||
|
elif issubclass(type_, BaseModel):
|
||||||
|
kind = "Pydantic"
|
||||||
|
else:
|
||||||
|
kind = "Regular"
|
||||||
|
|
||||||
|
classes_.append(
|
||||||
|
ClassInfo(
|
||||||
|
name=name,
|
||||||
|
qualified_name=f"{namespace}.{name}",
|
||||||
|
kind=kind,
|
||||||
|
is_public=not name.startswith("_"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif inspect.isfunction(type_):
|
||||||
|
functions.append(
|
||||||
|
FunctionInfo(
|
||||||
|
name=name,
|
||||||
|
qualified_name=f"{namespace}.{name}",
|
||||||
|
is_public=not name.startswith("_"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return ModuleMembers(
|
||||||
|
classes_=classes_,
|
||||||
|
functions=functions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_module_members(
|
||||||
|
module_members: Sequence[ModuleMembers],
|
||||||
|
) -> ModuleMembers:
|
||||||
|
"""Merge module members."""
|
||||||
|
classes_: List[ClassInfo] = []
|
||||||
|
functions: List[FunctionInfo] = []
|
||||||
|
for module in module_members:
|
||||||
|
classes_.extend(module["classes_"])
|
||||||
|
functions.extend(module["functions"])
|
||||||
|
|
||||||
|
return ModuleMembers(
|
||||||
|
classes_=classes_,
|
||||||
|
functions=functions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_package_modules(
|
||||||
|
package_directory: Union[str, Path]
|
||||||
|
) -> Dict[str, ModuleMembers]:
|
||||||
|
"""Recursively load modules of a package based on the file system.
|
||||||
|
|
||||||
|
Traversal based on the file system makes it easy to determine which
|
||||||
|
of the modules/packages are part of the package vs. 3rd party or built-in.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
package_directory: Path to the package directory.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: A list of loaded module objects.
|
||||||
|
"""
|
||||||
|
package_path = (
|
||||||
|
Path(package_directory)
|
||||||
|
if isinstance(package_directory, str)
|
||||||
|
else package_directory
|
||||||
|
)
|
||||||
|
modules_by_namespace = {}
|
||||||
|
|
||||||
|
package_name = package_path.name
|
||||||
|
|
||||||
|
for file_path in package_path.rglob("*.py"):
|
||||||
|
if not file_path.name.startswith("__"):
|
||||||
|
relative_module_name = file_path.relative_to(package_path)
|
||||||
|
# Get the full namespace of the module
|
||||||
|
namespace = str(relative_module_name).replace(".py", "").replace("/", ".")
|
||||||
|
# Keep only the top level namespace
|
||||||
|
top_namespace = namespace.split(".")[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
module_members = _load_module_members(
|
||||||
|
f"{package_name}.{namespace}", namespace
|
||||||
|
)
|
||||||
|
# Merge module members if the namespace already exists
|
||||||
|
if top_namespace in modules_by_namespace:
|
||||||
|
existing_module_members = modules_by_namespace[top_namespace]
|
||||||
|
_module_members = _merge_module_members(
|
||||||
|
[existing_module_members, module_members]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_module_members = module_members
|
||||||
|
|
||||||
|
modules_by_namespace[top_namespace] = _module_members
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"Error: Unable to import module '{namespace}' with error: {e}")
|
||||||
|
|
||||||
|
return modules_by_namespace
|
||||||
|
|
||||||
|
|
||||||
|
def _construct_doc(pkg: str, members_by_namespace: Dict[str, ModuleMembers]) -> str:
|
||||||
|
"""Construct the contents of the reference.rst file for the given package.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pkg: The package name
|
||||||
|
members_by_namespace: The members of the package, dict organized by top level
|
||||||
|
module contains a list of classes and functions
|
||||||
|
inside of the top level namespace.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The contents of the reference.rst file.
|
||||||
|
"""
|
||||||
full_doc = f"""\
|
full_doc = f"""\
|
||||||
=============
|
=======================
|
||||||
``{pkg}`` API Reference
|
``{pkg}`` API Reference
|
||||||
=============
|
=======================
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for module, _members in sorted(members.items(), key=lambda kv: kv[0]):
|
namespaces = sorted(members_by_namespace)
|
||||||
classes = _members["classes"]
|
|
||||||
|
for module in namespaces:
|
||||||
|
_members = members_by_namespace[module]
|
||||||
|
classes = _members["classes_"]
|
||||||
functions = _members["functions"]
|
functions = _members["functions"]
|
||||||
if not (classes or functions):
|
if not (classes or functions):
|
||||||
continue
|
continue
|
||||||
section = f":mod:`{pkg}.{module}`"
|
section = f":mod:`{pkg}.{module}`"
|
||||||
|
underline = "=" * (len(section) + 1)
|
||||||
full_doc += f"""\
|
full_doc += f"""\
|
||||||
{section}
|
{section}
|
||||||
{'=' * (len(section) + 1)}
|
{underline}
|
||||||
|
|
||||||
.. automodule:: {pkg}.{module}
|
.. automodule:: {pkg}.{module}
|
||||||
:no-members:
|
:no-members:
|
||||||
@ -52,7 +212,6 @@ def construct_doc(pkg: str, members: dict) -> str:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if classes:
|
if classes:
|
||||||
cstring = "\n ".join(sorted(classes))
|
|
||||||
full_doc += f"""\
|
full_doc += f"""\
|
||||||
Classes
|
Classes
|
||||||
--------------
|
--------------
|
||||||
@ -60,13 +219,31 @@ Classes
|
|||||||
|
|
||||||
.. autosummary::
|
.. autosummary::
|
||||||
:toctree: {module}
|
:toctree: {module}
|
||||||
:template: class.rst
|
"""
|
||||||
|
|
||||||
{cstring}
|
for class_ in classes:
|
||||||
|
if not class_['is_public']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if class_["kind"] == "TypedDict":
|
||||||
|
template = "typeddict.rst"
|
||||||
|
elif class_["kind"] == "enum":
|
||||||
|
template = "enum.rst"
|
||||||
|
elif class_["kind"] == "Pydantic":
|
||||||
|
template = "pydantic.rst"
|
||||||
|
else:
|
||||||
|
template = "class.rst"
|
||||||
|
|
||||||
|
full_doc += f"""\
|
||||||
|
:template: {template}
|
||||||
|
|
||||||
|
{class_["qualified_name"]}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if functions:
|
if functions:
|
||||||
fstring = "\n ".join(sorted(functions))
|
_functions = [f["qualified_name"] for f in functions if f["is_public"]]
|
||||||
|
fstring = "\n ".join(sorted(_functions))
|
||||||
full_doc += f"""\
|
full_doc += f"""\
|
||||||
Functions
|
Functions
|
||||||
--------------
|
--------------
|
||||||
@ -83,12 +260,15 @@ Functions
|
|||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
lc_members = load_members(PKG_DIR)
|
"""Generate the reference.rst file for each package."""
|
||||||
lc_doc = ".. _api_reference:\n\n" + construct_doc("langchain", lc_members)
|
lc_members = _load_package_modules(PKG_DIR)
|
||||||
|
lc_doc = ".. _api_reference:\n\n" + _construct_doc("langchain", lc_members)
|
||||||
with open(WRITE_FILE, "w") as f:
|
with open(WRITE_FILE, "w") as f:
|
||||||
f.write(lc_doc)
|
f.write(lc_doc)
|
||||||
exp_members = load_members(EXP_DIR)
|
exp_members = _load_package_modules(EXP_DIR)
|
||||||
exp_doc = ".. _experimental_api_reference:\n\n" + construct_doc("langchain_experimental", exp_members)
|
exp_doc = ".. _experimental_api_reference:\n\n" + _construct_doc(
|
||||||
|
"langchain_experimental", exp_members
|
||||||
|
)
|
||||||
with open(EXP_WRITE_FILE, "w") as f:
|
with open(EXP_WRITE_FILE, "w") as f:
|
||||||
f.write(exp_doc)
|
f.write(exp_doc)
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
-e libs/langchain
|
-e libs/langchain
|
||||||
|
-e libs/experimental
|
||||||
autodoc_pydantic==1.8.0
|
autodoc_pydantic==1.8.0
|
||||||
myst_parser
|
myst_parser
|
||||||
nbsphinx==0.8.9
|
nbsphinx==0.8.9
|
||||||
|
@ -5,17 +5,6 @@
|
|||||||
|
|
||||||
.. autoclass:: {{ objname }}
|
.. autoclass:: {{ objname }}
|
||||||
|
|
||||||
{% block methods %}
|
|
||||||
{% if methods %}
|
|
||||||
.. rubric:: {{ _('Methods') }}
|
|
||||||
|
|
||||||
.. autosummary::
|
|
||||||
{% for item in methods %}
|
|
||||||
~{{ name }}.{{ item }}
|
|
||||||
{%- endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block attributes %}
|
{% block attributes %}
|
||||||
{% if attributes %}
|
{% if attributes %}
|
||||||
.. rubric:: {{ _('Attributes') }}
|
.. rubric:: {{ _('Attributes') }}
|
||||||
@ -27,4 +16,21 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block methods %}
|
||||||
|
{% if methods %}
|
||||||
|
.. rubric:: {{ _('Methods') }}
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
{% for item in methods %}
|
||||||
|
~{{ name }}.{{ item }}
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
|
{% for item in methods %}
|
||||||
|
.. automethod:: {{ name }}.{{ item }}
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
.. example_links:: {{ objname }}
|
.. example_links:: {{ objname }}
|
14
docs/api_reference/templates/enum.rst
Normal file
14
docs/api_reference/templates/enum.rst
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
:mod:`{{module}}`.{{objname}}
|
||||||
|
{{ underline }}==============
|
||||||
|
|
||||||
|
.. currentmodule:: {{ module }}
|
||||||
|
|
||||||
|
.. autoclass:: {{ objname }}
|
||||||
|
|
||||||
|
{% block attributes %}
|
||||||
|
{% for item in attributes %}
|
||||||
|
.. autoattribute:: {{ item }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
.. example_links:: {{ objname }}
|
22
docs/api_reference/templates/pydantic.rst
Normal file
22
docs/api_reference/templates/pydantic.rst
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
:mod:`{{module}}`.{{objname}}
|
||||||
|
{{ underline }}==============
|
||||||
|
|
||||||
|
.. currentmodule:: {{ module }}
|
||||||
|
|
||||||
|
.. autopydantic_model:: {{ objname }}
|
||||||
|
:model-show-json: False
|
||||||
|
:model-show-config-summary: False
|
||||||
|
:model-show-validator-members: False
|
||||||
|
:model-show-field-summary: False
|
||||||
|
:field-signature-prefix: param
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:inherited-members:
|
||||||
|
:member-order: groupwise
|
||||||
|
:show-inheritance: True
|
||||||
|
:special-members: __call__
|
||||||
|
|
||||||
|
{% block attributes %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
.. example_links:: {{ objname }}
|
14
docs/api_reference/templates/typeddict.rst
Normal file
14
docs/api_reference/templates/typeddict.rst
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
:mod:`{{module}}`.{{objname}}
|
||||||
|
{{ underline }}==============
|
||||||
|
|
||||||
|
.. currentmodule:: {{ module }}
|
||||||
|
|
||||||
|
.. autoclass:: {{ objname }}
|
||||||
|
|
||||||
|
{% block attributes %}
|
||||||
|
{% for item in attributes %}
|
||||||
|
.. autoattribute:: {{ item }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
.. example_links:: {{ objname }}
|
Loading…
Reference in New Issue
Block a user