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
38 changes: 38 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Docs

on:
push:
branches: [main]

Comment thread
cretz marked this conversation as resolved.
permissions:
contents: read
pages: write
id-token: write

# Allow one concurrent deployment, cancelling in-progress runs.
concurrency:
group: pages
cancel-in-progress: true

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4

- uses: astral-sh/setup-uv@v6

- run: uv sync

- name: Generate docs
run: uv run poe generate-docs

- uses: actions/upload-pages-artifact@v3
with:
path: _site

- id: deployment
uses: actions/deploy-pages@v4
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
.pytest_cache
.ruff_cache
.venv
.doctrees
__pycache__
_site
scripts/docgen/api
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Baseten Python SDK

Python SDK for Baseten.
[![PyPI](https://img.shields.io/pypi/v/baseten.svg)](https://pypi.org/project/baseten/)

Python SDK for Baseten. See the [API documentation](https://basetenlabs.github.io/baseten-python/) and [usage](#usage) below.

⚠️ Under active development. Nothing should be considered stable at this time.

Expand Down
13 changes: 12 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ description = "Baseten Python SDK"
readme = "README.md"
license = "MIT"
requires-python = ">=3.10"
classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
dependencies = [
"httpx>=0.27",
"pydantic>=2.0",
]

[dependency-groups]
dev = [
"autodoc-pydantic>=2.2.0",
# Capped at <0.56: starting in later releases (confirmed in 0.59.1),
# complex object/scalar defaults are emitted as raw literals assigned to
# model-typed fields (e.g. `x: Model | None = {..dict..}`, `Limit | None =
Expand All @@ -20,11 +28,13 @@ dev = [
# these at runtime, but the literal does not match the annotation, so ty
# rejects the generated code. No flag restores the old emission.
"datamodel-code-generator>=0.55.0,<0.56",
"furo>=2025.12.19",
"poethepoet>=0.35",
"pytest>=9.0.2",
"pytest-asyncio>=1.3.0",
"pyyaml>=6.0",
"ruff>=0.15.16",
"sphinx>=8.1.3",
"ty>=0.0.43",
]

Expand All @@ -34,6 +44,7 @@ Issues = "https://github.com/basetenlabs/baseten-python/issues"

[tool.poe.tasks]
generate-api = "python -m scripts.apigen"
generate-docs = "sphinx-build -j auto -b html -d .doctrees scripts/docgen _site"
typecheck = "ty check"
test = "pytest"

Expand All @@ -54,7 +65,7 @@ sequence = [
# exclude-newer = "7 days"

[tool.ty.src]
exclude = ["scripts/e2e_test_bootstrap/"]
exclude = ["scripts/docgen/", "scripts/e2e_test_bootstrap/"]

[build-system]
requires = ["hatchling"]
Expand Down
9 changes: 9 additions & 0 deletions scripts/docgen/_static/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* Hide the bottom-of-page footer bar. */
.bottom-of-page {
display: none;
}

/* Restore bottom spacing the hidden bar used to provide below the prev/next links. */
.related-pages {
margin-bottom: 1.5rem;
}
5 changes: 5 additions & 0 deletions scripts/docgen/_templates/autosummary/base.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{ objname | escape | underline }}

.. currentmodule:: {{ module }}

.. auto{{ objtype }}:: {{ objname }}
5 changes: 5 additions & 0 deletions scripts/docgen/_templates/autosummary/class.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{ objname | escape | underline }}

.. currentmodule:: {{ module }}

.. autoclass:: {{ objname }}
59 changes: 59 additions & 0 deletions scripts/docgen/_templates/autosummary/module.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{# Modules listed here only expose the named classes in the left nav. Every
class is still listed in the on-page table below and gets its own page (the
non-nav ones are generated as orphans by _generate_class_pages in conf.py).
Keep in sync with NAV_ONLY in conf.py. #}
{% set nav_only = {
"baseten.client.managementapi": ["ApiClient", "AsyncApiClient"],
"baseten.client.inferenceapi": ["ApiClient", "AsyncApiClient"],
"baseten.client.modelconfig": ["ModelConfig"],
} %}
{{ fullname | escape | underline }}

.. automodule:: {{ fullname }}
:no-members:

{% block classes %}
{% if classes %}
.. rubric:: Classes

.. autosummary::
{% if fullname not in nav_only %}:toctree:
{% endif %}:nosignatures:
{% for item in classes %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

{% block functions %}
{% if functions %}
.. rubric:: Functions

.. autosummary::
:toctree:
{% for item in functions %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

{% if fullname in nav_only %}
.. toctree::
:hidden:
{% for item in nav_only[fullname] %}
{{ fullname }}.{{ item }}
{%- endfor %}
{% endif %}

{% block modules %}
{% if modules %}
.. rubric:: Modules

.. autosummary::
:toctree:
:recursive:
{% for item in modules %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
86 changes: 86 additions & 0 deletions scripts/docgen/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Sphinx configuration for the API docs.

Build with ``poe generate-docs``, which runs ``sphinx-build`` over this
directory into ``_site``. Uses autosummary (recursive) to walk the package and
autodoc to import and document it, with autodoc_pydantic rendering the pydantic
models.
"""

import importlib
import inspect
import pathlib

project = "Baseten Python SDK"
html_title = "Baseten SDK"
html_theme = "furo"
templates_path = ["_templates"]
html_static_path = ["_static"]
html_css_files = ["custom.css"]

extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.intersphinx",
"sphinx.ext.napoleon",
"sphinxcontrib.autodoc_pydantic",
]

# httpx ships no Sphinx inventory (objects.inv), so it cannot be linked here.
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"pydantic": ("https://docs.pydantic.dev/latest", None),
}

autosummary_generate = True
# The public API is re-exported into package __init__s via imports; without this
# autosummary would treat those names as imported members and skip them.
autosummary_imported_members = True
autodoc_default_options = {"members": True, "show-inheritance": True}
autodoc_typehints = "description"
# Members are one-per-class-page, so drop the class prefix in the right-hand TOC.
toc_object_entries_show_parents = "hide"

# Render pydantic models without the inherited BaseModel boilerplate.
autodoc_pydantic_model_show_config_summary = False
autodoc_pydantic_model_show_validator_summary = False
autodoc_pydantic_model_show_json = False
autodoc_pydantic_model_member_order = "bysource"

# For these modules, only the listed classes appear in the left nav. Every other
# class still gets its own page (generated as an orphan below) and is listed in
# the module's on-page table, but is kept out of the sidebar. Keep in sync with
# the matching table in _templates/autosummary/module.rst.
NAV_ONLY = {
"baseten.client.managementapi": ["ApiClient", "AsyncApiClient"],
"baseten.client.inferenceapi": ["ApiClient", "AsyncApiClient"],
"baseten.client.modelconfig": ["ModelConfig"],
}


def _generate_class_pages(app):
"""Generate per-class stub pages for the NAV_ONLY modules.

autosummary couples page generation to nav inclusion (its ``:toctree:``
both writes the stub and adds it to the sidebar). To give every class a
page while keeping only the allowlisted ones in the nav, we generate the
stubs ourselves: allowlisted classes are normal pages (wired into the nav by
a hidden toctree in the module template), the rest are orphans.
"""
api_dir = pathlib.Path(app.srcdir) / "api"
api_dir.mkdir(exist_ok=True)
for module_name, nav_classes in NAV_ONLY.items():
module = importlib.import_module(module_name)
for name in getattr(module, "__all__", []):
if not inspect.isclass(getattr(module, name)):
continue
header = ":orphan:\n\n" if name not in nav_classes else ""
(api_dir / f"{module_name}.{name}.rst").write_text(
f"{header}{name}\n{'=' * len(name)}\n\n"
f".. currentmodule:: {module_name}\n\n"
f".. autoclass:: {name}\n",
encoding="utf-8",
)


def setup(app):
app.connect("builder-inited", _generate_class_pages)
13 changes: 13 additions & 0 deletions scripts/docgen/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Baseten Python SDK
==================

This site is the **API reference** for the Baseten Python SDK, generated from
the source. For installation, usage, and examples, see the project on GitHub:
`basetenlabs/baseten-python <https://github.com/basetenlabs/baseten-python>`_.

.. autosummary::
:toctree: api
:recursive:
:caption: API Reference

baseten.client
Loading
Loading