diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml
index d1f0609f..3106a23e 100644
--- a/.github/workflows/docs-build.yml
+++ b/.github/workflows/docs-build.yml
@@ -9,6 +9,10 @@ on:
jobs:
docs-build:
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ root: [sdk, cli]
if: github.event.pull_request.draft == false
timeout-minutes: 15
@@ -30,21 +34,25 @@ jobs:
uv pip list
- name: Build docs
- working-directory: python/docs
+ working-directory: python/docs/${{ matrix.root }}
run: |
- export PATH="$PWD/../.venv/bin:$PATH"
+ export PATH="$PWD/../../.venv/bin:$PATH"
make html SPHINXOPTS="-W --keep-going"
- name: Upload docs artifact
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
- name: docs-build
- path: python/docs/build/html/
+ name: docs-${{ matrix.root }}
+ path: python/docs/${{ matrix.root }}/build/html/
retention-days: 7
docs-linkcheck:
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ root: [sdk, cli]
if: github.event.pull_request.draft == false
timeout-minutes: 15
@@ -65,9 +73,9 @@ jobs:
uv pip install -e .
- name: Check links
- working-directory: python/docs
+ working-directory: python/docs/${{ matrix.root }}
run: |
- export PATH="$PWD/../.venv/bin:$PATH"
+ export PATH="$PWD/../../.venv/bin:$PATH"
make linkcheck
guardian-docs:
diff --git a/.gitignore b/.gitignore
index 90a93631..b0639ec9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,6 +69,7 @@ instance/
.scrapy
# Sphinx documentation
+/docs/
docs/_build/
python/docs/_build/
@@ -161,3 +162,4 @@ cython_debug/
.idea/
/uv.lock
.vscode*
+.worktrees/
diff --git a/python/README.md b/python/README.md
index 1eea5542..6184ba44 100644
--- a/python/README.md
+++ b/python/README.md
@@ -197,7 +197,8 @@ pip install -e .
Build docs from the repository root:
```bash
-uv run --group docs sphinx-build -M html python/docs/source python/docs/build -W --keep-going
+uv run --group docs sphinx-build -M html python/docs/sdk python/docs/sdk/build -W --keep-going
+uv run --group docs sphinx-build -M html python/docs/cli python/docs/cli/build -W --keep-going
```
# License
diff --git a/python/docs/Makefile b/python/docs/Makefile
index 713e2915..c7222ae8 100644
--- a/python/docs/Makefile
+++ b/python/docs/Makefile
@@ -1,14 +1,18 @@
-# Minimal makefile for Sphinx documentation
+# Build both independent Sphinx documentation roots.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-SOURCEDIR = source
-BUILDDIR = build
+SPHINXOPTS ?=
+ROOTS = sdk cli
+
+.PHONY: help html linkcheck clean
help:
- @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+ @echo "Build targets: html, linkcheck, clean"
+
+html:
+ @for root in $(ROOTS); do $(MAKE) -C $$root html SPHINXOPTS="$(SPHINXOPTS)" || exit $$?; done
-.PHONY: help Makefile
+linkcheck:
+ @for root in $(ROOTS); do $(MAKE) -C $$root linkcheck SPHINXOPTS="$(SPHINXOPTS)" || exit $$?; done
-%: Makefile
- @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+clean:
+ @for root in $(ROOTS); do $(MAKE) -C $$root clean || exit $$?; done
diff --git a/python/docs/cli/Makefile b/python/docs/cli/Makefile
new file mode 100644
index 00000000..91116bf4
--- /dev/null
+++ b/python/docs/cli/Makefile
@@ -0,0 +1,14 @@
+# Minimal makefile for Sphinx documentation
+
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+SOURCEDIR = .
+BUILDDIR = build
+
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/python/docs/cli/_reference.py b/python/docs/cli/_reference.py
new file mode 100644
index 00000000..2f0ff1a7
--- /dev/null
+++ b/python/docs/cli/_reference.py
@@ -0,0 +1,278 @@
+"""Structured Click command reference rendering for the CLI documentation."""
+
+from __future__ import annotations
+
+import inspect
+
+import click
+from click_extra.sphinx.click import TreeDirective
+from docutils import nodes
+from docutils.statemachine import StringList
+
+
+def _clean_text(value: str | None) -> str:
+ """Return a compact description for a command or parameter."""
+ if not value:
+ return "No description provided."
+ return " ".join(inspect.cleandoc(value).split())
+
+
+def _display_default(value: object) -> str:
+ if value is None:
+ return "—"
+ if isinstance(value, bool):
+ return str(value).lower()
+ return repr(value)
+
+
+def _literal(value: object, rst: bool) -> str:
+ marker = "``" if rst else "`"
+ return f"{marker}{value}{marker}"
+
+
+def _display_type(param: click.Parameter, context: click.Context) -> str:
+ """Get the Click metavar without depending on a Click minor version."""
+ for getter in (
+ lambda: param.make_metavar(context),
+ lambda: param.make_metavar(),
+ lambda: param.type.get_metavar(param, context),
+ lambda: param.type.name,
+ ):
+ try:
+ value = getter()
+ except (AttributeError, TypeError):
+ continue
+ if value:
+ return str(value)
+ return "—"
+
+
+def _split_help(value: str | None) -> tuple[list[str], list[str]]:
+ """Separate command prose from its documented examples."""
+ text = inspect.cleandoc(value or "").strip()
+ if not text:
+ return [], []
+
+ lines = text.splitlines()
+ example_index = next(
+ (index for index, line in enumerate(lines) if line.strip().lower() in {"example:", "examples:"}),
+ None,
+ )
+ if example_index is None:
+ return lines, []
+
+ description = lines[:example_index]
+ while description and not description[-1].strip():
+ description.pop()
+
+ examples = [line[2:] if line.startswith(" ") else line for line in lines[example_index + 1 :]]
+ while examples and not examples[-1].strip():
+ examples.pop()
+ return description, examples
+
+
+def _parameter_rows(
+ command: click.Command,
+ context: click.Context,
+ rst: bool,
+) -> tuple[list[list[str]], list[list[str]]]:
+ arguments: list[list[str]] = []
+ options: list[list[str]] = []
+
+ for param in command.params:
+ # Every command answers --help identically, so a row for it on each
+ # Options table is repeated noise — omit it throughout.
+ if isinstance(param, click.Option) and "--help" in param.opts:
+ continue
+ description = _clean_text(getattr(param, "help", None))
+ if isinstance(param, click.Argument):
+ # An argument's metavar just repeats its name, so show the Click
+ # type (text, path, integer, ...) instead.
+ arguments.append(
+ [
+ _literal(param.name.upper(), rst),
+ _literal(param.type.name or "text", rst),
+ "yes" if param.required else "no",
+ description,
+ ],
+ )
+ continue
+
+ if isinstance(param, click.Option):
+ names = [*param.opts, *param.secondary_opts]
+ options.append(
+ [
+ ", ".join(_literal(name, rst) for name in names),
+ _literal(_display_type(param, context), rst) if not param.is_flag else "—",
+ "yes" if param.required else "no",
+ # A flag's implicit false default carries no information.
+ "—"
+ if param.default is None or (param.is_flag and param.default is False)
+ else _literal(_display_default(param.default), rst),
+ description,
+ ],
+ )
+
+ return arguments, options
+
+
+def _command_link(name: str, anchor: str) -> nodes.paragraph:
+ """A table cell linking a command's name to its section on this page."""
+ paragraph = nodes.paragraph()
+ link = nodes.reference(refuri=f"#{anchor}")
+ link += nodes.literal(text=name)
+ paragraph += link
+ return paragraph
+
+
+def _append_table(
+ directive: ReferenceDirective,
+ parent: nodes.Element,
+ headers: list[str],
+ rows: list[list[str]],
+ source_file: str,
+) -> None:
+ table = nodes.table(classes=["colwidths-auto"])
+ tgroup = nodes.tgroup(cols=len(headers))
+ table += tgroup
+ for _ in headers:
+ tgroup += nodes.colspec(colwidth=1)
+
+ def add_row(container: nodes.Element, values: list[str | nodes.Node]) -> None:
+ row = nodes.row()
+ for value in values:
+ entry = nodes.entry()
+ if isinstance(value, nodes.Node):
+ entry += value
+ else:
+ directive.state.nested_parse(StringList([value], source_file), 0, entry)
+ row += entry
+ container += row
+
+ thead = nodes.thead()
+ add_row(thead, headers)
+ tgroup += thead
+ tbody = nodes.tbody()
+ for row in rows:
+ add_row(tbody, row)
+ tgroup += tbody
+ parent += table
+
+
+class ReferenceDirective(TreeDirective):
+ """Render Click metadata as a structured API-style reference."""
+
+ def run(self) -> list[nodes.Node]:
+ if self.content:
+ self.runner.execute_source(self)
+
+ cli_expr = self.arguments[0].strip()
+ try:
+ cli = eval(cli_expr, self.runner.namespace)
+ except Exception as exc:
+ raise RuntimeError(
+ f"lightning-reference: failed to evaluate {cli_expr!r}: {exc}",
+ ) from exc
+ if not isinstance(cli, click.Command):
+ raise TypeError(
+ f"lightning-reference: {cli_expr!r} did not yield a click.Command (got {type(cli).__name__}).",
+ )
+
+ max_depth = self.options.get("max-depth", 10)
+ root_label = self.options.get("root-label") or cli.name or cli_expr
+ anchor_prefix = self.options.get("anchor-prefix") or self._slug(root_label)
+ root_parts = root_label.split()
+ entries = self._walk(cli, max_depth)
+ source_file, _ = self.get_source_info()
+ # A container (not a section): the page's own title already names the
+ # root command, so the root gets no heading of its own — and a title-less
+ # section would leak its first paragraph into the page toc. The class lets
+ # downstream stylesheets target CLI reference pages without content-sniffing.
+ # Child sections are hoisted out on return (see below).
+ root_section = nodes.container(ids=[anchor_prefix], classes=["cli-reference"])
+ command_sections: dict[tuple[str, ...], nodes.Element] = {(): root_section}
+
+ for path, command in [([], cli), *entries]:
+ anchor = "-".join([anchor_prefix, *(self._slug(part) for part in path)])
+ label = " ".join([root_label, *path])
+ full_path = [*root_parts, *path]
+ context = click.Context(command, info_name=" ".join(full_path))
+ if path:
+ command_section = nodes.section(ids=[anchor])
+ command_sections[tuple(path[:-1])] += command_section
+ command_sections[tuple(path)] = command_section
+ command_section += nodes.title(text=label)
+ else:
+ command_section = root_section
+
+ description, examples = _split_help(command.help)
+ if description:
+ self.state.nested_parse(StringList(description, source_file), 0, command_section)
+
+ usage = " ".join(command.collect_usage_pieces(context))
+ usage = " ".join([*full_path, usage]).strip()
+ if usage:
+ # A literal block (rather than an inline literal) so themes give
+ # the synopsis code-block treatment: highlighting + a copy button.
+ label = nodes.paragraph()
+ label += nodes.strong(text="Usage:")
+ command_section += label
+ command_section += nodes.literal_block(usage, usage, language="console")
+
+ if isinstance(command, click.Group) and command.commands:
+ # "Commands" matches Click's own vocabulary: the usage line says
+ # `COMMAND [ARGS]...` and Click's help prints a "Commands" section.
+ commands_section = nodes.section(ids=[f"{anchor}-commands"])
+ commands_section += nodes.title(text="Commands")
+ rows = [
+ [
+ _command_link(name, f"{anchor}-{self._slug(name)}"),
+ _clean_text(subcommand.get_short_help_str(limit=200)),
+ ]
+ for name, subcommand in sorted(command.commands.items())
+ ]
+ _append_table(self, commands_section, ["Command", "Description"], rows, source_file)
+ command_section += commands_section
+
+ arguments, options = _parameter_rows(command, context, True)
+ if arguments:
+ arguments_section = nodes.section(ids=[f"{anchor}-arguments"])
+ arguments_section += nodes.title(text="Arguments")
+ _append_table(
+ self,
+ arguments_section,
+ ["Name", "Type", "Required", "Description"],
+ arguments,
+ source_file,
+ )
+ command_section += arguments_section
+ if options:
+ options_section = nodes.section(ids=[f"{anchor}-options"])
+ options_section += nodes.title(text="Options")
+ _append_table(
+ self,
+ options_section,
+ ["Option", "Type", "Required", "Default", "Description"],
+ options,
+ source_file,
+ )
+ command_section += options_section
+ if examples:
+ examples_section = nodes.section(ids=[f"{anchor}-examples"])
+ examples_section += nodes.title(text="Examples")
+ examples_section += nodes.literal_block(
+ "\n".join(examples),
+ "\n".join(examples),
+ language="console",
+ )
+ command_section += examples_section
+
+ # Hoist the root's child sections (Commands, each subcommand, ...) to
+ # document level: Sphinx's page toc only descends through sections, so
+ # anything left inside the container would vanish from "On this page".
+ result: list[nodes.Node] = [root_section]
+ for child in list(root_section.children):
+ if isinstance(child, nodes.section):
+ root_section.remove(child)
+ result.append(child)
+ return result
diff --git a/python/docs/source/_static/copybutton.js b/python/docs/cli/_static/copybutton.js
similarity index 100%
rename from python/docs/source/_static/copybutton.js
rename to python/docs/cli/_static/copybutton.js
diff --git a/python/docs/source/_static/images/icon.svg b/python/docs/cli/_static/images/icon.svg
similarity index 100%
rename from python/docs/source/_static/images/icon.svg
rename to python/docs/cli/_static/images/icon.svg
diff --git a/python/docs/source/_static/images/logo-large.svg b/python/docs/cli/_static/images/logo-large.svg
similarity index 100%
rename from python/docs/source/_static/images/logo-large.svg
rename to python/docs/cli/_static/images/logo-large.svg
diff --git a/python/docs/source/_static/images/logo-small.svg b/python/docs/cli/_static/images/logo-small.svg
similarity index 100%
rename from python/docs/source/_static/images/logo-small.svg
rename to python/docs/cli/_static/images/logo-small.svg
diff --git a/python/docs/source/_static/images/logo.png b/python/docs/cli/_static/images/logo.png
similarity index 100%
rename from python/docs/source/_static/images/logo.png
rename to python/docs/cli/_static/images/logo.png
diff --git a/python/docs/source/_static/images/logo.svg b/python/docs/cli/_static/images/logo.svg
similarity index 100%
rename from python/docs/source/_static/images/logo.svg
rename to python/docs/cli/_static/images/logo.svg
diff --git a/python/docs/source/_static/main.css b/python/docs/cli/_static/main.css
similarity index 100%
rename from python/docs/source/_static/main.css
rename to python/docs/cli/_static/main.css
diff --git a/python/docs/source/_templates/theme_variables.jinja b/python/docs/cli/_templates/theme_variables.jinja
similarity index 100%
rename from python/docs/source/_templates/theme_variables.jinja
rename to python/docs/cli/_templates/theme_variables.jinja
diff --git a/python/docs/cli/api-key.rst b/python/docs/cli/api-key.rst
new file mode 100644
index 00000000..ce5874b4
--- /dev/null
+++ b/python/docs/cli/api-key.rst
@@ -0,0 +1,8 @@
+API Key
++++++++
+
+.. lightning-reference:: api_key
+ :root-label: lightning api-key
+ :anchor-prefix: api-key
+
+ from lightning_sdk.cli.groups import api_key
diff --git a/python/docs/cli/base-studio.rst b/python/docs/cli/base-studio.rst
new file mode 100644
index 00000000..b1846b0a
--- /dev/null
+++ b/python/docs/cli/base-studio.rst
@@ -0,0 +1,8 @@
+Base Studio
++++++++++++
+
+.. lightning-reference:: base_studio
+ :root-label: lightning base-studio
+ :anchor-prefix: base-studio
+
+ from lightning_sdk.cli.groups import base_studio
diff --git a/python/docs/source/conf.py b/python/docs/cli/conf.py
similarity index 84%
rename from python/docs/source/conf.py
rename to python/docs/cli/conf.py
index 49ba2151..ff834e1d 100644
--- a/python/docs/source/conf.py
+++ b/python/docs/cli/conf.py
@@ -3,16 +3,17 @@
import sys
import click
-from click.formatting import HelpFormatter
_PATH_HERE = os.path.abspath(os.path.dirname(__file__))
_PATH_ROOT = os.path.realpath(os.path.join(_PATH_HERE, "..", ".."))
+sys.path.insert(0, _PATH_HERE)
sys.path.insert(0, _PATH_ROOT)
+os.environ["LIGHTNING_EXPERIMENTAL_CLI_ONLY"] = "1"
+
import lightning_sdk # noqa: E402
from lightning_sdk.cli.entrypoint import main_cli as _main_cli # noqa: E402
-
-_main_cli.context_class.formatter_class = HelpFormatter
+from _reference import ReferenceDirective # noqa: E402
_CLI_ARGUMENT_HELP = {
("lightning config set user", "user_name"): "User name to make active in the local Lightning CLI config.",
@@ -96,47 +97,6 @@ def _apply_cli_argument_help(command: click.Command, command_path: str) -> None:
_apply_cli_argument_help(_main_cli, "lightning")
-def _strip_click_bar(line: str) -> str:
- return line[2:] if line.startswith("| ") else line
-
-
-def _is_cli_example_heading(line: str) -> bool:
- return _strip_click_bar(line).strip() in {"Example:", "Examples:"}
-
-
-def _is_cli_example_line(line: str) -> bool:
- line = _strip_click_bar(line)
- return line == "" or line.startswith(" ")
-
-
-def _format_cli_examples_as_code_blocks(app, ctx, lines: list[str]) -> None: # noqa: ANN001, ARG001
- formatted: list[str] = []
- index = 0
-
- while index < len(lines):
- line = lines[index]
- if not _is_cli_example_heading(line):
- formatted.append(line)
- index += 1
- continue
-
- formatted.extend([_strip_click_bar(line).strip(), "", ".. code-block:: console", ""])
- index += 1
-
- example_lines: list[str] = []
- while index < len(lines) and _is_cli_example_line(lines[index]):
- example_lines.append(_strip_click_bar(lines[index]))
- index += 1
-
- while example_lines and example_lines[-1] == "":
- example_lines.pop()
-
- formatted.extend(f" {example_line.lstrip()}" if example_line else "" for example_line in example_lines)
- formatted.append("")
-
- lines[:] = formatted
-
-
project = "Lightning SDK"
copyright = "Lightning AI" # noqa: A001
author = "Lightning AI"
@@ -155,20 +115,18 @@ def _format_cli_examples_as_code_blocks(app, ctx, lines: list[str]) -> None: #
"sphinx.ext.autosectionlabel",
"sphinx.ext.todo",
"sphinx_autodoc_typehints",
- "sphinx_click",
+ "click_extra.sphinx",
"sphinx_copybutton",
"sphinx_paramlinks",
"sphinx_togglebutton",
- "myst_parser",
]
-templates_path = ["_templates"]
+click_extra_enable_exec_directives = True
-myst_heading_anchors = 3
+templates_path = ["_templates"]
source_suffix = {
".rst": "restructuredtext",
- ".md": "markdown",
}
master_doc = "index"
@@ -254,8 +212,6 @@ def _can_resolve_host(hostname: str) -> bool:
"tqdm",
"backoff",
]
-sphinx_click_mock_imports = autodoc_mock_imports
-
copybutton_prompt_text = ">>> "
copybutton_prompt_text1 = "... "
copybutton_only_copy_prompt_lines = True
@@ -270,4 +226,4 @@ def _can_resolve_host(hostname: str) -> bool:
def setup(app) -> None: # noqa: ANN001
app.add_css_file("main.css")
- app.connect("sphinx-click-process-description", _format_cli_examples_as_code_blocks)
+ app.add_directive("lightning-reference", ReferenceDirective)
diff --git a/python/docs/cli/config.rst b/python/docs/cli/config.rst
new file mode 100644
index 00000000..c801b8af
--- /dev/null
+++ b/python/docs/cli/config.rst
@@ -0,0 +1,8 @@
+Config
+++++++
+
+.. lightning-reference:: config
+ :root-label: lightning config
+ :anchor-prefix: config
+
+ from lightning_sdk.cli.groups import config
diff --git a/python/docs/cli/container.rst b/python/docs/cli/container.rst
new file mode 100644
index 00000000..c93c1c61
--- /dev/null
+++ b/python/docs/cli/container.rst
@@ -0,0 +1,8 @@
+Container
++++++++++
+
+.. lightning-reference:: container
+ :root-label: lightning container
+ :anchor-prefix: container
+
+ from lightning_sdk.cli.groups import container
diff --git a/python/docs/cli/cp.rst b/python/docs/cli/cp.rst
new file mode 100644
index 00000000..4c47fd57
--- /dev/null
+++ b/python/docs/cli/cp.rst
@@ -0,0 +1,8 @@
+Copy
+++++
+
+.. lightning-reference:: cp
+ :root-label: lightning cp
+ :anchor-prefix: cp
+
+ from lightning_sdk.cli.groups import cp
diff --git a/python/docs/cli/deployment.rst b/python/docs/cli/deployment.rst
new file mode 100644
index 00000000..cc29bf15
--- /dev/null
+++ b/python/docs/cli/deployment.rst
@@ -0,0 +1,8 @@
+Deployment
+++++++++++
+
+.. lightning-reference:: deployment
+ :root-label: lightning deployment
+ :anchor-prefix: deployment
+
+ from lightning_sdk.cli.groups import deployment
diff --git a/python/docs/cli/file.rst b/python/docs/cli/file.rst
new file mode 100644
index 00000000..b9cb5e78
--- /dev/null
+++ b/python/docs/cli/file.rst
@@ -0,0 +1,8 @@
+File
+++++
+
+.. lightning-reference:: file
+ :root-label: lightning file
+ :anchor-prefix: file
+
+ from lightning_sdk.cli.groups import file
diff --git a/python/docs/cli/folder.rst b/python/docs/cli/folder.rst
new file mode 100644
index 00000000..4c317f2a
--- /dev/null
+++ b/python/docs/cli/folder.rst
@@ -0,0 +1,8 @@
+Folder
+++++++
+
+.. lightning-reference:: folder
+ :root-label: lightning folder
+ :anchor-prefix: folder
+
+ from lightning_sdk.cli.groups import folder
diff --git a/python/docs/cli/index.rst b/python/docs/cli/index.rst
new file mode 100644
index 00000000..6b864f4d
--- /dev/null
+++ b/python/docs/cli/index.rst
@@ -0,0 +1,92 @@
+Command Line Interface
+++++++++++++++++++++++
+
+The Python package installs the ``lightning`` command. The ``lightning-sdk``
+console script is an alias for the same command group.
+
+Use the CLI when you want to manage Lightning AI resources from a terminal,
+CI job, shell script, or other automation.
+
+.. lightning-reference:: main_cli
+ :root-label: lightning
+ :anchor-prefix: lightning
+
+ from lightning_sdk.cli.entrypoint import main_cli
+
+Install
+-------
+
+Install or upgrade the Python package:
+
+.. code-block:: bash
+
+ pip install lightning-sdk -U
+
+Authenticate
+------------
+
+For interactive use, sign in with:
+
+.. code-block:: bash
+
+ lightning login
+
+For non-interactive environments, configure credentials through environment
+variables instead:
+
+.. code-block:: bash
+
+ export LIGHTNING_USER_ID=your-user-id
+ export LIGHTNING_API_KEY=your-api-key
+
+Usage
+-----
+
+Run a command group directly:
+
+.. code-block:: bash
+
+ lightning [command]
+
+Every command and subcommand exposes help directly:
+
+.. code-block:: bash
+
+ lightning COMMAND --help
+
+Common Workflows
+----------------
+
+* Develop interactively with :doc:`studio`.
+* Submit and inspect training or batch work with :doc:`job` and :doc:`mmt`.
+* Build and operate inference services with :doc:`deployment` and :doc:`model`.
+* Move data and artifacts with :doc:`file`, :doc:`folder`, :doc:`container`, and :doc:`cp`.
+* Configure accounts, organizations, teamspaces, cloud accounts, and SSH with
+ :doc:`config`, :doc:`api-key`, and :doc:`ssh`.
+* Manage lower-level sandbox sessions with :doc:`sandbox`.
+
+Command details
+---------------
+
+The pages below keep focused URLs for each command group and its full option
+reference.
+
+.. toctree::
+ :maxdepth: 1
+
+ config
+ job
+ mmt
+ machine
+ deployment
+ container
+ model
+ api-key
+ file
+ folder
+ ssh
+ studio
+ sandbox
+ base-studio
+ license
+ cp
diff --git a/python/docs/cli/job.rst b/python/docs/cli/job.rst
new file mode 100644
index 00000000..292fc270
--- /dev/null
+++ b/python/docs/cli/job.rst
@@ -0,0 +1,8 @@
+Job
++++
+
+.. lightning-reference:: job
+ :root-label: lightning job
+ :anchor-prefix: job
+
+ from lightning_sdk.cli.groups import job
diff --git a/python/docs/cli/license.rst b/python/docs/cli/license.rst
new file mode 100644
index 00000000..9ff67763
--- /dev/null
+++ b/python/docs/cli/license.rst
@@ -0,0 +1,8 @@
+License
++++++++
+
+.. lightning-reference:: license
+ :root-label: lightning license
+ :anchor-prefix: license
+
+ from lightning_sdk.cli.groups import license
diff --git a/python/docs/cli/machine.rst b/python/docs/cli/machine.rst
new file mode 100644
index 00000000..d02e6d0c
--- /dev/null
+++ b/python/docs/cli/machine.rst
@@ -0,0 +1,8 @@
+Machine
++++++++
+
+.. lightning-reference:: machine
+ :root-label: lightning machine
+ :anchor-prefix: machine
+
+ from lightning_sdk.cli.groups import machine
diff --git a/python/docs/cli/make.bat b/python/docs/cli/make.bat
new file mode 100644
index 00000000..c1becfd1
--- /dev/null
+++ b/python/docs/cli/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+
+:end
+popd
diff --git a/python/docs/cli/mmt.rst b/python/docs/cli/mmt.rst
new file mode 100644
index 00000000..b994fb61
--- /dev/null
+++ b/python/docs/cli/mmt.rst
@@ -0,0 +1,8 @@
+Multi-Machine Training
+++++++++++++++++++++++
+
+.. lightning-reference:: mmt
+ :root-label: lightning mmt
+ :anchor-prefix: mmt
+
+ from lightning_sdk.cli.groups import mmt
diff --git a/python/docs/cli/model.rst b/python/docs/cli/model.rst
new file mode 100644
index 00000000..5dafe706
--- /dev/null
+++ b/python/docs/cli/model.rst
@@ -0,0 +1,8 @@
+Model
++++++
+
+.. lightning-reference:: model
+ :root-label: lightning model
+ :anchor-prefix: model
+
+ from lightning_sdk.cli.groups import model
diff --git a/python/docs/cli/sandbox.rst b/python/docs/cli/sandbox.rst
new file mode 100644
index 00000000..f8e711d2
--- /dev/null
+++ b/python/docs/cli/sandbox.rst
@@ -0,0 +1,8 @@
+Sandbox
++++++++
+
+.. lightning-reference:: sandbox
+ :root-label: lightning sandbox
+ :anchor-prefix: sandbox
+
+ from lightning_sdk.cli.groups import sandbox
diff --git a/python/docs/cli/ssh.rst b/python/docs/cli/ssh.rst
new file mode 100644
index 00000000..fdd0ab7e
--- /dev/null
+++ b/python/docs/cli/ssh.rst
@@ -0,0 +1,8 @@
+SSH
++++
+
+.. lightning-reference:: ssh
+ :root-label: lightning ssh
+ :anchor-prefix: ssh
+
+ from lightning_sdk.cli.groups import ssh
diff --git a/python/docs/cli/studio.rst b/python/docs/cli/studio.rst
new file mode 100644
index 00000000..43ae54cc
--- /dev/null
+++ b/python/docs/cli/studio.rst
@@ -0,0 +1,8 @@
+Studio
+++++++
+
+.. lightning-reference:: studio
+ :root-label: lightning studio
+ :anchor-prefix: studio
+
+ from lightning_sdk.cli.groups import studio
diff --git a/python/docs/make.bat b/python/docs/make.bat
index 4d9eb83d..b6f1cd11 100644
--- a/python/docs/make.bat
+++ b/python/docs/make.bat
@@ -2,34 +2,20 @@
pushd %~dp0
-REM Command file for Sphinx documentation
+if "%1" == "" goto help
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
-set SOURCEDIR=source
-set BUILDDIR=build
-
-if "%1" == "" goto help
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
+for %%R in (sdk cli) do (
+ call %%R\make.bat %1
+ if errorlevel 1 exit /b 1
)
-
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+echo Build targets: html, linkcheck, clean
:end
popd
diff --git a/python/docs/sdk/Makefile b/python/docs/sdk/Makefile
new file mode 100644
index 00000000..91116bf4
--- /dev/null
+++ b/python/docs/sdk/Makefile
@@ -0,0 +1,14 @@
+# Minimal makefile for Sphinx documentation
+
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+SOURCEDIR = .
+BUILDDIR = build
+
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/python/docs/sdk/_static/copybutton.js b/python/docs/sdk/_static/copybutton.js
new file mode 100644
index 00000000..aef241ab
--- /dev/null
+++ b/python/docs/sdk/_static/copybutton.js
@@ -0,0 +1,78 @@
+/* Copied from the official Python docs: https://docs.python.org/3/_static/copybutton.js */
+$(document).ready(function () {
+ /* Add a [>>>] button on the top-right corner of code samples to hide
+ * the >>> and ... prompts and the output and thus make the code
+ * copyable. */
+ var div = $(
+ ".highlight-python .highlight," +
+ ".highlight-python3 .highlight," +
+ ".highlight-pycon .highlight," +
+ ".highlight-default .highlight",
+ );
+ var pre = div.find("pre");
+
+ // get the styles from the current theme
+ pre.parent().parent().css("position", "relative");
+ var hide_text = "Hide the prompts and output";
+ var show_text = "Show the prompts and output";
+ var border_width = pre.css("border-top-width");
+ var border_style = pre.css("border-top-style");
+ var border_color = pre.css("border-top-color");
+ var button_styles = {
+ cursor: "pointer",
+ position: "absolute",
+ top: "0",
+ right: "0",
+ "border-color": border_color,
+ "border-style": border_style,
+ "border-width": border_width,
+ color: border_color,
+ "text-size": "75%",
+ "font-family": "monospace",
+ "padding-left": "0.2em",
+ "padding-right": "0.2em",
+ "border-radius": "0 3px 0 0",
+ };
+
+ // create and add the button to all the code blocks that contain >>>
+ div.each(function (index) {
+ var jthis = $(this);
+ if (jthis.find(".gp").length > 0) {
+ var button = $('>>>');
+ button.css(button_styles);
+ button.attr("title", hide_text);
+ button.data("hidden", "false");
+ jthis.prepend(button);
+ }
+ // tracebacks (.gt) contain bare text elements that need to be
+ // wrapped in a span to work with .nextUntil() (see later)
+ jthis
+ .find("pre:has(.gt)")
+ .contents()
+ .filter(function () {
+ return this.nodeType == 3 && this.data.trim().length > 0;
+ })
+ .wrap("");
+ });
+
+ // define the behavior of the button when it's clicked
+ $(".copybutton").click(function (e) {
+ e.preventDefault();
+ var button = $(this);
+ if (button.data("hidden") === "false") {
+ // hide the code output
+ button.parent().find(".go, .gp, .gt").hide();
+ button.next("pre").find(".gt").nextUntil(".gp, .go").css("visibility", "hidden");
+ button.css("text-decoration", "line-through");
+ button.attr("title", show_text);
+ button.data("hidden", "true");
+ } else {
+ // show the code output
+ button.parent().find(".go, .gp, .gt").show();
+ button.next("pre").find(".gt").nextUntil(".gp, .go").css("visibility", "visible");
+ button.css("text-decoration", "none");
+ button.attr("title", hide_text);
+ button.data("hidden", "false");
+ }
+ });
+});
diff --git a/python/docs/sdk/_static/images/icon.svg b/python/docs/sdk/_static/images/icon.svg
new file mode 100644
index 00000000..e88fc190
--- /dev/null
+++ b/python/docs/sdk/_static/images/icon.svg
@@ -0,0 +1,9 @@
+
diff --git a/python/docs/sdk/_static/images/logo-large.svg b/python/docs/sdk/_static/images/logo-large.svg
new file mode 100644
index 00000000..39531f95
--- /dev/null
+++ b/python/docs/sdk/_static/images/logo-large.svg
@@ -0,0 +1,9 @@
+
diff --git a/python/docs/sdk/_static/images/logo-small.svg b/python/docs/sdk/_static/images/logo-small.svg
new file mode 100644
index 00000000..1f523a57
--- /dev/null
+++ b/python/docs/sdk/_static/images/logo-small.svg
@@ -0,0 +1,9 @@
+
diff --git a/python/docs/sdk/_static/images/logo.png b/python/docs/sdk/_static/images/logo.png
new file mode 100644
index 00000000..392c965b
Binary files /dev/null and b/python/docs/sdk/_static/images/logo.png differ
diff --git a/python/docs/sdk/_static/images/logo.svg b/python/docs/sdk/_static/images/logo.svg
new file mode 100644
index 00000000..60efaa29
--- /dev/null
+++ b/python/docs/sdk/_static/images/logo.svg
@@ -0,0 +1,12 @@
+
diff --git a/python/docs/sdk/_static/main.css b/python/docs/sdk/_static/main.css
new file mode 100644
index 00000000..8739cdf7
--- /dev/null
+++ b/python/docs/sdk/_static/main.css
@@ -0,0 +1 @@
+/* Custom styles for lightning-sdk docs */
diff --git a/python/docs/sdk/_templates/theme_variables.jinja b/python/docs/sdk/_templates/theme_variables.jinja
new file mode 100644
index 00000000..5d092539
--- /dev/null
+++ b/python/docs/sdk/_templates/theme_variables.jinja
@@ -0,0 +1,18 @@
+{%- set external_urls = {
+ 'github': 'https://github.com/Lightning-AI/lightning-Sandbox',
+ 'github_issues': 'https://github.com/Lightning-AI/lightning-Sandbox/issues',
+ 'contributing': 'https://github.com/Lightning-AI/lightning/blob/master/CONTRIBUTING.md',
+ 'governance': 'https://github.com/Lightning-AI/lightning/blob/master/governance.md',
+ 'docs': 'https://lightning-ai.github.io/lightning-Sandbox/',
+ 'twitter': 'https://twitter.com/LightningAI',
+ 'discuss': 'https://discord.com/invite/tfXFetEZxv',
+ 'tutorials': 'https://lightning.ai',
+ 'previous_pytorch_versions': 'https://lightning-ai.github.io/lightning-Sandbox/',
+ 'home': 'https://lightning-ai.github.io/lightning-Sandbox/',
+ 'get_started': 'https://lightning.ai',
+ 'features': 'https://lightning-ai.github.io/lightning-Sandbox/',
+ 'blog': 'https://www.Lightning.ai/blog',
+ 'resources': 'https://lightning.ai',
+ 'support': 'https://lightning-ai.github.io/lightning-Sandbox/',
+}
+-%}
diff --git a/python/docs/source/api.rst b/python/docs/sdk/api.rst
similarity index 100%
rename from python/docs/source/api.rst
rename to python/docs/sdk/api.rst
diff --git a/python/docs/source/api/agent.rst b/python/docs/sdk/api/agent.rst
similarity index 100%
rename from python/docs/source/api/agent.rst
rename to python/docs/sdk/api/agent.rst
diff --git a/python/docs/source/api/deployment.rst b/python/docs/sdk/api/deployment.rst
similarity index 100%
rename from python/docs/source/api/deployment.rst
rename to python/docs/sdk/api/deployment.rst
diff --git a/python/docs/source/api/experiment.rst b/python/docs/sdk/api/experiment.rst
similarity index 100%
rename from python/docs/source/api/experiment.rst
rename to python/docs/sdk/api/experiment.rst
diff --git a/python/docs/source/api/job.rst b/python/docs/sdk/api/job.rst
similarity index 100%
rename from python/docs/source/api/job.rst
rename to python/docs/sdk/api/job.rst
diff --git a/python/docs/source/api/k8s-cluster.rst b/python/docs/sdk/api/k8s-cluster.rst
similarity index 100%
rename from python/docs/source/api/k8s-cluster.rst
rename to python/docs/sdk/api/k8s-cluster.rst
diff --git a/python/docs/source/api/machine.rst b/python/docs/sdk/api/machine.rst
similarity index 100%
rename from python/docs/source/api/machine.rst
rename to python/docs/sdk/api/machine.rst
diff --git a/python/docs/source/api/mmt.rst b/python/docs/sdk/api/mmt.rst
similarity index 100%
rename from python/docs/source/api/mmt.rst
rename to python/docs/sdk/api/mmt.rst
diff --git a/python/docs/source/api/models.rst b/python/docs/sdk/api/models.rst
similarity index 100%
rename from python/docs/source/api/models.rst
rename to python/docs/sdk/api/models.rst
diff --git a/python/docs/source/api/organization.rst b/python/docs/sdk/api/organization.rst
similarity index 100%
rename from python/docs/source/api/organization.rst
rename to python/docs/sdk/api/organization.rst
diff --git a/python/docs/source/api/owner.rst b/python/docs/sdk/api/owner.rst
similarity index 100%
rename from python/docs/source/api/owner.rst
rename to python/docs/sdk/api/owner.rst
diff --git a/python/docs/source/api/status.rst b/python/docs/sdk/api/status.rst
similarity index 100%
rename from python/docs/source/api/status.rst
rename to python/docs/sdk/api/status.rst
diff --git a/python/docs/source/api/studio.rst b/python/docs/sdk/api/studio.rst
similarity index 100%
rename from python/docs/source/api/studio.rst
rename to python/docs/sdk/api/studio.rst
diff --git a/python/docs/source/api/teamspace.rst b/python/docs/sdk/api/teamspace.rst
similarity index 100%
rename from python/docs/source/api/teamspace.rst
rename to python/docs/sdk/api/teamspace.rst
diff --git a/python/docs/source/api/user.rst b/python/docs/sdk/api/user.rst
similarity index 100%
rename from python/docs/source/api/user.rst
rename to python/docs/sdk/api/user.rst
diff --git a/python/docs/sdk/conf.py b/python/docs/sdk/conf.py
new file mode 100644
index 00000000..b7dcd0a3
--- /dev/null
+++ b/python/docs/sdk/conf.py
@@ -0,0 +1,139 @@
+import os
+import socket
+import sys
+
+_PATH_HERE = os.path.abspath(os.path.dirname(__file__))
+_PATH_ROOT = os.path.realpath(os.path.join(_PATH_HERE, "..", ".."))
+sys.path.insert(0, _PATH_ROOT)
+
+import lightning_sdk # noqa: E402
+project = "Lightning SDK"
+copyright = "Lightning AI" # noqa: A001
+author = "Lightning AI"
+version = lightning_sdk.__version__
+release = lightning_sdk.__version__
+
+needs_sphinx = "8.0"
+
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.autosummary",
+ "sphinx.ext.doctest",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.napoleon",
+ "sphinx.ext.viewcode",
+ "sphinx.ext.autosectionlabel",
+ "sphinx.ext.todo",
+ "sphinx_autodoc_typehints",
+ "sphinx_copybutton",
+ "sphinx_paramlinks",
+ "sphinx_togglebutton",
+ "myst_parser",
+]
+
+templates_path = ["_templates"]
+
+myst_heading_anchors = 3
+
+source_suffix = {
+ ".rst": "restructuredtext",
+ ".md": "markdown",
+}
+
+master_doc = "index"
+language = "en"
+exclude_patterns = ["_build", "_templates"]
+pygments_style = None
+
+html_theme = "pydata_sphinx_theme"
+html_theme_options = {
+ "github_url": "https://github.com/Lightning-AI/lightning-sdk",
+ "show_toc_level": 2,
+}
+
+html_static_path = ["_static"]
+htmlhelp_basename = "lightning-sdk-doc"
+
+
+def _can_resolve_host(hostname: str) -> bool:
+ try:
+ socket.getaddrinfo(hostname, 443)
+ except OSError:
+ return False
+ return True
+
+
+if all(_can_resolve_host(host) for host in ("docs.python.org", "pytorch.org")):
+ intersphinx_mapping = {
+ "python": ("https://docs.python.org/3", None),
+ "torch": ("https://pytorch.org/docs/stable/", None),
+ }
+else:
+ intersphinx_mapping = {}
+
+nitpicky = True
+nitpick_ignore = [
+ ("py:class", "typing.Any"),
+ ("py:data", "typing.Any"),
+ ("py:data", "typing.Optional"),
+ ("py:data", "typing.Union"),
+ ("py:class", "pathlib.Path"),
+ ("py:class", "pathlib._local.Path"),
+ ("py:class", "enum.Enum"),
+ # base / internal types not worth documenting separately
+ ("py:class", "Auth"),
+ ("py:class", "lightning_sdk.api.deployment_api.Auth"),
+ ("py:class", "MachineDict"),
+]
+nitpick_ignore_regex = [
+ # private / internal base classes
+ ("py:class", r".*\._\w+"),
+ # generated openapi types (full path)
+ ("py:class", r"lightning_sdk\.lightning_cloud\..*"),
+ # V1* and Externalv1* openapi short names
+ ("py:class", r"V1\w+"),
+ ("py:class", r"Externalv1\w+"),
+]
+
+autosummary_generate = True
+autodoc_member_order = "groupwise"
+autoclass_content = "both"
+autodoc_typehints = "description"
+typehints_description_target = "documented_params"
+
+autodoc_default_options = {
+ "members": True,
+ "methods": True,
+ "special-members": "__call__",
+ "exclude-members": "_abc_impl",
+ "show-inheritance": True,
+}
+
+autosectionlabel_prefix_document = True
+autosectionlabel_maxdepth = 1
+
+autodoc_mock_imports = [
+ "lightning_cloud",
+ "requests",
+ "docker",
+ "fastapi",
+ "uvicorn",
+ "simple_term_menu",
+ "rich",
+ "tqdm",
+ "backoff",
+]
+copybutton_prompt_text = ">>> "
+copybutton_prompt_text1 = "... "
+copybutton_only_copy_prompt_lines = True
+
+linkcheck_anchors = False
+linkcheck_timeout = 60
+linkcheck_retries = 3
+linkcheck_ignore = [
+ r"https://lightning\.ai/.*",
+]
+
+
+def setup(app) -> None: # noqa: ANN001
+ app.add_css_file("main.css")
diff --git a/python/docs/source/examples.rst b/python/docs/sdk/examples.rst
similarity index 100%
rename from python/docs/source/examples.rst
rename to python/docs/sdk/examples.rst
diff --git a/python/docs/source/examples/api-cli.rst b/python/docs/sdk/examples/api-cli.rst
similarity index 100%
rename from python/docs/source/examples/api-cli.rst
rename to python/docs/sdk/examples/api-cli.rst
diff --git a/python/docs/source/examples/jobs-cli.rst b/python/docs/sdk/examples/jobs-cli.rst
similarity index 100%
rename from python/docs/source/examples/jobs-cli.rst
rename to python/docs/sdk/examples/jobs-cli.rst
diff --git a/python/docs/source/examples/jobs.rst b/python/docs/sdk/examples/jobs.rst
similarity index 100%
rename from python/docs/source/examples/jobs.rst
rename to python/docs/sdk/examples/jobs.rst
diff --git a/python/docs/source/examples/mmts-cli.rst b/python/docs/sdk/examples/mmts-cli.rst
similarity index 100%
rename from python/docs/source/examples/mmts-cli.rst
rename to python/docs/sdk/examples/mmts-cli.rst
diff --git a/python/docs/source/examples/mmts.rst b/python/docs/sdk/examples/mmts.rst
similarity index 100%
rename from python/docs/source/examples/mmts.rst
rename to python/docs/sdk/examples/mmts.rst
diff --git a/python/docs/source/examples/sandboxes-cli.rst b/python/docs/sdk/examples/sandboxes-cli.rst
similarity index 100%
rename from python/docs/source/examples/sandboxes-cli.rst
rename to python/docs/sdk/examples/sandboxes-cli.rst
diff --git a/python/docs/source/examples/sandboxes.rst b/python/docs/sdk/examples/sandboxes.rst
similarity index 100%
rename from python/docs/source/examples/sandboxes.rst
rename to python/docs/sdk/examples/sandboxes.rst
diff --git a/python/docs/source/examples/studios-cli.rst b/python/docs/sdk/examples/studios-cli.rst
similarity index 100%
rename from python/docs/source/examples/studios-cli.rst
rename to python/docs/sdk/examples/studios-cli.rst
diff --git a/python/docs/source/examples/studios.rst b/python/docs/sdk/examples/studios.rst
similarity index 100%
rename from python/docs/source/examples/studios.rst
rename to python/docs/sdk/examples/studios.rst
diff --git a/python/docs/source/examples/teamspaces-cli.rst b/python/docs/sdk/examples/teamspaces-cli.rst
similarity index 100%
rename from python/docs/source/examples/teamspaces-cli.rst
rename to python/docs/sdk/examples/teamspaces-cli.rst
diff --git a/python/docs/source/examples/teamspaces.rst b/python/docs/sdk/examples/teamspaces.rst
similarity index 100%
rename from python/docs/source/examples/teamspaces.rst
rename to python/docs/sdk/examples/teamspaces.rst
diff --git a/python/docs/source/index.rst b/python/docs/sdk/index.rst
similarity index 88%
rename from python/docs/source/index.rst
rename to python/docs/sdk/index.rst
index 5e78f425..234127f4 100644
--- a/python/docs/source/index.rst
+++ b/python/docs/sdk/index.rst
@@ -47,8 +47,6 @@ Documentation Map
The :ref:`Examples ` provide tutorial walkthroughs for
common CLI and SDK workflows.
-The :ref:`CLI reference ` documents the command-line interface.
-
The :ref:`API reference ` documents every public class and function.
.. _start-section:
@@ -62,14 +60,4 @@ Start
install
quickstart
examples
- cli
-
-.. _api-reference-section:
-
-API Reference
-=============
-
-.. toctree::
- :maxdepth: 1
-
api
diff --git a/python/docs/source/install.rst b/python/docs/sdk/install.rst
similarity index 100%
rename from python/docs/source/install.rst
rename to python/docs/sdk/install.rst
diff --git a/python/docs/sdk/make.bat b/python/docs/sdk/make.bat
new file mode 100644
index 00000000..c1becfd1
--- /dev/null
+++ b/python/docs/sdk/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+
+:end
+popd
diff --git a/python/docs/source/quickstart.rst b/python/docs/sdk/quickstart.rst
similarity index 100%
rename from python/docs/source/quickstart.rst
rename to python/docs/sdk/quickstart.rst
diff --git a/python/docs/source/cli.rst b/python/docs/source/cli.rst
deleted file mode 100644
index 55c2ae44..00000000
--- a/python/docs/source/cli.rst
+++ /dev/null
@@ -1,93 +0,0 @@
-Command Line Interface
-======================
-
-The Python package installs the ``lightning`` command. The ``lightning-sdk``
-console script is an alias for the same command group.
-
-Use the CLI when you want to manage Lightning AI resources from a terminal,
-CI job, shell script, or other automation. The command reference below is
-generated from the Click command tree, so command groups, options, arguments,
-and examples stay aligned with the installed package.
-
-Install
--------
-
-Install or upgrade the Python package:
-
-.. code-block:: bash
-
- pip install lightning-sdk -U
-
-Authenticate
-------------
-
-For interactive use, sign in with:
-
-.. code-block:: bash
-
- lightning login
-
-For non-interactive environments, configure credentials through environment
-variables instead:
-
-.. code-block:: bash
-
- export LIGHTNING_USER_ID=your-user-id
- export LIGHTNING_API_KEY=your-api-key
-
-Usage
------
-
-Run a command group directly:
-
-.. code-block:: bash
-
- lightning [command]
-
-Every command and subcommand exposes help from the same Click definitions used
-by this reference:
-
-.. code-block:: bash
-
- lightning COMMAND --help
-
-Common Workflows
-----------------
-
-- Develop interactively with :doc:`cli/studio`.
-- Submit and inspect training or batch work with :doc:`cli/job` and
- :doc:`cli/mmt`.
-- Build and operate inference services with :doc:`cli/deployment` and
- :doc:`cli/model`.
-- Move data and artifacts with :doc:`cli/file`, :doc:`cli/folder`,
- :doc:`cli/container`, and :doc:`cli/cp`.
-- Configure accounts, organizations, teamspaces, cloud accounts, and SSH with
- :doc:`cli/config`, :doc:`cli/api-key`, and :doc:`cli/ssh`.
-- Manage lower-level sandbox sessions with :doc:`cli/sandbox`.
-
-Command Groups
---------------
-
-.. toctree::
- :maxdepth: 1
-
- cli/config
- cli/job
- cli/mmt
- cli/machine
- cli/deployment
- cli/container
- cli/model
- cli/api-key
- cli/file
- cli/folder
- cli/ssh
- cli/studio
- cli/sandbox
- cli/base-studio
- cli/license
- cli/cp
-
-.. click:: lightning_sdk.cli.entrypoint:main_cli
- :prog: lightning
- :nested: short
diff --git a/python/docs/source/cli/api-key.rst b/python/docs/source/cli/api-key.rst
deleted file mode 100644
index 0ffcbc03..00000000
--- a/python/docs/source/cli/api-key.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-API Key
-=======
-
-.. click:: lightning_sdk.cli.groups:api_key
- :prog: lightning api-key
- :nested: full
diff --git a/python/docs/source/cli/base-studio.rst b/python/docs/source/cli/base-studio.rst
deleted file mode 100644
index f9cc7415..00000000
--- a/python/docs/source/cli/base-studio.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Base Studio
-===========
-
-.. click:: lightning_sdk.cli.groups:base_studio
- :prog: lightning base-studio
- :nested: full
diff --git a/python/docs/source/cli/config.rst b/python/docs/source/cli/config.rst
deleted file mode 100644
index e33ec115..00000000
--- a/python/docs/source/cli/config.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Config
-======
-
-.. click:: lightning_sdk.cli.groups:config
- :prog: lightning config
- :nested: full
diff --git a/python/docs/source/cli/container.rst b/python/docs/source/cli/container.rst
deleted file mode 100644
index 4c4a8143..00000000
--- a/python/docs/source/cli/container.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Container
-=========
-
-.. click:: lightning_sdk.cli.groups:container
- :prog: lightning container
- :nested: full
diff --git a/python/docs/source/cli/cp.rst b/python/docs/source/cli/cp.rst
deleted file mode 100644
index 57598bb5..00000000
--- a/python/docs/source/cli/cp.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Copy
-====
-
-.. click:: lightning_sdk.cli.groups:cp
- :prog: lightning cp
- :nested: full
diff --git a/python/docs/source/cli/deployment.rst b/python/docs/source/cli/deployment.rst
deleted file mode 100644
index 1c6a1c65..00000000
--- a/python/docs/source/cli/deployment.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Deployment
-==========
-
-.. click:: lightning_sdk.cli.groups:deployment
- :prog: lightning deployment
- :nested: full
diff --git a/python/docs/source/cli/file.rst b/python/docs/source/cli/file.rst
deleted file mode 100644
index 5cf5cfe8..00000000
--- a/python/docs/source/cli/file.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-File
-====
-
-.. click:: lightning_sdk.cli.groups:file
- :prog: lightning file
- :nested: full
diff --git a/python/docs/source/cli/folder.rst b/python/docs/source/cli/folder.rst
deleted file mode 100644
index 35394def..00000000
--- a/python/docs/source/cli/folder.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Folder
-======
-
-.. click:: lightning_sdk.cli.groups:folder
- :prog: lightning folder
- :nested: full
diff --git a/python/docs/source/cli/job.rst b/python/docs/source/cli/job.rst
deleted file mode 100644
index 1741d463..00000000
--- a/python/docs/source/cli/job.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Job
-===
-
-.. click:: lightning_sdk.cli.groups:job
- :prog: lightning job
- :nested: full
diff --git a/python/docs/source/cli/license.rst b/python/docs/source/cli/license.rst
deleted file mode 100644
index 22ed5662..00000000
--- a/python/docs/source/cli/license.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-License
-=======
-
-.. click:: lightning_sdk.cli.groups:license
- :prog: lightning license
- :nested: full
diff --git a/python/docs/source/cli/machine.rst b/python/docs/source/cli/machine.rst
deleted file mode 100644
index c940cc95..00000000
--- a/python/docs/source/cli/machine.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Machine
-=======
-
-.. click:: lightning_sdk.cli.groups:machine
- :prog: lightning machine
- :nested: full
diff --git a/python/docs/source/cli/mmt.rst b/python/docs/source/cli/mmt.rst
deleted file mode 100644
index 80819f7b..00000000
--- a/python/docs/source/cli/mmt.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Multi-Machine Training
-======================
-
-.. click:: lightning_sdk.cli.groups:mmt
- :prog: lightning mmt
- :nested: full
diff --git a/python/docs/source/cli/model.rst b/python/docs/source/cli/model.rst
deleted file mode 100644
index c5297b4d..00000000
--- a/python/docs/source/cli/model.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Model
-=====
-
-.. click:: lightning_sdk.cli.groups:model
- :prog: lightning model
- :nested: full
diff --git a/python/docs/source/cli/sandbox.rst b/python/docs/source/cli/sandbox.rst
deleted file mode 100644
index 7069759d..00000000
--- a/python/docs/source/cli/sandbox.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Sandbox
-=======
-
-.. click:: lightning_sdk.cli.groups:sandbox
- :prog: lightning sandbox
- :nested: full
diff --git a/python/docs/source/cli/ssh.rst b/python/docs/source/cli/ssh.rst
deleted file mode 100644
index 78b058c2..00000000
--- a/python/docs/source/cli/ssh.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-SSH
-===
-
-.. click:: lightning_sdk.cli.groups:ssh
- :prog: lightning ssh
- :nested: full
diff --git a/python/docs/source/cli/studio.rst b/python/docs/source/cli/studio.rst
deleted file mode 100644
index d8b9d936..00000000
--- a/python/docs/source/cli/studio.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Studio
-======
-
-.. click:: lightning_sdk.cli.groups:studio
- :prog: lightning studio
- :nested: full
diff --git a/python/pyproject.toml b/python/pyproject.toml
index a51b62e3..36fea1cf 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -50,7 +50,7 @@ serve = ["litserve>=0.2.5"]
[dependency-groups]
docs = [
"sphinx>=8.0,<9.0",
- "sphinx-click>=6.2,<7.0",
+ "click-extra[sphinx]>=8.3,<9.0",
"myst-parser>=5.0.0",
"docutils>=0.20",
"sphinx-autodoc-typehints>=1.16",