diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 70f4b22..164178d 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -46,11 +46,11 @@ jobs: python -m pip install .[docs] - name: Build Sphinx site run: | - sphinx-build -b html docs/ docs/_build/html + sphinx-build -b html docs/sphinx docs/sphinx/_build/html - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: docs/_build/html + path: docs/sphinx/_build/html deploy: environment: diff --git a/.gitignore b/.gitignore index 97b2d3a..94ff718 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,7 @@ instance/ .scrapy # Sphinx documentation -docs/_build/ +docs/*/_build/ # PyBuilder .pybuilder/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 916b746..500b97d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,26 +82,30 @@ uv pip install .[docs] Using the Makefile generated by Sphinx: ```bash -make -C docs html +make -C docs/sphinx html ``` Or directly with sphinx-build: ```bash -sphinx-build -b html docs/ docs/_build/html +sphinx-build -b html docs/sphinx docs/sphinx/_build/html ``` Open the result in your browser: ```bash # Linux -xdg-open docs/_build/html/index.html +xdg-open docs/sphinx/_build/html/index.html + +# Or serve locally +make -C docs/sphinx serve +# Opens at http://localhost:8000/ ``` Clean build artifacts: ```bash -make -C docs clean +make -C docs/sphinx clean ``` ### Publishing (CI) diff --git a/REUSE.toml b/REUSE.toml index 83eaf08..bce00f8 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -45,3 +45,32 @@ path = "**/*.ico" SPDX-FileCopyrightText = "2025 InOrbit, Inc." SPDX-License-Identifier = "MIT" SPDX-FileComment = "ICO assets" + +# Documentation content files (portable between Sphinx and Mintlify) +# These use YAML frontmatter instead of HTML comments for Mintlify compatibility +[[annotations]] +path = "docs/contents/**/*.md" +SPDX-FileCopyrightText = "2025 InOrbit, Inc." +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Documentation content (Mintlify-compatible)" + +# Documentation JSON config +[[annotations]] +path = "docs/contents/docs.json" +SPDX-FileCopyrightText = "2025 InOrbit, Inc." +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Mintlify navigation configuration" + +# Mintlify build Makefile (has SPDX header but adding for completeness) +[[annotations]] +path = "docs/mintlify/Makefile" +SPDX-FileCopyrightText = "2025 InOrbit, Inc." +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Mintlify build configuration" + +# Sphinx wrapper files (use {include} directives, minimal content) +[[annotations]] +path = "docs/sphinx/**/*.md" +SPDX-FileCopyrightText = "2025 InOrbit, Inc." +SPDX-License-Identifier = "MIT" +SPDX-FileComment = "Sphinx documentation wrappers" diff --git a/docs/_static/favicon.ico b/docs/contents/_static/favicon.ico similarity index 100% rename from docs/_static/favicon.ico rename to docs/contents/_static/favicon.ico diff --git a/docs/_static/inorbit-logo-black.svg b/docs/contents/_static/inorbit-logo-black.svg similarity index 100% rename from docs/_static/inorbit-logo-black.svg rename to docs/contents/_static/inorbit-logo-black.svg diff --git a/docs/_static/inorbit-logo-white.svg b/docs/contents/_static/inorbit-logo-white.svg similarity index 100% rename from docs/_static/inorbit-logo-white.svg rename to docs/contents/_static/inorbit-logo-white.svg diff --git a/docs/_static/mark-github.svg b/docs/contents/_static/mark-github.svg similarity index 100% rename from docs/_static/mark-github.svg rename to docs/contents/_static/mark-github.svg diff --git a/docs/configuration.md b/docs/contents/configuration.md similarity index 95% rename from docs/configuration.md rename to docs/contents/configuration.md index 7d08933..2f689cf 100644 --- a/docs/configuration.md +++ b/docs/contents/configuration.md @@ -1,10 +1,7 @@ - - -# Configuration +--- +title: "Configuration" +description: "Configuration models and file formats for connectors" +--- The `inorbit-connector` framework uses Pydantic models for configuration, providing validation and type safety. @@ -12,7 +9,7 @@ The `inorbit-connector` framework uses Pydantic models for configuration, provid The main configuration class is `ConnectorConfig`, which contains all settings for your connector. It includes a `fleet` field containing a list of `RobotConfig` entries. -Connectors should subclass `inorbit_connector.models.ConnectorConfig` and define a `connector_config` field that contains the configuration for the connector. For more details see the {ref}`creating-a-custom-configuration` section below. +Connectors should subclass `inorbit_connector.models.ConnectorConfig` and define a `connector_config` field that contains the configuration for the connector. For more details see the [Creating a Custom Configuration](#creating-a-custom-configuration) section below. ### Key Fields diff --git a/docs/contents/docs.json b/docs/contents/docs.json new file mode 100644 index 0000000..d89530f --- /dev/null +++ b/docs/contents/docs.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://mintlify.com/docs.json", + "navigation": { + "products": [ + { + "product": "Ground Control", + "tabs": [ + { + "tab": "Connecting Devices", + "pages": [ + { + "group": "Connector Framework", + "pages": [ + "ground-control/robot-integration/connector-framework/index", + "ground-control/robot-integration/connector-framework/getting-started", + { + "group": "Usage", + "pages": [ + "ground-control/robot-integration/connector-framework/usage/index", + "ground-control/robot-integration/connector-framework/usage/single-robot", + "ground-control/robot-integration/connector-framework/usage/fleet", + "ground-control/robot-integration/connector-framework/usage/commands-handling" + ] + }, + "ground-control/robot-integration/connector-framework/configuration", + "ground-control/robot-integration/connector-framework/publishing", + { + "group": "Specification", + "pages": [ + "ground-control/robot-integration/connector-framework/specification/index", + "ground-control/robot-integration/connector-framework/specification/connector", + "ground-control/robot-integration/connector-framework/specification/models", + "ground-control/robot-integration/connector-framework/specification/commands", + "ground-control/robot-integration/connector-framework/specification/utils", + "ground-control/robot-integration/connector-framework/specification/logging" + ] + } + ] + } + ] + } + ] + } + ] + } +} + + diff --git a/docs/getting-started.md b/docs/contents/getting-started.md similarity index 95% rename from docs/getting-started.md rename to docs/contents/getting-started.md index 021d7b0..90b35c4 100644 --- a/docs/getting-started.md +++ b/docs/contents/getting-started.md @@ -1,10 +1,7 @@ - - -# Getting Started +--- +title: "Getting Started" +description: "Installation and setup for the InOrbit Connector Framework" +--- ## Installation diff --git a/docs/index.md b/docs/contents/index.md similarity index 76% rename from docs/index.md rename to docs/contents/index.md index 40b291b..4ed5672 100644 --- a/docs/index.md +++ b/docs/contents/index.md @@ -1,10 +1,7 @@ - - -# inorbit-connector +--- +title: "Connector Framework" +description: "Python framework for developing InOrbit robot connectors" +--- A Python framework for developing *connectors* for the [InOrbit](https://inorbit.ai/) RobOps ecosystem. @@ -31,21 +28,10 @@ Both connector types provide: - Python 3.10 or later - InOrbit account ([it's free to sign up!](https://control.inorbit.ai)) -```{toctree} -:maxdepth: 2 -:hidden: - -getting-started -specification/index -usage/index -configuration -publishing -``` - ## Documentation Sections -- **Getting Started**: Installation, environment setup, and running a connector -- **Specification**: Package-level specification of the connector API (what you can call and what you can override) -- **Usage**: Detailed guides for implementing single-robot and fleet connectors -- **Configuration**: Complete connector customization guide -- **Publishing**: How to publish data to InOrbit +- **Getting Started**: Installation, environment setup, and running a connector ([Getting Started](getting-started)) +- **Specification**: Package-level specification of the connector API ([Specification](specification/index)) +- **Usage**: Detailed guides for implementing single-robot and fleet connectors ([Usage](usage/index)) +- **Configuration**: Connector configuration models and file formats ([Configuration](configuration)) +- **Publishing**: How to publish data to InOrbit ([Publishing](publishing)) diff --git a/docs/publishing.md b/docs/contents/publishing.md similarity index 97% rename from docs/publishing.md rename to docs/contents/publishing.md index 39960fb..09189aa 100644 --- a/docs/publishing.md +++ b/docs/contents/publishing.md @@ -1,10 +1,7 @@ - - -# Publishing Data +--- +title: "Publishing Data" +description: "How to publish robot data to InOrbit" +--- The connector framework provides methods to publish various types of data to InOrbit. Both single-robot (`Connector`) and fleet (`FleetConnector`) connectors support publishing, with fleet connectors using methods prefixed with `publish_robot_*` that require a `robot_id` parameter. diff --git a/docs/specification/commands.md b/docs/contents/specification/commands.md similarity index 91% rename from docs/specification/commands.md rename to docs/contents/specification/commands.md index 728df21..148d395 100644 --- a/docs/specification/commands.md +++ b/docs/contents/specification/commands.md @@ -1,10 +1,7 @@ - - -# Commands utilities +--- +title: "Commands Utilities" +description: "Command handling utilities specification" +--- This page specifies command handling helpers from `inorbit_connector.commands`. @@ -46,7 +43,7 @@ These classes support type-safe parsing and validation of structured command par - `CommandModel` is a Pydantic `BaseModel` configured with `extra="forbid"`, and converts `ValidationError` into `CommandFailure`. - `ExcludeUnsetMixin` changes `model_dump()` default behavior to `exclude_unset=True` (useful when you want to emit only explicitly-provided fields). -See {doc}`../usage/commands-handling` for end-to-end usage patterns. +See [Commands Handling](../usage/commands-handling) for end-to-end usage patterns. ## Note: re-exports via `inorbit_connector.connector` diff --git a/docs/specification/connector.md b/docs/contents/specification/connector.md similarity index 80% rename from docs/specification/connector.md rename to docs/contents/specification/connector.md index 2285541..813dafa 100644 --- a/docs/specification/connector.md +++ b/docs/contents/specification/connector.md @@ -1,10 +1,7 @@ - - -# Connector API +--- +title: "Connector API" +description: "Connector and FleetConnector class specifications" +--- This page specifies the connector base classes you subclass to build connectors. @@ -12,7 +9,7 @@ This page specifies the connector base classes you subclass to build connectors. `inorbit_connector.connector.FleetConnector` is the base class for connectors that manage multiple robots. -(spec-connector-fleetconnector-connect)= + ### `_connect()` **Override.** @@ -25,7 +22,7 @@ Typical responsibilities: - Optionally fetch the fleet membership and call `update_fleet()` **before** sessions are created. - Start background polling tasks that keep fresh state for all robots. -(spec-connector-fleetconnector-execution-loop)= + ### `_execution_loop()` **Override.** @@ -37,14 +34,14 @@ Typical responsibilities: - For each robot in `self.robot_ids`, fetch the latest state (often from background polling state) and publish data via the `publish_robot_*` methods. - Handle exceptions inside the loop when possible (the framework logs and continues on exceptions). -(spec-connector-fleetconnector-disconnect)= + ### `_disconnect()` **Override.** Called once during shutdown, after Edge SDK sessions are disconnected. Use this to stop polling tasks, close sockets, and release resources. -(spec-connector-fleetconnector-command-handler)= + ### `_inorbit_robot_command_handler(robot_id, command_name, args, options)` **Override.** @@ -55,9 +52,9 @@ Contract: - `options["result_function"](...)` must be called to report success/failure, or you may raise `CommandFailure` for structured failure reporting. -See {doc}`../usage/commands-handling` for the full command result contract. +See [Commands Handling](../usage/commands-handling) for the full command result contract. -(spec-connector-fleetconnector-fetch-robot-map)= + ### `fetch_robot_map(robot_id, frame_id) -> MapConfigTemp | None` **Optional override.** @@ -70,14 +67,14 @@ If `publish_robot_pose()` references a `frame_id` that is not present in `config Return `None` if the map can’t be fetched. -(spec-connector-fleetconnector-is-online)= + ### `_is_fleet_robot_online(robot_id) -> bool` **Optional override.** The Edge SDK uses this callback to determine if a robot should be considered online. Default implementation returns `True`. -(spec-connector-fleetconnector-lifecycle)= + ### `start()` / `join()` / `stop()` **Callable.** @@ -86,7 +83,7 @@ The Edge SDK uses this callback to determine if a robot should be considered onl - `join()` blocks until the background thread exits. - `stop()` signals the event loop to stop and waits briefly for shutdown. -(spec-connector-fleetconnector-fleet-management)= + ### `robot_ids` / `update_fleet(fleet)` **Callable.** @@ -96,14 +93,14 @@ The Edge SDK uses this callback to determine if a robot should be considered onl This is designed to be used during `_connect()` when your fleet membership comes from an external system. -(spec-connector-fleetconnector-publish-robot-pose)= + ### `publish_robot_pose(robot_id, x, y, yaw, frame_id=None, **kwargs)` **Callable.** Publishes pose for one robot. If the `frame_id` differs from the last published `frame_id` for that robot, the framework triggers `publish_robot_map(..., is_update=True)`. -(spec-connector-fleetconnector-publish-robot-map)= + ### `publish_robot_map(robot_id, frame_id, is_update=False)` **Callable.** @@ -111,22 +108,22 @@ Publishes pose for one robot. If the `frame_id` differs from the last published - If `frame_id` exists in `config.maps`, publishes the configured map to InOrbit. - Otherwise schedules an async fetch via `fetch_robot_map()` (see above). -(spec-connector-fleetconnector-publish-robot-odometry)= + ### `publish_robot_odometry(robot_id, **kwargs)` **Callable.** Publishes odometry data for one robot. -(spec-connector-fleetconnector-publish-robot-key-values)= + ### `publish_robot_key_values(robot_id, **kwargs)` **Callable.** Publishes key-value telemetry for one robot. -(spec-connector-fleetconnector-publish-robot-system-stats)= + ### `publish_robot_system_stats(robot_id, **kwargs)` **Callable.** Publishes system stats for one robot. -(spec-connector-fleetconnector-get-robot-session)= + ### `_get_robot_session(robot_id) -> RobotSession` **Callable (advanced).** @@ -137,7 +134,7 @@ Returns the underlying Edge SDK session for `robot_id`. Use this if you need Edg `inorbit_connector.connector.Connector` is a single-robot specialization of `FleetConnector`. It selects a single robot out of the fleet config and provides convenience wrappers that omit `robot_id`. -(spec-connector-connector-lifecycle-hooks)= + ### Lifecycle hooks **Override.** Same intent as fleet: @@ -146,28 +143,28 @@ Returns the underlying Edge SDK session for `robot_id`. Use this if you need Edg - `_execution_loop()` - `_disconnect()` -(spec-connector-connector-command-handler)= + ### `_inorbit_command_handler(command_name, args, options)` **Override.** Single-robot command handler. It is called through the fleet-level handler internally, but without requiring you to accept `robot_id`. -(spec-connector-connector-fetch-map)= + ### `fetch_map(frame_id) -> MapConfigTemp | None` **Optional override.** Single-robot convenience for map fetching. The framework uses it by delegating `fetch_robot_map()` to `fetch_map()`. -(spec-connector-connector-is-online)= + ### `_is_robot_online() -> bool` **Optional override.** Single-robot convenience for online status. The fleet-level online check delegates to this method. -(spec-connector-connector-publishing)= + ### Publishing wrappers **Callable.** Single-robot wrappers over the fleet publishing methods: @@ -177,7 +174,7 @@ Single-robot convenience for online status. The fleet-level online check delegat - `publish_key_values(...)` - `publish_system_stats(...)` -(spec-connector-connector-get-session)= + ### `_get_session() -> RobotSession` **Callable (advanced).** diff --git a/docs/specification/index.md b/docs/contents/specification/index.md similarity index 54% rename from docs/specification/index.md rename to docs/contents/specification/index.md index abcd99e..6bd9449 100644 --- a/docs/specification/index.md +++ b/docs/contents/specification/index.md @@ -1,10 +1,7 @@ - - -# Specification +--- +title: "Specification" +description: "Public API specification for the inorbit-connector package" +--- This section specifies the public surface of the `inorbit-connector` package: what you are expected to **override** when implementing a connector, and what you can **call** at runtime. @@ -38,11 +35,11 @@ For map handling, `publish_pose()` / `publish_robot_pose()` automatically trigge For narrative guides, see: -- Single robot: {doc}`../usage/single-robot` -- Fleet: {doc}`../usage/fleet` -- Commands: {doc}`../usage/commands-handling` -- Publishing: {doc}`../publishing` -- Configuration: {doc}`../configuration` +- Single robot: [Single-Robot Connector](../usage/single-robot) +- Fleet: [Fleet Connector](../usage/fleet) +- Commands: [Commands Handling](../usage/commands-handling) +- Publishing: [Publishing Data](../publishing) +- Configuration: [Configuration](../configuration) ## API surface (callable + overridable) @@ -50,47 +47,42 @@ The table below lists package-defined symbols meant for direct use (call) or ext | Kind | Symbol | Purpose | Details | | --- | --- | --- | --- | -| Override | `FleetConnector._connect()` | Connect to external services (fleet manager, robot backends) before sessions initialize. | {ref}`Details ` | -| Override | `FleetConnector._execution_loop()` | Periodic loop; publish telemetry for each robot. | {ref}`Details ` | -| Override | `FleetConnector._disconnect()` | Shutdown external services and release resources. | {ref}`Details ` | -| Override | `FleetConnector._inorbit_robot_command_handler()` | Handle commands for a specific `robot_id`. | {ref}`Details ` | -| Override (optional) | `FleetConnector.fetch_robot_map()` | Fetch a missing map (bytes + metadata) when publishing pose refers to an unknown `frame_id`. | {ref}`Details ` | -| Override (optional) | `FleetConnector._is_fleet_robot_online()` | Provide robot online status; used by Edge SDK callback. | {ref}`Details ` | -| Call | `FleetConnector.start()` / `join()` / `stop()` | Run and control the connector lifecycle. | {ref}`Details ` | -| Call | `FleetConnector.update_fleet()` / `FleetConnector.robot_ids` | Update fleet configuration (typically during `_connect()`) and access robot IDs. | {ref}`Details ` | -| Call | `FleetConnector.publish_robot_pose()` | Publish pose; triggers map publish when `frame_id` changes. | {ref}`Details ` | -| Call | `FleetConnector.publish_robot_map()` | Publish map metadata/image from configured maps (or after fetch). | {ref}`Details ` | -| Call | `FleetConnector.publish_robot_odometry()` | Publish odometry. | {ref}`Details ` | -| Call | `FleetConnector.publish_robot_key_values()` | Publish key-value telemetry. | {ref}`Details ` | -| Call | `FleetConnector.publish_robot_system_stats()` | Publish system stats telemetry. | {ref}`Details ` | -| Call (advanced) | `FleetConnector._get_robot_session()` | Access the underlying Edge SDK `RobotSession` for a specific robot. | {ref}`Details ` | -| Override | `Connector._connect()` / `_execution_loop()` / `_disconnect()` | Same lifecycle hooks as fleet, for a single robot. | {ref}`Details ` | -| Override | `Connector._inorbit_command_handler()` | Handle commands for the single robot. | {ref}`Details ` | -| Override (optional) | `Connector.fetch_map()` | Fetch a missing map for the current robot when pose references an unknown `frame_id`. | {ref}`Details ` | -| Override (optional) | `Connector._is_robot_online()` | Provide online status for the current robot. | {ref}`Details ` | -| Call | `Connector.publish_pose()` / `publish_map()` | Publish pose/map for the current robot (map handling included). | {ref}`Details ` | -| Call | `Connector.publish_odometry()` / `publish_key_values()` / `publish_system_stats()` | Publish telemetry for the current robot. | {ref}`Details ` | -| Call (advanced) | `Connector._get_session()` | Access the underlying Edge SDK `RobotSession` for the current robot. | {ref}`Details ` | -| Type | `ConnectorConfig` | Base configuration model for connectors. | {ref}`Details ` | -| Type | `RobotConfig` | Per-robot configuration (robot_id + cameras). | {ref}`Details ` | -| Type | `MapConfig` / `MapConfigTemp` | Map configuration (file-backed vs in-memory bytes) used by map publishing/fetching. | {ref}`Details ` | -| Type | `LoggingConfig` / `LogLevels` | Logging configuration and log-level enum. | {ref}`Details ` | -| Call | `read_yaml()` | Load YAML configuration data (with deprecated `robot_id` selection support). | {ref}`Details ` | -| Type / Call | `CommandResultCode` / `CommandFailure` | Standard command result code enum and structured failure exception. | {ref}`Details ` | -| Call | `parse_custom_command_args()` | Parse custom-command args (RunScript action payload) into `(script_name, params)`. | {ref}`Details ` | -| Type | `CommandModel` / `ExcludeUnsetMixin` | Pydantic-based command argument validation utilities. | {ref}`Details ` | -| Call | `setup_logger()` | Configure logging from `LoggingConfig`. | {ref}`Details ` | -| Type | `ConditionalColoredFormatter` | Optional `colorlog`-backed formatter used by the default logging config. | {ref}`Details ` | - -```{toctree} -:maxdepth: 2 -:hidden: - -connector -models -commands -utils -logging -``` - - +| Override | `FleetConnector._connect()` | Connect to external services (fleet manager, robot backends) before sessions initialize. | [Details](connector.md#spec-connector-fleetconnector-connect) | +| Override | `FleetConnector._execution_loop()` | Periodic loop; publish telemetry for each robot. | [Details](connector.md#spec-connector-fleetconnector-execution-loop) | +| Override | `FleetConnector._disconnect()` | Shutdown external services and release resources. | [Details](connector.md#spec-connector-fleetconnector-disconnect) | +| Override | `FleetConnector._inorbit_robot_command_handler()` | Handle commands for a specific `robot_id`. | [Details](connector.md#spec-connector-fleetconnector-command-handler) | +| Override (optional) | `FleetConnector.fetch_robot_map()` | Fetch a missing map (bytes + metadata) when publishing pose refers to an unknown `frame_id`. | [Details](connector.md#spec-connector-fleetconnector-fetch-robot-map) | +| Override (optional) | `FleetConnector._is_fleet_robot_online()` | Provide robot online status; used by Edge SDK callback. | [Details](connector.md#spec-connector-fleetconnector-is-online) | +| Call | `FleetConnector.start()` / `join()` / `stop()` | Run and control the connector lifecycle. | [Details](connector.md#spec-connector-fleetconnector-lifecycle) | +| Call | `FleetConnector.update_fleet()` / `FleetConnector.robot_ids` | Update fleet configuration (typically during `_connect()`) and access robot IDs. | [Details](connector.md#spec-connector-fleetconnector-fleet-management) | +| Call | `FleetConnector.publish_robot_pose()` | Publish pose; triggers map publish when `frame_id` changes. | [Details](connector.md#spec-connector-fleetconnector-publish-robot-pose) | +| Call | `FleetConnector.publish_robot_map()` | Publish map metadata/image from configured maps (or after fetch). | [Details](connector.md#spec-connector-fleetconnector-publish-robot-map) | +| Call | `FleetConnector.publish_robot_odometry()` | Publish odometry. | [Details](connector.md#spec-connector-fleetconnector-publish-robot-odometry) | +| Call | `FleetConnector.publish_robot_key_values()` | Publish key-value telemetry. | [Details](connector.md#spec-connector-fleetconnector-publish-robot-key-values) | +| Call | `FleetConnector.publish_robot_system_stats()` | Publish system stats telemetry. | [Details](connector.md#spec-connector-fleetconnector-publish-robot-system-stats) | +| Call (advanced) | `FleetConnector._get_robot_session()` | Access the underlying Edge SDK `RobotSession` for a specific robot. | [Details](connector.md#spec-connector-fleetconnector-get-robot-session) | +| Override | `Connector._connect()` / `_execution_loop()` / `_disconnect()` | Same lifecycle hooks as fleet, for a single robot. | [Details](connector.md#spec-connector-connector-lifecycle-hooks) | +| Override | `Connector._inorbit_command_handler()` | Handle commands for the single robot. | [Details](connector.md#spec-connector-connector-command-handler) | +| Override (optional) | `Connector.fetch_map()` | Fetch a missing map for the current robot when pose references an unknown `frame_id`. | [Details](connector.md#spec-connector-connector-fetch-map) | +| Override (optional) | `Connector._is_robot_online()` | Provide online status for the current robot. | [Details](connector.md#spec-connector-connector-is-online) | +| Call | `Connector.publish_pose()` / `publish_map()` | Publish pose/map for the current robot (map handling included). | [Details](connector.md#spec-connector-connector-publishing) | +| Call | `Connector.publish_odometry()` / `publish_key_values()` / `publish_system_stats()` | Publish telemetry for the current robot. | [Details](connector.md#spec-connector-connector-publishing) | +| Call (advanced) | `Connector._get_session()` | Access the underlying Edge SDK `RobotSession` for the current robot. | [Details](connector.md#spec-connector-connector-get-session) | +| Type | `ConnectorConfig` | Base configuration model for connectors. | [Details](models.md#spec-models-connectorconfig) | +| Type | `RobotConfig` | Per-robot configuration (robot_id + cameras). | [Details](models.md#spec-models-robotconfig) | +| Type | `MapConfig` / `MapConfigTemp` | Map configuration (file-backed vs in-memory bytes) used by map publishing/fetching. | [Details](models.md#spec-models-mapconfig) | +| Type | `LoggingConfig` / `LogLevels` | Logging configuration and log-level enum. | [Details](logging.md#spec-logging-loggingconfig) | +| Call | `read_yaml()` | Load YAML configuration data (with deprecated `robot_id` selection support). | [Details](utils.md#spec-utils-readyaml) | +| Type / Call | `CommandResultCode` / `CommandFailure` | Standard command result code enum and structured failure exception. | [Details](commands.md#spec-commands-commandfailure) | +| Call | `parse_custom_command_args()` | Parse custom-command args (RunScript action payload) into `(script_name, params)`. | [Details](commands.md#spec-commands-parse-custom-command-args) | +| Type | `CommandModel` / `ExcludeUnsetMixin` | Pydantic-based command argument validation utilities. | [Details](commands.md#spec-commands-commandmodel) | +| Call | `setup_logger()` | Configure logging from `LoggingConfig`. | [Details](logging.md#spec-logging-setup-logger) | +| Type | `ConditionalColoredFormatter` | Optional `colorlog`-backed formatter used by the default logging config. | [Details](logging.md#spec-logging-conditional-colored-formatter) | + +## Pages + +- [Connector API](connector) +- [Models (configuration)](models) +- [Commands utilities](commands) +- [Utilities](utils) +- [Logging](logging) diff --git a/docs/specification/logging.md b/docs/contents/specification/logging.md similarity index 92% rename from docs/specification/logging.md rename to docs/contents/specification/logging.md index a075645..b6d3ae4 100644 --- a/docs/specification/logging.md +++ b/docs/contents/specification/logging.md @@ -1,10 +1,7 @@ - - -# Logging +--- +title: "Logging" +description: "Logging configuration specification" +--- This page specifies logging-related helpers from `inorbit_connector.logging`. diff --git a/docs/specification/models.md b/docs/contents/specification/models.md similarity index 92% rename from docs/specification/models.md rename to docs/contents/specification/models.md index 3e9878d..3ded9e5 100644 --- a/docs/specification/models.md +++ b/docs/contents/specification/models.md @@ -1,10 +1,7 @@ - - -# Models (configuration) +--- +title: "Models" +description: "Configuration models specification" +--- (configuration) This page specifies the configuration models defined by `inorbit_connector.models`. @@ -58,7 +55,7 @@ Logging configuration used by the connector at startup: - `log_level`: optional override for the root logger level. - `defaults`: dictionary passed to the logging config (e.g. `log_file`). -See {ref}`spec-logging-setup-logger` for how it is applied. +See [setup_logger()](logging.md#spec-logging-setup-logger) for how it is applied. ## Deprecated: `InorbitConnectorConfig` diff --git a/docs/specification/utils.md b/docs/contents/specification/utils.md similarity index 85% rename from docs/specification/utils.md rename to docs/contents/specification/utils.md index 1d556e6..f9947b7 100644 --- a/docs/specification/utils.md +++ b/docs/contents/specification/utils.md @@ -1,10 +1,7 @@ - - -# Utilities +--- +title: "Utilities" +description: "Utility functions specification" +--- This page specifies utilities from `inorbit_connector.utils`. @@ -22,7 +19,7 @@ Behavior: Notes: -- New configurations should follow the `ConnectorConfig` schema described in {doc}`../configuration`. +- New configurations should follow the `ConnectorConfig` schema described in [Configuration](../configuration). ## Constants diff --git a/docs/usage/commands-handling.md b/docs/contents/usage/commands-handling.md similarity index 97% rename from docs/usage/commands-handling.md rename to docs/contents/usage/commands-handling.md index 5a19131..3cffa52 100644 --- a/docs/usage/commands-handling.md +++ b/docs/contents/usage/commands-handling.md @@ -1,10 +1,7 @@ - - -# Commands Handling +--- +title: "Commands Handling" +description: "How to handle commands from InOrbit" +--- Commands from InOrbit are automatically routed to your `_inorbit_robot_command_handler()` method in the case of a fleet connector, or to your `_inorbit_command_handler()` method in the case of a single-robot connector. @@ -145,7 +142,7 @@ async def _inorbit_robot_command_handler( ) ``` -It is recommended to complement its use with the `CommandModel` class (see {ref}`using-commandmodel-for-type-safe-argument-parsing`) for safe type validation and parsing. +It is recommended to complement its use with the `CommandModel` class (see [Using CommandModel for Type-Safe Argument Parsing](#using-commandmodel-for-type-safe-argument-parsing)) for safe type validation and parsing. (using-commandmodel-for-type-safe-argument-parsing)= ## Using `CommandModel` for Type-Safe Argument Parsing @@ -228,7 +225,7 @@ command = CommandQueueMission(**script_args) ``` The exception is automatically handled by the connector's command execution framework, so you don't need to catch `ValidationError` exceptions. -See the {ref}`reporting-failure` section for more details. +See the [Reporting Failure](#reporting-failure) section for more details. ### Excluding Unset Fields diff --git a/docs/usage/fleet.md b/docs/contents/usage/fleet.md similarity index 98% rename from docs/usage/fleet.md rename to docs/contents/usage/fleet.md index 04075e7..a9f933e 100644 --- a/docs/usage/fleet.md +++ b/docs/contents/usage/fleet.md @@ -1,10 +1,7 @@ - - -# Fleet Connector +--- +title: "Fleet Connector" +description: "Guide for implementing a fleet connector" +--- Subclass `inorbit_connector.connector.FleetConnector` to manage multiple robots simultaneously. diff --git a/docs/contents/usage/index.md b/docs/contents/usage/index.md new file mode 100644 index 0000000..da9d89c --- /dev/null +++ b/docs/contents/usage/index.md @@ -0,0 +1,10 @@ +--- +title: "Usage" +description: "Guides for implementing connectors" +--- + +## Guides + +- [Single-Robot Connector](single-robot) +- [Fleet Connector](fleet) +- [Commands Handling](commands-handling) diff --git a/docs/usage/single-robot.md b/docs/contents/usage/single-robot.md similarity index 98% rename from docs/usage/single-robot.md rename to docs/contents/usage/single-robot.md index feac2e2..bcc6a7c 100644 --- a/docs/usage/single-robot.md +++ b/docs/contents/usage/single-robot.md @@ -1,10 +1,7 @@ - - -# Single-Robot Connector +--- +title: "Single-Robot Connector" +description: "Guide for implementing a single-robot connector" +--- Subclass `inorbit_connector.connector.Connector` to create a connector for a single robot. diff --git a/docs/mintlify/Makefile b/docs/mintlify/Makefile new file mode 100644 index 0000000..d9207be --- /dev/null +++ b/docs/mintlify/Makefile @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2025 InOrbit, Inc. +# SPDX-License-Identifier: MIT + +# Makefile for building Mintlify-compatible docs from shared content + +CONTENT_DIR = ../contents +BUILD_DIR = _build + +.PHONY: build clean + +# Build Mintlify-compatible docs with absolute links +build: + @echo "Building Mintlify-compatible docs..." + @python build.py "$(CONTENT_DIR)" "$(BUILD_DIR)" + @echo "Done. Output in $(BUILD_DIR)/" + +# Clean build artifacts +clean: + @rm -rf "$(BUILD_DIR)" + @echo "Cleaned $(BUILD_DIR)/" + diff --git a/docs/mintlify/build.py b/docs/mintlify/build.py new file mode 100644 index 0000000..c778f7f --- /dev/null +++ b/docs/mintlify/build.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2025 InOrbit, Inc. +# SPDX-License-Identifier: MIT +"""Build Mintlify-compatible docs from the shared content files. + +This script: +1. Copies content files to the build directory +2. Converts relative links to absolute Mintlify routes +3. Prepares the output for the multirepo action +""" + +import re +import shutil +import sys +from pathlib import Path + +# Mintlify route prefix for connector framework docs +PREFIX = "/ground-control/robot-integration/connector-framework" + +# Pattern to match markdown links: [text](path) or [text](path#anchor) +# Excludes: absolute URLs (http://, https://), absolute paths (/...), and pure anchors (#...) +LINK_PATTERN = re.compile(r"\[([^\]]+)\]\((?!https?://|mailto:|/|#)([^)]+)\)") + + +def convert_link(match: re.Match, file_path: Path, content_root: Path) -> str: + """Convert a relative link to an absolute Mintlify route.""" + text = match.group(1) + target = match.group(2) + + # Get the directory of the current file relative to content root + file_dir = file_path.parent.relative_to(content_root) + + # Resolve the relative path + if target.startswith("../"): + # Go up directories + resolved = (file_dir / target).as_posix() + # Normalize path (remove ../) + parts = [] + for part in resolved.split("/"): + if part == "..": + if parts: + parts.pop() + elif part and part != ".": + parts.append(part) + resolved = "/".join(parts) + else: + # Same directory or subdirectory + if str(file_dir) == ".": + resolved = target + else: + resolved = f"{file_dir}/{target}".replace("./", "") + + # Strip .md extension for Mintlify routes + if resolved.endswith(".md"): + resolved = resolved[:-3] + # Also handle .md before anchors (e.g., "file.md#anchor" -> "file#anchor") + resolved = re.sub(r"\.md(#)", r"\1", resolved) + + # Build absolute Mintlify route + absolute_path = f"{PREFIX}/{resolved}" + + return f"[{text}]({absolute_path})" + + +def process_file(src_file: Path, dst_file: Path, content_root: Path) -> int: + """Process a single file, converting links. Returns count of conversions.""" + content = src_file.read_text(encoding="utf-8") + count = 0 + + def replace_link(match: re.Match) -> str: + nonlocal count + count += 1 + return convert_link(match, src_file, content_root) + + new_content = LINK_PATTERN.sub(replace_link, content) + + dst_file.parent.mkdir(parents=True, exist_ok=True) + dst_file.write_text(new_content, encoding="utf-8") + + return count + + +def main(): + if len(sys.argv) < 3: + print("Usage: build.py ", file=sys.stderr) + sys.exit(1) + + content_dir = Path(sys.argv[1]) + output_dir = Path(sys.argv[2]) + + if not content_dir.is_dir(): + print(f"Error: {content_dir} is not a directory", file=sys.stderr) + sys.exit(1) + + # Clean output directory + if output_dir.exists(): + shutil.rmtree(output_dir) + output_dir.mkdir(parents=True) + + total_links = 0 + total_files = 0 + + # Process all markdown files + for src_file in content_dir.rglob("*.md"): + rel_path = src_file.relative_to(content_dir) + dst_file = output_dir / rel_path + + links = process_file(src_file, dst_file, content_dir) + if links > 0: + print(f" {rel_path}: {links} links converted") + total_links += links + total_files += 1 + + # Copy non-markdown files (images, etc.) + for src_file in content_dir.rglob("*"): + if src_file.is_file() and src_file.suffix != ".md": + rel_path = src_file.relative_to(content_dir) + dst_file = output_dir / rel_path + dst_file.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(src_file, dst_file) + + print(f"\nTotal: {total_files} files, {total_links} links converted") + print(f"Output: {output_dir}") + + +if __name__ == "__main__": + main() diff --git a/docs/Makefile b/docs/sphinx/Makefile similarity index 80% rename from docs/Makefile rename to docs/sphinx/Makefile index e409574..a02f320 100644 --- a/docs/Makefile +++ b/docs/sphinx/Makefile @@ -16,7 +16,12 @@ BUILDDIR = _build help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile +.PHONY: help Makefile serve + +# Serve locally for testing +serve: html + @echo "Serving at http://localhost:8000/" + @cd "$(BUILDDIR)/html" && python -m http.server 8000 # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). diff --git a/docs/conf.py b/docs/sphinx/conf.py similarity index 59% rename from docs/conf.py rename to docs/sphinx/conf.py index 7bacee7..f3e5394 100644 --- a/docs/conf.py +++ b/docs/sphinx/conf.py @@ -7,23 +7,31 @@ # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -import os +import re from pathlib import Path -import sys -from inorbit_connector import __version__ -# Ensure the package can be imported so we can read its version -sys.path.insert(0, os.path.abspath("..")) +def _read_package_version() -> str: + """Read __version__ from the package without importing it. + + This keeps the docs build independent from the environment (no need to have the + package importable). + """ + repo_root = Path(__file__).resolve().parents[2] + init_py = repo_root / "inorbit_connector" / "__init__.py" + text = init_py.read_text(encoding="utf-8") + match = re.search(r'^__version__\s*=\s*"([^"]+)"\s*$', text, re.MULTILINE) + if not match: + raise RuntimeError("Could not determine package version from __init__.py") + return match.group(1) + project = "inorbit-connector" copyright = "2025, InOrbit, Inc." author = "InOrbit, Inc." -release = __version__ -version = __version__ +release = _read_package_version() +version = release # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration @@ -39,11 +47,18 @@ templates_path = ["_templates"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +root_doc = "index" + +# The portable docs use explicit HTML anchors (e.g. ) for deep +# linking compatibility across doc systems. MyST can't validate those anchors, +# and will emit "xref_missing" warnings for links that include a fragment. +suppress_warnings = ["myst.xref_missing"] + # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "furo" -html_static_path = ["_static"] +html_static_path = ["../contents/_static"] # Support both Markdown (MyST) and reStructuredText sources source_suffix = { @@ -53,7 +68,7 @@ def _read_static_svg(filename: str) -> str: - static_dir = Path(__file__).resolve().parent / "_static" + static_dir = Path(__file__).resolve().parents[1] / "contents" / "_static" return (static_dir / filename).read_text(encoding="utf-8") @@ -63,7 +78,7 @@ def _read_static_svg(filename: str) -> str: "dark_logo": "inorbit-logo-white.svg", "source_repository": "https://github.com/inorbit-ai/inorbit-connector-python/", "source_branch": "main", - "source_directory": "docs/", + "source_directory": "docs/contents/", "top_of_page_buttons": ["view", "edit"], "footer_icons": [ { @@ -76,4 +91,4 @@ def _read_static_svg(filename: str) -> str: } # Favicon -html_favicon = "_static/favicon.ico" +html_favicon = "../contents/_static/favicon.ico" diff --git a/docs/sphinx/configuration.md b/docs/sphinx/configuration.md new file mode 100644 index 0000000..34502bd --- /dev/null +++ b/docs/sphinx/configuration.md @@ -0,0 +1,5 @@ +# Configuration + +```{include} ../contents/configuration.md +:start-line: 5 +``` diff --git a/docs/sphinx/getting-started.md b/docs/sphinx/getting-started.md new file mode 100644 index 0000000..e204688 --- /dev/null +++ b/docs/sphinx/getting-started.md @@ -0,0 +1,6 @@ +# Getting Started + +```{include} ../contents/getting-started.md +:start-line: 5 +:end-before: "## Next Steps" +``` diff --git a/docs/sphinx/index.md b/docs/sphinx/index.md new file mode 100644 index 0000000..265e3a8 --- /dev/null +++ b/docs/sphinx/index.md @@ -0,0 +1,23 @@ + + +# Connector Framework + +```{include} ../contents/index.md +:start-line: 5 +:end-before: "## Documentation Sections" +``` + +```{toctree} +:maxdepth: 2 +:hidden: + +getting-started +specification/index +usage/index +configuration +publishing +``` diff --git a/docs/sphinx/publishing.md b/docs/sphinx/publishing.md new file mode 100644 index 0000000..d49c4c9 --- /dev/null +++ b/docs/sphinx/publishing.md @@ -0,0 +1,5 @@ +# Publishing Data + +```{include} ../contents/publishing.md +:start-line: 5 +``` diff --git a/docs/sphinx/specification/commands.md b/docs/sphinx/specification/commands.md new file mode 100644 index 0000000..18f4b21 --- /dev/null +++ b/docs/sphinx/specification/commands.md @@ -0,0 +1,5 @@ +# Commands Utilities + +```{include} ../../contents/specification/commands.md +:start-line: 5 +``` diff --git a/docs/sphinx/specification/connector.md b/docs/sphinx/specification/connector.md new file mode 100644 index 0000000..73a92b3 --- /dev/null +++ b/docs/sphinx/specification/connector.md @@ -0,0 +1,5 @@ +# Connector API + +```{include} ../../contents/specification/connector.md +:start-line: 5 +``` diff --git a/docs/sphinx/specification/index.md b/docs/sphinx/specification/index.md new file mode 100644 index 0000000..104b09e --- /dev/null +++ b/docs/sphinx/specification/index.md @@ -0,0 +1,17 @@ +# Specification + +```{include} ../../contents/specification/index.md +:start-line: 5 +:end-before: "## Pages" +``` + +```{toctree} +:maxdepth: 2 +:hidden: + +connector +models +commands +utils +logging +``` diff --git a/docs/sphinx/specification/logging.md b/docs/sphinx/specification/logging.md new file mode 100644 index 0000000..b4372a3 --- /dev/null +++ b/docs/sphinx/specification/logging.md @@ -0,0 +1,5 @@ +# Logging + +```{include} ../../contents/specification/logging.md +:start-line: 5 +``` diff --git a/docs/sphinx/specification/models.md b/docs/sphinx/specification/models.md new file mode 100644 index 0000000..fef5fca --- /dev/null +++ b/docs/sphinx/specification/models.md @@ -0,0 +1,5 @@ +# Models + +```{include} ../../contents/specification/models.md +:start-line: 5 +``` diff --git a/docs/sphinx/specification/utils.md b/docs/sphinx/specification/utils.md new file mode 100644 index 0000000..2dde3af --- /dev/null +++ b/docs/sphinx/specification/utils.md @@ -0,0 +1,5 @@ +# Utilities + +```{include} ../../contents/specification/utils.md +:start-line: 5 +``` diff --git a/docs/sphinx/usage/commands-handling.md b/docs/sphinx/usage/commands-handling.md new file mode 100644 index 0000000..f2add3c --- /dev/null +++ b/docs/sphinx/usage/commands-handling.md @@ -0,0 +1,5 @@ +# Commands Handling + +```{include} ../../contents/usage/commands-handling.md +:start-line: 5 +``` diff --git a/docs/sphinx/usage/fleet.md b/docs/sphinx/usage/fleet.md new file mode 100644 index 0000000..5e45656 --- /dev/null +++ b/docs/sphinx/usage/fleet.md @@ -0,0 +1,5 @@ +# Fleet Connector + +```{include} ../../contents/usage/fleet.md +:start-line: 5 +``` diff --git a/docs/usage/index.md b/docs/sphinx/usage/index.md similarity index 59% rename from docs/usage/index.md rename to docs/sphinx/usage/index.md index 2b2a6fc..7110e09 100644 --- a/docs/usage/index.md +++ b/docs/sphinx/usage/index.md @@ -1,9 +1,3 @@ - - # Usage Guides for implementing connectors. @@ -16,4 +10,3 @@ single-robot fleet commands-handling ``` - diff --git a/docs/sphinx/usage/single-robot.md b/docs/sphinx/usage/single-robot.md new file mode 100644 index 0000000..6a683cd --- /dev/null +++ b/docs/sphinx/usage/single-robot.md @@ -0,0 +1,5 @@ +# Single-Robot Connector + +```{include} ../../contents/usage/single-robot.md +:start-line: 5 +```