Skip to content

feat: the Kaiten Python SDK - #2

Merged
Alex (Alexkuva) merged 1 commit into
mainfrom
feat/kaiten-python-sdk
Sep 30, 2026
Merged

Alex (Alexkuva) merged 1 commit into
mainfrom
feat/kaiten-python-sdk

Conversation

@Alexkuva

@Alexkuva Alex (Alexkuva) commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

The Kaiten Python SDK: kaitencloud, a typed client for both Kaiten APIs, generated and tested
against the contract of kaitencloud/kaiten@fa7f554f, its main of 2026-09-29.

from kaitencloud import KaitenClient

client = KaitenClient(base_url="https://kaiten.example.com", token="ksh_...")

for customer in client.customers.list():
    print(customer.name)

client.instances.report_usage("acme-production", "seats", 1)

The README is the guide; this description is what to look at when reviewing.

Coverage

Surface Operations Wrapped
Core API 107 98 — of the nine left out, three (get-targeting-context, lint-targeting-rule, test-targeting-rule) are the console's rule-editor tooling and four (listNotifications, markNotificationsRead, get/putNotificationPreferences) a signed-in person's own feed and preferences, all of which kaiten's own SDK coverage test exempts for the same reasons; the other two are OFREP, see below
Platform API 10 10

openapi/coverage.yaml maps every operation to the method that calls it. The tests fail when an
operation is left unwrapped, when a mapped method is missing, and when a client grows a public
method nothing maps.

Beyond the resources themselves: license versioning (licenses.publish / archive /
unarchive, and license_families to resolve a family to the version it serves), models for all
55 webhook events, and kaitencloud.types.Scope, generated from the scopes the contract publishes.

Decisions worth a second opinion

  1. The package is kaitencloud, not kaiten. kaiten on PyPI is a third-party client for
    kaiten.ru, so it can never be ours. docs/content/docs/sdks/python.mdx still promises
    pip install kaiten and a draft API; it needs rewriting.
  2. Webhook subscription endpoints are not wrapped. POST /api/webhooks and friends are
    served by saas-api and are outside the published contract, so nothing here could test them
    against it. Verifying deliveries is included.
  3. update() mirrors the API's PUT: it replaces. The docstrings say so field by field,
    including the two exceptions on instances, where an omitted slug or deployment zone is kept.
  4. Flags are defined here and evaluated through OpenFeature. evaluateFlag and
    evaluateFlagsBulk are deliberately unwrapped: Kaiten implements OFREP so that any
    OpenFeature SDK can read its flags through the generic provider, and a method here would be
    a second, worse client for the same endpoint. The README and the playbook show
    openfeature-provider-ofrep with the same URL and token.
  5. The API address is required. base_url or KAITEN_BASE_URL, and a clear error naming
    both when neither is set: every deployment has its own address, and the SDK never guesses
    where to send a token. go-sdk takes it as a required argument too.
  6. Apache-2.0 with the DCO, from the open-source pack Kaiten and its SDKs are adopting — this
    repository is the first. LICENSE (the apache.org text, byte for byte), NOTICE, DCO.md,
    CONTRIBUTING.md, SECURITY.md, a pull request template, and a DCO check the repository owns
    (.github/workflows/dco.yml, no third-party app); every Python file opens with
    # SPDX-License-Identifier: Apache-2.0, generated ones included, and a test keeps it so.
    For the repositories that follow: go-sdk's and cli's current LICENSE reword section 9, so take
    the pack's.

How it stays true to the API

  • openapi/ holds a snapshot of both contracts, copied by task sync:openapi from a kaiten
    checkout, with the commit recorded in openapi/source.yaml.
  • The models are generated from it (scripts/generate_models.py), and the sync client from the
    async source (scripts/unasync.py). CI fails if either is stale.
  • Every method is exercised against a fake API built from the contract, and every request body is
    validated against the operation's JSON Schema with readOnly properties removed — so a key too
    many or a required key missing fails here rather than as a 422 in production.

The playbook

examples/playbook.py is the part to try by hand. Thirteen steps walk a deployment end to end —
catalog, license and a second version of it, customer, component, release, zone, metadata field,
instance — meter usage until the license refuses a report, evaluate a flag through OpenFeature,
mint and revoke a token, verify a webhook signature, then delete what the run made. Each step
prints the SDK call before it makes it; examples/README.md explains how to run it.

KAITEN_BASE_URL=http://localhost:6000 KAITEN_AUTH_TOKEN=ksh_... \
  uv run python examples/playbook.py --pause

Resources are named after the run and recorded in .kaiten-playbook.json, so runs never collide,
--from resumes where you stopped, and cleanup — never included by a bare run or by --from —
deletes only what that file records. tests/test_playbook.py runs every step against the same
contract-backed fake as the rest of the suite, and mypy --strict covers examples/, so the
example cannot rot.

