Skip to content

Commit f1adcf2

Browse files
committed
docs: add Sphinx documentation site
Add complete Sphinx-based documentation with sphinx-book-theme including: - API reference with autodoc for all public classes - Conceptual guides (architecture, checkpointing, stores, TTL, etc.) - User guides (getting started, installation, middleware, migration) - Example notebook integration via copy_notebooks.py - GitHub Actions workflow for automated deployment to GitHub Pages - Makefile targets for local docs build and preview
1 parent 453241d commit f1adcf2

43 files changed

Lines changed: 3955 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docs.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Deploy Documentation
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: "pages"
15+
cancel-in-progress: false
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Set up Python
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: "3.12"
28+
29+
- name: Install Poetry
30+
uses: snok/install-poetry@v1
31+
with:
32+
version: latest
33+
virtualenvs-create: true
34+
virtualenvs-in-project: true
35+
36+
- name: Install project dependencies
37+
run: poetry install --all-extras
38+
39+
- name: Install docs dependencies
40+
run: pip install -r docs/requirements.txt
41+
42+
- name: Copy example notebooks
43+
run: python docs/copy_notebooks.py
44+
45+
- name: Build documentation
46+
run: sphinx-build -b html docs docs/_build/html
47+
48+
- name: Upload artifact
49+
uses: actions/upload-pages-artifact@v3
50+
with:
51+
path: docs/_build/html
52+
53+
deploy:
54+
environment:
55+
name: github-pages
56+
url: ${{ steps.deployment.outputs.page_url }}
57+
runs-on: ubuntu-latest
58+
needs: build
59+
steps:
60+
- name: Deploy to GitHub Pages
61+
id: deployment
62+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ instance/
107107

108108
# Sphinx documentation
109109
docs/_build/
110+
docs/examples/checkpoints/
111+
docs/examples/human_in_the_loop/
112+
docs/examples/memory/
113+
docs/examples/middleware/
114+
docs/examples/react_agent/
110115

111116
# PyBuilder
112117
.pybuilder/

Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: install format lint test test-all test-sentinel clean redis-start redis-stop check-types check
1+
.PHONY: install format lint test test-all test-sentinel clean redis-start redis-stop check-types check docs docs-clean docs-serve
22

33
install:
44
poetry install --all-extras
@@ -41,6 +41,17 @@ find-dead-code:
4141

4242
check: lint test
4343

44+
docs:
45+
python docs/copy_notebooks.py
46+
sphinx-build -b html docs docs/_build/html
47+
48+
docs-clean:
49+
rm -rf docs/_build
50+
rm -rf docs/examples/checkpoints docs/examples/human_in_the_loop docs/examples/memory docs/examples/middleware docs/examples/react_agent
51+
52+
docs-serve: docs
53+
python -m http.server 8085 --directory docs/_build/html
54+
4455
clean:
4556
find . -type d -name "__pycache__" -exec rm -rf {} +
4657
find . -type d -name ".pytest_cache" -exec rm -rf {} +

