Skip to content

Python SDK

This is the in-code half of smplkit: resolve flags, read config, control log levels, and emit audit events from one SmplClient. Manage the underlying resources — creating flags, configs, loggers — with the MCP tools or the Console.

Install

bash
pip install smplkit-sdk

Requires Python 3.10+. The version installed is the latest published on PyPI. The loguru logging adapter ships as an extra:

bash
pip install "smplkit-sdk[loguru]"

Initialize the client

One client exposes all four runtime products as sub-namespaces — client.flags, client.config, client.logging, and client.audit. The SDK key (sk_api_...) is read from the SMPLKIT_API_KEY environment variable or the ~/.smplkit profile, so you rarely pass it in code. Set environment (and optionally service) once on the client — it is never a per-call argument; flag, config, and logger resolution is per-environment.

python
from smplkit import Context, SmplClient

with SmplClient(environment="production", service="my-service") as client:
    client.wait_until_ready()  # optional: pre-fetch flags + configs before serving traffic
    ...

wait_until_ready() pre-fetches every flag and config and opens the live-updates WebSocket, giving you a deterministic boot before the first request.

Async: swap in AsyncSmplClient and await the I/O methods. Flag declarations, .get(), and config.subscribe() stay synchronous; wait_until_ready, config.bind, config.get_value, logging.install, and audit.events.record are awaited.

python
from smplkit import AsyncSmplClient

async with AsyncSmplClient(environment="production", service="my-service") as client:
    await client.wait_until_ready()
    ...

Flags

When you need a runtime on/off switch or a typed, targetable value. Declare a typed handle with a safe default, attach context, then evaluate with .get(). The default is served if the flag is missing or smplkit is unreachable — pick the conservative value.

python
from smplkit import Context

checkout = client.flags.boolean_flag("checkout-v2", default=False)
banner   = client.flags.string_flag("banner-color", default="red")
retries  = client.flags.number_flag("max-retries", default=3)

# Ambient context — set once per request, typically from middleware.
client.set_context([
    Context("user", "alice@acme.com", plan="enterprise", beta_tester=True),
    Context("account", "1234", region="us", industry="technology"),
])

if checkout.get():
    render_new_checkout()

Python uses ambient context via client.set_context([...]) (it returns a scope usable as a with block to auto-revert). You can also pass context=[...] to a single checkout.get(context=[...]) call.

→ Full reference (every language, all context modes, change listeners): Flags Runtime

Config

When your app needs server-managed configuration that updates live. Read a single value with get_value(id, key, default) (the form with a default never raises), or bind() a Pydantic model or dict to get a live object that is mutated in place as the config changes.

python
from pydantic import BaseModel

class Plan(BaseModel):
    max_seats: int = 5
    tier: str = "free"

# bind() returns a live object — updates land in place, no re-fetch.
billing = client.config.bind("billing", Plan())
print(billing.max_seats)

# Read one value directly, with a default served if the key is absent.
slow_ms = client.config.get_value("database", "slow_query_threshold_ms", 500)

# Or subscribe to a live dict-like view.
app_name = client.config.subscribe("common")["app.name"]

Config Runtime

Logging

When you want to control log levels at runtime from smplkit instead of redeploying. One install() call hands your log levels to smplkit; use your loggers normally afterward and levels are server-driven. Adapters auto-load for the Python standard logging module and (with the [loguru] extra) loguru — no explicit wiring needed.

python
import logging

client.logging.install()  # await client.logging.install() on AsyncSmplClient

logging.getLogger("myapp.db").debug("levels are now controlled by smplkit")

Logging Runtime

Audit

When you need a durable, queryable record of who did what. Call client.audit.events.record(...) — fire-and-forget by default (buffered, retried); pass flush=True (or call events.flush(...)) for durability before exit. The first three arguments — event_type, resource_type, resource_id — are required. resource_type must not start with smpl. (reserved).

python
client.audit.events.record(
    event_type="invoice.created",
    resource_type="invoice",
    resource_id="inv-1",
    category="billing",
    severity="WARN",
    data={"snapshot": {"total_cents": 4900, "currency": "USD"}},
    flush=True,  # omit to flush asynchronously
)

Emit audit events

The author + operate loop

Authoring a feature is a tight loop: write the SDK call, provision the resource with the MCP tools, then verify. For a checkout kill-switch:

  1. Write — declare the handle and evaluate it: checkout = client.flags.boolean_flag("checkout-v2", default=False)if checkout.get(): render_new_checkout().
  2. Provisioncreate_flag(key="checkout-v2", type="boolean", default=false).
  3. Verifyget_flag(key="checkout-v2").

The flag id in code must equal the key you provision (checkout-v2). The same write → create_config/set_log_levelget_config/query_events loop applies to config, logging, and audit.