Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ instance/

# Sphinx documentation
docs/_build/
docs/source/_autosummary/
agents/*/docs/source/_autosummary/

# PyBuilder
.pybuilder/
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ repos:
name: Run checks through Poe
entry: uv run poe pre-commit-check
language: system
files: "^(agents/|scripts/)"
files: "^(agents/|scripts/|docs/|README\\.md)"

- repo: https://github.com/PyCQA/bandit
rev: 1.8.5
Expand Down
Empty file.
18 changes: 18 additions & 0 deletions agents/agent1/docs/source/agent1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Agent 1
=======

Package Documentation
---------------------

.. automodule:: python_agent_template.agents.agent1
:members:
:undoc-members:
:show-inheritance:

CLI Entry Point
---------------

.. automodule:: python_agent_template.agents.agent1.__main__
:members:
:undoc-members:
:show-inheritance:
98 changes: 98 additions & 0 deletions agents/agent1/docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from __future__ import annotations

import logging
import sys
from pathlib import Path

logger = logging.getLogger(__name__)

try:
import tomllib
except ModuleNotFoundError: # Python < 3.11
try:
import tomli as tomllib # type: ignore[import-not-found]
except ModuleNotFoundError:
tomllib = None # type: ignore[assignment]

THIS_FILE = Path(__file__).resolve()


def _find_upwards(start: Path, marker: str = "pyproject.toml") -> Path:
"""Return the first parent containing ``marker``; raise if none is found."""
for parent in [start, *start.parents]:
if (parent / marker).is_file():
return parent
logger.debug("%s not found starting at %s", marker, start)
err = FileNotFoundError(marker)
if hasattr(err, "add_note"):
err.add_note(f"search start: {start}")
raise err


AGENT_ROOT = _find_upwards(THIS_FILE)
PROJECT_ROOT = _find_upwards(AGENT_ROOT.parent)
sys.path.insert(0, str(PROJECT_ROOT))
for src_path in PROJECT_ROOT.glob("agents/*/src"):
sys.path.insert(0, str(src_path))

project = "python-agent-template"
author = "python-agent-template maintainers"


def _get_project_version(default: str = "0.0.0") -> str:
"""Return the project version from this agent's pyproject.toml, or a default."""
pyproject_path = AGENT_ROOT / "pyproject.toml"
if tomllib is None or not pyproject_path.is_file():
return default

try:
with pyproject_path.open("rb") as f:
data = tomllib.load(f)
except OSError as exc:
logger.warning("Failed to read %s; falling back to default version.", pyproject_path, exc_info=exc)
return default
except tomllib.TOMLDecodeError as exc: # type: ignore[union-attr]
logger.warning("Failed to parse %s; falling back to default version.", pyproject_path, exc_info=exc)
return default

version = data.get("project", {}).get("version") or data.get("tool", {}).get("poetry", {}).get("version")
return version or default


version = _get_project_version()
release = version

extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
]

try:
if tomllib is not None:
# Only enable when the TOML parser (and therefore the extension's deps) is available.
# Import is intentionally unused; it fails fast if the dependency stack is missing.
import sphinx_autodoc_typehints # noqa: F401 # pyright: ignore[reportUnusedImport]

extensions.append("sphinx_autodoc_typehints")
except Exception:
logger.warning("sphinx_autodoc_typehints not enabled; dependency stack missing.", exc_info=True)

autosummary_generate = True
autodoc_typehints = "description"
autodoc_default_options = {
"members": True,
"undoc-members": True,
"show-inheritance": True,
}

napoleon_google_docstring = True
napoleon_numpy_docstring = False

# set to ["_templates"] when the directory is added
templates_path: list[str] = []
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

html_theme = "alabaster"
html_static_path: list[str] = [] # set to ["_static"] when the directory is added
7 changes: 7 additions & 0 deletions agents/agent1/docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Python Agent Template
=====================

.. toctree::
:maxdepth: 4

