Metadata-Version: 2.4
Name: tensormesh
Version: 0.1.6
Summary: Tensormesh Python SDK with CLI support.
Author: Tensormesh
License-Expression: LicenseRef-Tensormesh-Proprietary
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.11
Requires-Dist: click>=8.0
Requires-Dist: requests>=2.31
Requires-Dist: PyYAML>=6.0
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: pre-commit>=4.0; extra == "dev"
Requires-Dist: pytest>=9.0; extra == "dev"
Requires-Dist: pytest-cov>=7.0; extra == "dev"
Requires-Dist: ruff>=0.15.0; extra == "dev"
Requires-Dist: shellcheck-py>=0.10.0.1; extra == "dev"
Dynamic: license-file

# Tensormesh SDK

Python SDK and CLI for Tensormesh AI inference and control-plane management.

## Install

```sh
pip install tensormesh
tm version
```

`pip install tensormesh` is the primary one-click install path for the Python SDK and the bundled `tm` CLI. If you only want the CLI on macOS or Linux without managing Python 3.12+, use the public standalone installer described in `docs/cli/guides/installation.mdx`.

## Python SDK Quick Start

```python
from tensormesh import Tensormesh
from tensormesh.types import ChatMessage

# Inference
with Tensormesh(inference_api_key="YOUR_INFERENCE_API_KEY") as client:
    completion = client.inference.serverless.chat.completions.create(
        model="SERVERLESS_MODEL_NAME",
        messages=[ChatMessage(role="user", content="Say hello.")],
    )
print(completion.choices[0].message.content)

# Control Plane
with Tensormesh(control_plane_token="YOUR_CONTROL_PLANE_TOKEN") as client:
    profile = client.control_plane.users.get_auth_profile()
    models = client.control_plane.models.list()
```

Full SDK docs: [docs/sdk/index.mdx](docs/sdk/index.mdx)

---

## Repository

This repository has three primary surfaces:

- A published-docs path for the Python SDK under `docs/sdk/`
- A Mintlify docs site under `docs/` for Tensormesh inference APIs and generated Control Plane API reference.
- A library-first Python SDK under `src/tensormesh/`, with a Python CLI, `tm`, under `src/tensormesh_cli/` consuming that SDK for Control Plane and inference workflows.

Supporting scripts live under `scripts/` for bootstrap, docs/spec sync, validation, and network verification.

No credentials are required for most local validation. `npm run sync`, `npm test`, `npm run verify`, and the default GitHub Actions verify workflow are all designed to run without Control Plane or gateway secrets. Credentials are only needed for the optional live network checks.
For SaaS upgrade checks against the latest published SDK wheel, use `npm run verify:released-client-compatibility`, which is also wired into the same-repo reusable/manual GitHub Actions workflow `.github/workflows/released-client-compatibility.yml`.

Release-facing docs:

- [CHANGELOG.md](CHANGELOG.md)
- [dev/sdk-release-policy.md](dev/sdk-release-policy.md)

## Main Components

- `docs/api-reference/src/*.yaml`: canonical inference API source inputs
- `docs/api-reference/on-demand.openapi.yaml`: generated On-Demand inference OpenAPI output
- `docs/api-reference/serverless.openapi.yaml`: generated Serverless inference OpenAPI output
- `openapi/tensormesh/**/*.swagger.json`: canonical Control Plane Swagger sources
- `openapi/.generated/tensormesh-api/specs/**/*.openapi.json`: normalized generated Control Plane OpenAPI outputs
- `docs/openapi/.generated/tensormesh-api/published-specs/**/*.openapi.json`: published generated Control Plane OpenAPI outputs materialized locally for docs validation
- `docs/docs.base.json`: hand-maintained Mintlify config source
- `docs/docs.json`: generated Mintlify config and navigation
- `src/tensormesh/`: Tensormesh Python SDK implementation
- `src/tensormesh_cli/`: Tensormesh CLI implementation
- `scripts/`: bootstrap, sync, verify, and e2e helpers

## Prerequisites

- Node.js 22 LTS recommended (see `.nvmrc`; CI and Mintlify require a modern Node runtime)
- Python 3.12 for repo scripts and the local venv
- `jq` for Swagger-to-OpenAPI conversion and network verification scripts