docs/Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Minimal makefile for Sphinx documentation
2+
3+
# You can set these variables from the command line, and also
4+
# from the environment for the first two.
5+
SPHINXOPTS ?=
6+
SPHINXBUILD ?= sphinx-build
7+
SOURCEDIR = .
8+
BUILDDIR = _build
9+
10+
# Put it first so that "make" without argument is like "make help".
11+
help:
12+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13+
14+
.PHONY: help Makefile
15+
16+
# Catch-all target: route all unknown targets to Sphinx using the new
17+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18+
%: Makefile
19+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
"""A directive to generate a gallery of images from structured data.
2+
3+
Generating a gallery of images that are all the same size is a common pattern in
4+
documentation, and this can be cumbersome if the gallery is generated
5+
programmatically. This directive wraps this particular use-case in a helper-
6+
directive to generate it with a single YAML configuration file.
7+
"""
8+
from pathlib import Path
9+
from typing import Any, Dict, List
10+
11+
from docutils import nodes
12+
from docutils.parsers.rst import directives
13+
from sphinx.application import Sphinx
14+
from sphinx.util import logging
15+
from sphinx.util.docutils import SphinxDirective
16+
from yaml import safe_load
17+
18+
logger = logging.getLogger(__name__)
19+
20+
21+
TEMPLATE_GRID = """
22+
`````{{grid}} {grid_columns}
23+
{container_options}
24+
25+
{content}
26+
27+
`````
28+
"""
29+
30+
GRID_CARD = """
31+
````{{grid-item-card}} {title}
32+
{card_options}
33+
34+
{content}
35+
````
36+
"""
37+
38+
39+
class GalleryDirective(SphinxDirective):
40+
"""A directive to show a gallery of images and links in a grid."""
41+
42+
name = "gallery-grid"
43+
has_content = True
44+
required_arguments = 0
45+
optional_arguments = 1
46+
final_argument_whitespace = True
47+
option_spec = {
48+
"grid-columns": directives.unchanged,
49+
"class-container": directives.unchanged,
50+
"class-card": directives.unchanged,
51+
}
52+
53+
def run(self) -> List[nodes.Node]:
54+
if self.arguments:
55+
path_data_rel = Path(self.arguments[0])
56+
path_doc, _ = self.get_source_info()
57+
path_doc = Path(path_doc).parent
58+
path_data = (path_doc / path_data_rel).resolve()
59+
if not path_data.exists():
60+
logger.warn(f"Could not find grid data at {path_data}.")
61+
nodes.text("No grid data found at {path_data}.")
62+
return
63+
yaml_string = path_data.read_text()
64+
else:
65+
yaml_string = "\n".join(self.content)
66+
67+
grid_data = safe_load(yaml_string)
68+
69+
grid_items = []
70+
for item in grid_data:
71+
options = {}
72+
if "website" in item:
73+
options["link"] = item["website"]
74+
75+
if "class-card" in self.options:
76+
options["class-card"] = self.options["class-card"]
77+
78+
if "img-background" in item:
79+
options["img-background"] = item["img-background"]
80+
81+
if "img-top" in item:
82+
options["img-top"] = item["img-top"]
83+
84+
if "img-bottom" in item:
85+
options["img-bottom"] = item["img-bottom"]
86+
87+
options_str = "\n".join(f":{k}: {v}" for k, v in options.items()) + "\n\n"
88+
89+
content_str = ""
90+
if "header" in item:
91+
content_str += f"{item['header']}\n\n^^^\n\n"
92+
93+
if "image" in item:
94+
content_str += f"![Gallery image]({item['image']})\n\n"
95+
96+
if "content" in item:
97+
content_str += f"{item['content']}\n\n"
98+
99+
if "footer" in item:
100+
content_str += f"+++\n\n{item['footer']}\n\n"
101+
102+
title = item.get("title", "")
103+
content_str += "\n"
104+
grid_items.append(
105+
GRID_CARD.format(
106+
card_options=options_str, content=content_str, title=title
107+
)
108+
)
109+
110+
container = nodes.container()
111+
container_options = {"gutter": 2, "class-container": "gallery-directive"}
112+
if "class-container" in self.options:
113+
container_options[
114+
"class-container"
115+
] += f' {self.options["class-container"]}'
116+
container_options_str = "\n".join(
117+
f":{k}: {v}" for k, v in container_options.items()
118+
)
119+
120+
grid_directive = TEMPLATE_GRID.format(
121+
grid_columns=self.options.get("grid-columns", "1 2 3 4"),
122+
container_options=container_options_str,
123+
content="\n".join(grid_items),
124+
)
125+
self.state.nested_parse([grid_directive], 0, container)
126+
container = container.children[0]
127+
128+
if self.options.get("container-class", []):
129+
container.attributes["classes"] += self.options.get("class", [])
130+
return [container]
131+
132+
133+
def setup(app: Sphinx) -> Dict[str, Any]:
134+
app.add_directive("gallery-grid", GalleryDirective)
135+
136+
return {
137+
"parallel_read_safe": True,
138+
"parallel_write_safe": True,
139+
}
1.89 KB
Loading
461 Bytes
Loading
801 Bytes
Loading
Lines changed: 66 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)