agent1
1 change: 1 addition & 0 deletions agents/agent1/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ include = "../../shared_tasks.toml"
bandit = "uv run bandit -c pyproject.toml -r src/python_agent_template/agents/agent1"
mypy = "uv run mypy --config-file $POE_ROOT/pyproject.toml src"
test = "uv run pytest --cov=python_agent_template.agents.agent1 --cov-report=term-missing:skip-covered"
docs = "uv run python ../../scripts/generate_docs.py --agents-only --agents agent1"

[build-system]
requires = ["flit-core>=3.12.0,<4"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ Run with `uv run poe <task>` from `agents/<agent>`, or `uv run -C agents/<agent>

## What lives where
- Agent-level (agents/<agent>): code, tests, Dockerfile, agent-specific tasks/config, LICENSE, README, built artifacts.
- Project-level (root): shared tasks, root `pyproject.toml`, shared scripts (`scripts/`), CI, global lint/type/test settings.
- Project-level (root): shared tasks, root pyproject, shared scripts (scripts/), CI, global lint/type/test settings.
- Docs (root): `docs/source/` Sphinx inputs build to `docs/generated` for the unified site.
- Docs (per-agent): `agents/<agent>/docs/source/` Sphinx inputs build to `agents/<agent>/docs/generated`.

### Documentation builds
- Refer to the dedicated docs build guide for commands, outputs, and CI recommendations: [docs/manual/docs-build-guide.md](docs/manual/docs-build-guide.md).

## Troubleshooting
- uv missing: install via `curl -LsSf https://astral.sh/uv/install.sh | sh`.
Expand Down
40 changes: 40 additions & 0 deletions docs/manual/docs-build-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Documentation Build Guide

How to build API docs for the template and each agent.

## What gets built
- Per-agent site: Sphinx sources in `agents/<agent>/docs/source` → HTML in `agents/<agent>/docs/generated`.
- Unified site: Sphinx sources in `docs/source` → HTML in `docs/generated`.
- Manual docs: hand-written content lives in `docs/manual`.

## Commands
- Default (build per-agent + unified):
- `uv run --group docs python scripts/generate_docs.py`
- Per-agent only:
- `uv run --group docs python scripts/generate_docs.py --agents-only [--agents agent1 agent2]`
- Unified only:
- `uv run --group docs python scripts/generate_docs.py --unified-only`
- Via poe (root):
- `uv run poe docs` (same as default)
- Optional setup: `uv run poe docs-install` to sync the docs group once

## Outputs
- Per-agent HTML: `agents/<agent>/docs/generated`
- Unified HTML: `docs/generated`

## Path overrides (rarely needed)
- `--root`: repo root override
- `--agent-source` / `--agent-output`: agent-local source/output (relative to each agent unless absolute)
- `--unified-source` / `--unified-output`: unified source/output (relative to repo unless absolute)

## Best practices
- Do not commit generated HTML; build in CI and publish artifacts or Pages instead.
- Keep `docs/source/_autosummary` out of git (already ignored); the build script clears it.
- Use `uv run poe docs` locally before releasing if you need to verify the output.

## CI recommendation
- Add a GitHub Actions job that installs the docs group (`uv sync --group docs`) and runs `uv run poe docs`.
- Fail the job on Sphinx warnings/errors; optionally upload `docs/generated` as an artifact.

## Per-agent tasks
- Agents may expose `poe docs` locally; agent1 example runs `uv run python ../../scripts/generate_docs.py --agents-only --agents agent1`.
18 changes: 18 additions & 0 deletions docs/source/agents/agent1/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Agent 1
=======

Package Documentation
---------------------

.. automodule:: python_agent_template.agents.agent1
:members:
:undoc-members:
:show-inheritance:

CLI Entry Point
---------------

.. automodule:: python_agent_template.agents.agent1.__main__
:members:
:undoc-members:
:show-inheritance:
83 changes: 83 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from __future__ import annotations

import logging
import sys
from pathlib import Path

try:
import tomllib
except ModuleNotFoundError: # Python < 3.11
try:
import tomli as tomllib # type: ignore[import-not-found]
except ModuleNotFoundError:
tomllib = None # type: ignore[assignment]

PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
for src_path in PROJECT_ROOT.glob("agents/*/src"):
sys.path.insert(0, str(src_path))

project = "python-agent-template"
author = "python-agent-template maintainers"
logger = logging.getLogger(__name__)


def _get_project_version(default: str = "0.0.0") -> str:
"""Return the project version from pyproject.toml, or a default."""

pyproject_path = PROJECT_ROOT / "pyproject.toml"
if tomllib is None or not pyproject_path.is_file():
return default

try:
with pyproject_path.open("rb") as f:
data = tomllib.load(f)
except OSError as exc:
logger.warning("Failed to read %s; falling back to default version.", pyproject_path, exc_info=exc)
return default
except tomllib.TOMLDecodeError as exc: # type: ignore[union-attr]
logger.warning("Failed to parse %s; falling back to default version.", pyproject_path, exc_info=exc)
return default

version = (
data.get("project", {}).get("version")
or data.get("tool", {}).get("poetry", {}).get("version")
)
return version or default


version = _get_project_version()
release = version

extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
]