macOS example:

```sh
brew install node@22 jq
export PATH="/opt/homebrew/opt/node@22/bin:$PATH"
```

## Quickstart

1. Bootstrap the repo:

```sh
npm run bootstrap
```

That flow preflights Node TLS against the npm registry with the repo CA bundle, installs Node dependencies, creates `.venv`, and runs the fast local validation path.

2. Start the docs site locally:

```sh
npm run dev
```

3. Optional: log in to the Control Plane:

```sh
./.venv/bin/tm auth login
./.venv/bin/tm auth whoami
```

4. Optional: sync managed gateway config and run a gateway smoke test:

```sh
./.venv/bin/tm auth login
./.venv/bin/tm init --sync

./.venv/bin/tm infer chat \
  --json '[{"role":"user","content":"Say hello."}]'
```

`tm init --sync` fetches the gateway provider, API key, user id, and served
model name from the Control Plane, then stores them under
`[managed]` in the active `config.toml` state root. When `TM_CONFIG_HOME` is
unset, that file is `~/.config/tensormesh/config.toml`.

`gateway_api_key` is the CLI config name for the same underlying inference API key the SDK accepts as `inference_api_key`, and `gateway_model_id` remains a config compatibility key whose value is the served model name sent as `model`.

`@latest` resolves from Control Plane model inventory, so it still requires
valid gateway credentials plus Control Plane auth:

```sh
./.venv/bin/tm infer chat \
  --model @latest \
  --json '[{"role":"user","content":"Say hello."}]'
```

## Python SDK

The repository packages a library-first Python SDK from `src/tensormesh/` with parallel sync and async clients.

Start with the published SDK docs:

- [docs/sdk/index.mdx](docs/sdk/index.mdx)
- [docs/sdk/guides/getting-started.mdx](docs/sdk/guides/getting-started.mdx)
- [docs/sdk/guides/auth-and-config.mdx](docs/sdk/guides/auth-and-config.mdx)
- [docs/sdk/guides/inference.mdx](docs/sdk/guides/inference.mdx)
- [docs/sdk/guides/control-plane.mdx](docs/sdk/guides/control-plane.mdx)
- [docs/cli/guides/control-plane-workflows.mdx](docs/cli/guides/control-plane-workflows.mdx)
- [docs/tensormesh-api/index.mdx](docs/tensormesh-api/index.mdx)

The Python SDK is published to PyPI as `tensormesh`, and the same distribution also installs the `tm` CLI. External Python applications should start from the SDK docs. CLI-only users on macOS or Linux can install the public standalone CLI distribution instead.

Basic serverless inference:

```python
from tensormesh import Tensormesh
from tensormesh.types import ChatMessage

with Tensormesh(
    inference_api_key="YOUR_INFERENCE_API_KEY",
) as client:
    completion = client.inference.serverless.chat.completions.create(
        model="SERVERLESS_MODEL_NAME",
        messages=[ChatMessage(role="user", content="Say hello.")],
    )

print(completion.choices[0].message.content)
```

Streaming on-demand inference:

```python
from tensormesh import Tensormesh
from tensormesh.types import ChatMessage

with Tensormesh(
    inference_api_key="YOUR_INFERENCE_API_KEY",
    on_demand_base_url="https://external.nebius.tensormesh.ai",
    on_demand_user_id="00000000-0000-0000-0000-000000000000",
) as client:
    stream = client.inference.on_demand.chat.completions.create(
        model="SERVED_GATEWAY_MODEL_NAME",
        messages=[ChatMessage(role="user", content="Stream a short reply.")],
        stream=True,
    )

    for text in stream.text_deltas():
        print(text, end="")
```

Async serverless inference:

```python
import asyncio

from tensormesh import AsyncTensormesh
from tensormesh.types import ChatMessage


async def main() -> None:
    async with AsyncTensormesh(
        inference_api_key="YOUR_INFERENCE_API_KEY",
    ) as client:
        completion = await client.inference.serverless.chat.completions.create(
            model="SERVERLESS_MODEL_NAME",
            messages=[ChatMessage(role="user", content="Say hello.")],
        )
        print(completion.choices[0].message.content)


asyncio.run(main())
```