Verification

  • 744 tests, on Python 3.10 through 3.14, and on the oldest declared dependency versions
    (httpx 0.27, pydantic 2.11).
  • ruff, ruff format --check, mypy --strict, the generated-code checks, and
    uv build + twine check --strict all pass — task check runs the lot. The wheel and the
    sdist both ship LICENSE and NOTICE.
  • Webhook verification is cross-checked against the svix library and its published reference
    vector.
  • Not exercised against a live Kaiten deployment. The playbook was run end to end, as a
    subprocess, against a server that answers with the contract's own samples, which checks the
    script, not a deployment.

Before release

  • Enable Require contributors to sign off on web-based commits (Settings → General, or for
    the whole organization), so commits made on github.com carry a sign-off too.
  • main's default-branch ruleset requires the checks lint, build and test, which the
    CI reports under exactly those names. After this merges, add DCO with GitHub Actions as its
    source: the workflow runs as it is on main (pull_request_target), so a pull request
    cannot edit the check that judges it, and it starts with the first pull request after this
    one. This one's commit is signed off already.
  • Remove the third-party DCO app from the repository: the check it publishes duplicates the
    workflow's, under the same name.
  • Everyone, KAITEN INC included, commits with git commit -s.
  • Turn on private vulnerability reporting, one of the two channels SECURITY.md gives. GitHub
    offers it on public repositories only, so it comes with making this one public; until then,
    reports go to [email protected], the other channel.
  • The README says the Kaiten trademark policy lives in the main repository; link it once kaiten
    publishes it.
  • Publishing needs a PyPI trusted publisher for kaitencloud and a pypi environment on this
    repository; the release workflow then runs on a v* tag.

🤖 Generated with Claude Code · ✅ Tested and approved by Alex (@Alexkuva), maintainer

@Alexkuva
Alex (Alexkuva) force-pushed the feat/kaiten-python-sdk branch 2 times, most recently from adf7e30 to eedbc3c Compare September 30, 2026 18:56
A typed Python client for both Kaiten APIs, generated and tested against the
contract of kaitencloud/kaiten@fa7f554f.

- Core API: KaitenClient and AsyncKaitenClient over 98 of the contract's 107
  operations. Of the nine left out, three are the console's rule-editor
  tooling and four a signed-in person's notification feed and preferences,
  which kaiten's own SDK coverage test exempts for the same reasons; the
  other two are OFREP flag evaluation, which belongs to OpenFeature.
- Platform API: KaitenPlatformClient and AsyncKaitenPlatformClient over all
  10 operations.
- License versioning: a license is a version of a license family, moved
  through DRAFT, PUBLISHED and ARCHIVED by publish(), archive() and
  unarchive(); license_families resolves a family to the version it serves,
  or to a pinned one.
- Models for every response and each of the 55 published webhook events,
  generated from openapi/ by scripts/generate_models.py: snake_case
  attributes, lenient about fields and enum values the API adds after a
  release, and to_dict() for the exact wire shape. kaitencloud.types.Scope
  lists every scope an organization token can carry.
- One implementation, two clients: src/kaitencloud/_sync is generated from
  _async by scripts/unasync.py, so they cannot drift.

Behaviours:

- list() walks every page at the API's maximum page size, and raises
  PaginationError rather than return a list the API said was incomplete.
- Retries cover only what is safe to repeat. A usage report is never
  retried: replayed after a lost response, it would be usage counted twice.
- Errors are typed and keep the RFC 9457 problem -- code, detail, errorId --
  with ThresholdExceededError for a report refused at the license's cap.
- A credential of the wrong class is refused before anything is sent.
- The API address is required, as base_url or KAITEN_BASE_URL: every
  deployment has its own, and the SDK never guesses where to send a token.
- Flags are defined here and evaluated through OpenFeature. Kaiten speaks
  OFREP, so the README shows openfeature-provider-ofrep rather than a second
  client for the same endpoint.

examples/playbook.py walks a deployment of your own from end to end, one
resumable step at a time, and deletes what it created; examples/README.md
explains how to run it.

Tests: 744, none of which opens a socket. Every mapped operation is called
on both clients against a fake API that answers the way the contract says,
and every request body is validated against the operation's own JSON Schema,
once with all arguments and once with only the required ones. The README's
examples and links, and every file's license notice, are checked too.

Licensed under Apache-2.0; contributions are certified under the Developer
Certificate of Origin, which .github/workflows/dco.yml checks on every pull
request with no third-party app (CONTRIBUTING.md, DCO.md, SECURITY.md, NOTICE).

Signed-off-by: Alexandre Bergere <[email protected]>
@Alexkuva
Alex (Alexkuva) merged commit ff5f596 into main Sep 30, 2026
10 checks passed
@Alexkuva
Alex (Alexkuva) deleted the feat/kaiten-python-sdk branch September 30, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants