tswitch
Typesafe switch, score, and bool primitives for Python, powered by TypeSafe AI.
Installation
pip install tswitch
Set your API key (see typesafe.ai):
export TYPESAFE_API_KEY=apikey_...
Quickstart
from tswitch import tswitch
tone = tswitch(
"I was charged twice and I am furious!",
calm="Polite, measured, or friendly language.",
angry="Hostile, frustrated, or expletive language.",
)
# tone == "angry"
Dispatch on structured state, then map case names to results:
from tswitch import tswitch
ticket = {"subject": "Refund please", "body": "You billed me twice this month."}
route = {
"billing": ["billing-team@example.com", "P1"],
"general": ["support@example.com", "P3"],
}[
tswitch(
ticket,
billing="Problems with charges, invoices, refunds, or subscriptions.",
general="Anything else.",
)
]
Score a value on a spectrum
tscore returns a float position along your ordered levels — it can fall between
two of them, so it maps onto a threshold:
from tswitch import tscore
frustration = tscore(
"I was charged twice and I am furious!",
"How frustrated does the customer appear?",
["Calm and neutral.", "Concerned but civil.", "Very angry or using strong language."],
)
if frustration >= 2:
escalate_to_human()
Judge a yes/no statement
tbool returns True when the probability that your statement is true is at or
above threshold (default 0.5), so it maps onto an if:
from tswitch import tbool
if tbool(
"I was charged twice. Please refund the duplicate.",
"Does the customer request a refund?",
true="The customer wants money returned.",
false="The customer is asking for information only.",
):
issue_refund()
Phrase the statement so it is clearly true or false. If you want to measure a
level of something ("how strong is this candidate in Python?"), use tscore
with defined levels instead.
The value
The value may be text, a JSON-like dict or list (nested non-JSON objects are described
by their repr), or any other Python object, which is described by its repr:
import datetime
tswitch(
{"at": datetime.datetime(2026, 1, 1, 3, 0), "text": "URGENT: site is down"},
urgent="Reports of outages or unavailable services.",
routine="Ordinary questions or feature requests.",
)
Case descriptions
Each case is keyed by its name and valued by a description of what matches — a string,
JSON-like content, or None for an undescribed label:
tswitch(
"The plot dragged in the second act.",
plot="Concerns about storyline or pacing.",
cast="Concerns about acting or characters.",
effects=None,
)
Descriptions don't have to be strings — any JSON-like value works, and non-JSON
objects are described by their repr:
bugs = tswitch(
{"error": "TypeError: 'NoneType' object is not iterable", "version": "1.4.2"},
crash={"matches": "failures that stop the program", "signals": ["traceback", "exit code"]},
cosmetic=("visual problems", "layout issues"),
other=object(), # described by repr(object())
)
Cases are keyword arguments, so names with dashes or spaces are unpacked from a dict:
tswitch(
"The plot dragged in the second act.",
**{"slow-burn": "Deliberate, gradual pacing.", "messy": "Confused or erratic pacing."},
)
Async
import asyncio
from tswitch import atswitch, atscore, atbool
async def main():
tone = await atswitch(
"Thanks, that solved it!",
calm="Polite, measured, or friendly language.",
angry="Hostile, frustrated, or expletive language.",
)
frustration = await atscore(
"Thanks, that solved it!",
"How frustrated does the customer appear?",
["Calm and neutral.", "Concerned but civil.", "Very angry."],
)
friendly = await atbool(tone, f"Is {tone!r} friendly language?")
print(tone, frustration, friendly)
asyncio.run(main())
Every function has an async twin: atswitch, atscore, and atbool.
API
tswitch(value, /, *, model=None, api_key=None, client=None, **cases) → str
Returns the name of the case matching value. Raises ValueError if no cases are
supplied, and TypeSafeError if no API key is available.
tscore(value, instructions, levels, /, *, model=None, api_key=None, client=None) → float
Returns the score of value along the ordered levels, from 0 to
len(levels) - 1; it can fall between two levels. Raises ValueError if levels
is empty.
tbool(value, instructions, /, *, true=None, false=None, threshold=0.5, model=None, api_key=None, client=None) → bool
Returns whether instructions is true of value, using the optional true/false
outcome descriptions and requiring a probability of yes at or above threshold.
Raises ValueError if threshold is outside [0, 1].
atswitch, atscore, atbool
The async variants of the functions above, with identical signatures and results.
Options:
-
model— model name override (Noneuses the environment or account default). -
api_key— API key override (NonereadsTYPESAFE_API_KEY). -
client— a reusableTypeSafeClient/AsyncTypeSafeClientfromtypesafe-sdk. It is used for the call and never closed; without it a short-lived client is created per call, so pass your own client in hot loops:from typesafe_sdk import TypeSafeClient from tswitch import tswitch with TypeSafeClient() as client: for email in emails: kind = tswitch(email, sales="...", support="...", client=client)
Errors from the underlying SDK (TypeSafeAPIError and subclasses) propagate unchanged;
see the typesafe-sdk docs for details.
Release files for tswitch 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tswitch-0.1.1.tar.gz | 11.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tswitch-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.1 kB
Release files / tswitch-0.1.1.tar.gz
| Download URL | tswitch-0.1.1.tar.gz |
|---|---|
| Size | 11.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7c37fa0b0b4643bbd2fc9531e738f67f3eb85dffa6641e5d881db4344d42e63b
|
|
BLAKE2b-256 checksum How to use checksums |
59792588161aa340c313ab4fb638726dbf4a7ee9409d9bca85fbb5a651bb3655
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / tswitch-0.1.1-py3-none-any.whl
| Download URL | tswitch-0.1.1-py3-none-any.whl |
|---|---|
| Size | 9.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4dbae2b07c6cb54ef9baa915310c380a84be9c16a5a4187aa1b3dadaf43de6c9
|
|
BLAKE2b-256 checksum How to use checksums |
f09737190f25c835f714c96002c03079882824484e1bac7ad4ccd6a4c7e3aeb1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log