Control plane user profile lookup:

```python
from tensormesh import Tensormesh

with Tensormesh(control_plane_token="YOUR_CONTROL_PLANE_TOKEN") as client:
    profile = client.control_plane.users.get_auth_profile()

print(profile.display_name)
```

Control plane billing and support:

```python
from tensormesh import Tensormesh

with Tensormesh(control_plane_token="YOUR_CONTROL_PLANE_TOKEN") as client:
    balance = client.control_plane.billing.balance.get()
    tickets = client.control_plane.support.tickets.list(size=20)

print(balance.units if balance is not None else "0")
print([ticket.subject for ticket in tickets.tickets])
```

Auto-pagination for paged control-plane resources:

```python
from tensormesh import Tensormesh

with Tensormesh(control_plane_token="YOUR_CONTROL_PLANE_TOKEN") as client:
    for transaction in client.control_plane.billing.transactions.auto_paging_iter(
        page_size=100,
    ):
        print(transaction.transaction_id, transaction.amount)
```

Raw and streaming inference responses:

```python
from tensormesh import AsyncTensormesh
from tensormesh import Tensormesh
from tensormesh.types import ChatMessage

with Tensormesh(inference_api_key="YOUR_INFERENCE_API_KEY") as client:
    raw_response = client.inference.serverless.chat.completions.with_raw_response.create(
        model="SERVERLESS_MODEL_NAME",
        messages=[ChatMessage(role="user", content="Say hello.")],
    )
print(raw_response.json())


async def inspect_stream() -> None:
    async with AsyncTensormesh(
        inference_api_key="YOUR_INFERENCE_API_KEY",
    ) as async_client:
        raw_stream = await async_client.inference.serverless.chat.completions.with_streaming_response.create(
            model="SERVERLESS_MODEL_NAME",
            messages=[ChatMessage(role="user", content="Stream a short reply.")],
        )
        async for line in raw_stream.iter_lines():
            print(line)
```

## Environment And Auth

For Python applications, prefer explicit SDK configuration through constructor
arguments or environment variables. The SDK does not read CLI config or auth
files by default.

The bullets below describe CLI and local-operator behavior:

- The standard persistent CLI setup lives under `~/.config/tensormesh/` by default.
- Set `TM_CONFIG_HOME` when you want the CLI to use a different local root for both `config.toml` and `auth.json`.
- The SDK default Control Plane base in this repo is `https://api.tensormesh.ai`, and it can be overridden with `control_plane_base_url` or `TENSORMESH_CONTROL_PLANE_BASE_URL`.
- The CLI targets the same host via `--controlplane-base`, `TENSORMESH_CONTROL_PLANE_BASE_URL`, or top-level `controlplane_base` in `config.toml`.
- Control Plane auth lives at `~/.config/tensormesh/auth.json` by default, or at `$TM_CONFIG_HOME/auth.json` when `TM_CONFIG_HOME` is set.
- `~/.config/tensormesh/config.toml` is the canonical CLI config file by default, or `$TM_CONFIG_HOME/config.toml` when `TM_CONFIG_HOME` is set.
- When `TM_CONFIG_HOME` is unset, `./.venv/bin/tm auth login` stores Control Plane tokens in `~/.config/tensormesh/auth.json`.
- When `TM_CONFIG_HOME` is unset, `./.venv/bin/tm config init` writes the canonical config file to `~/.config/tensormesh/config.toml`.
- When `TM_CONFIG_HOME` is unset, `./.venv/bin/tm init --sync` updates `[managed]` in `~/.config/tensormesh/config.toml` with synced gateway values.
- When `./.venv/bin/tm init --sync` runs with `--controlplane-base`, it also persists that `controlplane_base` into the active `config.toml` so later `@latest` and Control Plane-assisted flows stay on the same environment.
- Global CLI options such as `--output` must come before the subcommand: `tm --output json auth status`.
- `./.venv/bin/tm auth whoami` is the live bearer-token check and uses `GET /auth/profile`.
- `./.venv/bin/tm auth print-token --yes-i-know` prints the raw bearer token for controlled scripts.
- On-Demand inference commands normally read provider, API key, user id, and served model name from `[managed]` in the active `config.toml` state root.

