Skip to main content

shiftlabs

Type hints and a testing kit for shiftlabs workflows written in Python, and shiftlabs.client for Python apps that call the shiftlabs API.

uv add --dev shiftlabs   # or: pip install shiftlabs
from shiftlabs import Context, workflow


async def charge(invoice_id: str) -> dict[str, str]:
    ...


@workflow(payload={"type": "object", "properties": {"id": {"type": "string"}}, "required": ["id"]})
async def refund_check(ctx: Context) -> dict[str, str]:
    charged = await ctx.step.run("charge", charge, ctx.payload["id"], retries={"limit": 3, "delay": "10 seconds"})
    ctx.log.info("charged", {"id": charged["id"]})
    return charged

For workflows, the package is for your editor, type checker and tests. You don't need it at run time: the platform adds the same shiftlabs module to every Python workflow when you deploy, at the version the platform runs, and never adds shiftlabs.testing or shiftlabs.client. An app that calls the API with shiftlabs.client installs it as a dependency.

Run npx shiftlabs types to write shiftlabs_types.pyi with your workspace's secret, var, agent and workflow names. After that, ctx.secrets.NAME and ctx.vars.NAME only accept those names, and so do the client's agent and workflow arguments.

Testing

shiftlabs.testing.run_workflow runs a workflow inside a test, the way the platform runs it, with no deploy. Steps run at once: retries don't wait, FatalError fails a step at its first attempt, and sleeps return at once. Waits are answered by step name from events, approvals, asks, calls and starts, next to payload, secrets, vars and state. A missing event returns None, a missing approval is denied, and a missing ask or call fails its step. The payload is checked against the workflow's schema, as a webhook's is. ctx.secrets.NAME reads a stand-in, as on the platform: give a secret as {"value": …, "readable": True} to reveal it, or {"value": …, "hmac": True} to sign with it.

from unittest.mock import AsyncMock, patch

from shiftlabs.testing import run_workflow

import refund_check


def test_refund_check_charges_once() -> None:
    with patch.object(refund_check, "charge", AsyncMock(return_value={"id": "ch_123"})) as charge:
        result = run_workflow(refund_check.refund_check, payload={"id": "ch_123"})
    assert result.status == "completed", result.error
    assert result.output == {"id": "ch_123"}
    charge.assert_awaited_once_with("ch_123")

It returns a RunResult with status, output, error, steps, logs, state and started. Then it runs the workflow a second time on the stored step results, as a run that resumes after a sleep does, and raises AssertionError("code outside a step changed on replay: …") if that calls other steps, such as a step named from time.time(). replay=False skips that check. It mocks no network: use unittest.mock or your HTTP client's mocks. run_workflow runs the workflow in an event loop of its own, so sync and async tests can call it.

Client

shiftlabs.client calls the shiftlabs API from a Python app: chat with agents as your users and stream their answers, ask for JSON answers, run workflows, read runs, send events, answer approvals and mint chat tokens. Shiftlabs waits for each call; AsyncShiftlabs has the same members for asyncio. Both use only the standard library.

import os

from shiftlabs.client import Shiftlabs

sl = Shiftlabs(api_key=os.environ["SHIFTLABS_API_KEY"])
for event in sl.agents.chat("billing", person="user_ann").send("Where is my refund?"):
    if event["type"] == "text":
        print(event["delta"], end="")
started = sl.workflows.run("stripe-refund", {"invoiceId": "in_123"})
print(sl.runs.get(started.run_id)["run"]["status"])

Failures raise shiftlabs.client.ShiftlabsError, whose code, hint and to_dict() match the CLI's --json errors. See the client docs.

Docs: docs.shiftlabs.org.

Metadata

Release files for shiftlabs 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for shiftlabs 0.3.0
File Size Uploaded Provenance
shiftlabs-0.3.0.tar.gz 46.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for shiftlabs 0.3.0
File Interpreter ABI Platform
shiftlabs-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 95.1 kB

Release files / shiftlabs-0.3.0.tar.gz

Download URL shiftlabs-0.3.0.tar.gz
Size 46.6 kB
Tags Source
SHA-256 checksum
How to use checksums
55fdd274072854acddf335027a5db6b08d1208e51be813e75e03335533f1d433
BLAKE2b-256 checksum
How to use checksums
747a2c8451f98af05a3d5fa101b1b692b2fa17c034f54bdc1f702e70b4e5afe4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / shiftlabs-0.3.0-py3-none-any.whl

Download URL shiftlabs-0.3.0-py3-none-any.whl
Size 48.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0765df08ab9e1417819d1bb24c44dab8c44f963e64f5e053f93498d6afbe8150
BLAKE2b-256 checksum
How to use checksums
82f703ad221ef185a565b8651144adc415e97eb26989a6a91b8791840a81447c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Security for shiftlabs 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page