try:
if tomllib is not None:
# Only enable when the TOML parser (and therefore the extension's deps) is available.
# Import is intentionally unused; it fails fast if the dependency stack is missing.
import sphinx_autodoc_typehints # noqa: F401 # pyright: ignore[reportMissingImports,reportUnusedImport]
extensions.append("sphinx_autodoc_typehints")
except Exception:
logger.warning("sphinx_autodoc_typehints not enabled; dependency stack missing.", exc_info=True)

autosummary_generate = True
autodoc_typehints = "description"
autodoc_default_options = {
"members": True,
"undoc-members": True,
"show-inheritance": True,
}

napoleon_google_docstring = True
napoleon_numpy_docstring = False

templates_path: list[str] = [] # set to ["_templates"] when the directory is added
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

html_theme = "alabaster"
html_static_path: list[str] = [] # set to ["_static"] when the directory is added
9 changes: 9 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Python Agent Template
================================

Welcome to the documentation for all agents in the template.

.. toctree::
:maxdepth: 3

agents/agent1/index
13 changes: 7 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ dev = [
"tomli-w>=1.0.0",
]
docs = [
"py2docfx>=0.1.22.dev2259826",
"pip",
"sphinx>=7.4,<8",
"sphinx-autodoc-typehints>=2.2.1",
"tomli>=2.0.1",
]

[tool.uv]
Expand Down Expand Up @@ -150,7 +151,7 @@ markers = []
omit = ["**/__init__.py"]

[tool.pyright]
include = ["agents", "scripts"]
include = ["agents", "scripts", "docs"]
exclude = ["**/tests/**", "**/.venv/**", "**/.uv/**"]
extraPaths = ["scripts"]
typeCheckingMode = "strict"
Expand Down Expand Up @@ -193,11 +194,11 @@ bandit = ["bandit-agents", "bandit-scripts"]
bandit-agents = "python scripts/run_tasks_in_agents_if_exists.py bandit"
bandit-scripts = "uv run bandit -c pyproject.toml -r scripts"
test = "python scripts/run_tasks_in_agents_if_exists.py test"
markdown-code-lint = "uv run python scripts/check_md_code_blocks.py README.md agents/**/README.md"
markdown-code-lint = "uv run python scripts/check_md_code_blocks.py README.md docs/manual/*.md agents/**/README.md"
pre-commit-install = "uv run pre-commit install --install-hooks --overwrite"
install = "uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit --no-group=docs"
# docs = "uv run python scripts/generate_docs.py" # disabled: experimental docs pipeline
# docs-install = "uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit --group docs"
docs = "uv run python scripts/generate_docs.py"
docs-install = "uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit --group docs"
check = ["fmt", "lint", "pyright", "mypy", "bandit", "test", "markdown-code-lint"]
# Optional release/publish helpers (commented out by default)
# clean-dist-agents = "python scripts/run_tasks_in_agents_if_exists.py clean-dist"
Expand Down
Loading
Loading