Example:

```sh
./.venv/bin/tm auth login
./.venv/bin/tm init --sync
./.venv/bin/tm config show --sources
```

## Common Commands

- `npm run bootstrap`: preflight Node TLS against the npm registry, install Node deps, create `.venv`, and run local validation
- `./scripts/lint-shell.sh`: lint repo shell scripts using `shellcheck` from your PATH or `./.venv/bin`
- `npm run build:python-dist`: build SDK-first Python wheel artifacts, checksums, and the release manifest under `dist/python`
- `npm run build:python-sdist`: build the source distribution into `dist/python`
- `npm run dev`: start Mintlify locally
- `npm run doctor`: check local prerequisites and repo state
- `npm run clean`: remove local caches and build artifacts
- `npm run sync`: regenerate generated Control Plane OpenAPI outputs, semi-generated control-plane SDK resources, async SDK mirrors, inference OpenAPI outputs, CLI reference pages, and docs navigation
- `npm test`: run the fast network-free local verification path without regen
- `npm run verify:python`: run compile, ShellCheck, Ruff, and Python tests with coverage
- `npm run verify:compatibility`: run opt-in compatibility tests for removed surfaces and migration behavior
- `npm run verify:dist`: build fresh wheel and source-distribution artifacts, then smoke-test the packaged SDK+CLI install paths
- `./scripts/smoke-test-python-sdist.sh`: smoke-test a source distribution artifact from `dist/python` by default
- `npm run verify`: run sync plus CI-parity local validation without network probes
- `npm run verify:network`: run live inference smoke tests; this requires a synced `gateway_api_key`, plus the optional Control Plane proxy smoke when auth is available
- `./.venv/bin/tm --help`: inspect the current CLI surface

## CI

- The default GitHub Actions CI path is token-free. `verify.yml` runs docs/spec validation, Python linting, unit tests with coverage, and dist packaging smoke tests without requiring Control Plane or gateway credentials.
- Live dev/staging verification is separated into manual dispatch via `.github/workflows/live-verify.yml`, which materializes CLI state from Control Plane secrets, runs `tm init --sync`, and then runs `npm run verify:network` without changing the default network-free CI contract.

## Documentation Map

Use these docs by audience and task:

- Getting started with the repo: `README.md`
- Developer setup and local environment: [dev/setup.md](dev/setup.md)
- Validation, CI parity, release flow, and network verification: [dev/workflows.md](dev/workflows.md)
- CLI guides and command reference: [docs/cli/index.mdx](docs/cli/index.mdx)
- Docs/OpenAPI generation pipeline: [dev/docs-system.md](dev/docs-system.md)
- Mintlify hosting/proxy operational requirements: [dev/runbook-mintlify-hosting.md](dev/runbook-mintlify-hosting.md)
- Troubleshooting setup, TLS, and local verification: [dev/troubleshooting.md](dev/troubleshooting.md)
- Contributing expectations and generated-file rules: [CONTRIBUTING.md](CONTRIBUTING.md)
- Security reporting: [SECURITY.md](SECURITY.md)
- Docs site landing page and published API reference: `docs/index.mdx`

## Generated Files

- Do not hand-edit `openapi/.generated/tensormesh-api/specs/**/*.openapi.json`.
- Do not hand-edit `docs/openapi/.generated/tensormesh-api/published-specs/**/*.openapi.json`.
- Do not hand-edit `docs/cli/reference/**/*.mdx`.
- Update the source Swagger under `openapi/tensormesh/` and regenerate with `npm run sync`, which rebuilds the normalized raw specs, republishes the docs-facing specs, and refreshes the semi-generated SDK control-plane resource modules.
- Hand-maintain `docs/docs.base.json` and let `npm run sync` regenerate `docs/docs.json`.
- `docs/tensormesh-api/publish-manifest.json` declares merged published Control Plane nav specs such as the combined Model surface.
- `docs/cli/reference/**/*.mdx` is generated from the Click command tree plus structured metadata attached in `src/tensormesh_cli/docs_meta.py`.

## Governance

- [Contributing guide](CONTRIBUTING.md)
- [Security policy](SECURITY.md)
- [License](LICENSE)
