jev-fn
A small Python wrapper that exposes TypeSafe's Jev model as a single callable function.
if jev("Is this customer angry?") > 0.9:
flag_for_human_review()
Jev is a "System One" typed decision model. It takes a piece of text (the state) and a yes/no question and returns a calibrated probability — no prompt engineering, no JSON parsing, no scoring instructions needed.
Naming note: the PyPI names
jevandjev-evalare already registered by other projects. Checkhttps://pypi.org/project/jev-softbool/before publishing.
Install
pip install jev-fn
typesafe-sdk is the only runtime dependency.
Quickstart
Set one environment variable and you're done — no configure() call needed:
OpenRouter (free tier available, no waitlist — recommended for getting started):
pip install jev-fn
export OPENROUTER_API_KEY="sk-or-..." # get one at openrouter.ai/settings/keys
TypeSafe directly (the native backend):
pip install jev-fn
export TYPESAFE_API_KEY="your-key" # get one at console.typesafe.ai/keys
Then use jev() immediately:
from jev_fn import jev
if jev("Is this customer angry?", context="I want my money back NOW") > 0.9:
flag_for_human_review()
The library detects which backend to use from whichever key is present. If both are set, OPENROUTER_API_KEY takes priority.
Optional configuration
from jev_fn import configure
configure(model="jev-1.13") # pin to a specific version
configure(cache_enabled=True) # avoid duplicate API calls
configure(api_key="...", base_url="...") # fully explicit override
Basic usage
from jev_fn import jev
score = jev("Is this customer angry?", context="I have contacted support three times.")
print(score) # e.g. 0.91
jev() always returns a float in [0.0, 1.0]. Use it directly in if, while, sorted(), or any other Python expression:
if jev("Does this mention a refund?", context=message) > 0.8:
route_to_billing()
Context manager
Evaluate multiple predicates against the same text without repeating context=:
from jev_fn import jev, context
message = "This is the third time my order arrived broken. I want a refund."
with context(message):
angry = jev("Is the customer angry?")
refund = jev("Are they asking for a refund?")
urgent = jev("Is this urgent?")
The same helper is available as jev.context(...):
with jev.context("I cannot log in and need help immediately"):
if jev("Is this urgent?") > 0.8:
print("Prioritize this ticket")
An explicit context= or ctx= argument takes precedence over the context manager. Use one or the other, not both.
OpenRouter
OpenRouter hosts Jev under the same System One API, so you can route requests through OpenRouter instead of TypeSafe directly — useful for development, testing, or if you already have an OpenRouter account.
The simplest path is the OPENROUTER_API_KEY env var (see Quickstart above) — the library auto-routes. For programmatic control:
from jev_fn import configure, jev
configure(
base_url="https://openrouter.ai/api",
api_key="sk-or-...", # your OpenRouter key
model="jev-latest", # or pin to "jev-1.13"
)
score = jev("Is the customer angry?", context="Charged three times this week.")
The request shape, response shape, and the float you get back are identical — the same jev() call works against either backend.
Get an OpenRouter API key at openrouter.ai/settings/keys. Free-tier credits cover small-volume testing.
Caching
configure(cache_enabled=True, cache_ttl=300)
Identical (model, query, context) triples return the cached score without a network call. The cache is process-local and does not persist across restarts.
Exceptions
| Exception | When raised |
|---|---|
ConfigurationError |
Missing API key or invalid config value |
ProviderError |
HTTP or network error from the TypeSafe API |
ParseError |
Unexpected API response shape |
JevError |
Base class for all library exceptions |
Async
from jev_fn import ajev, context
async def route_ticket(message: str) -> str:
with context(message):
angry = await ajev("Is the customer angry?")
refund = await ajev("Are they asking for a refund?")
urgent = await ajev("Is this urgent?")
...
ajev uses AsyncTypeSafeClient from the SDK — safe to await directly on an event loop.
How it works
Under the hood each call uses the official typesafe-sdk:
# sync
with TypeSafeClient(api_key=..., model="jev-latest") as client:
response = client.system_one(
state="<your context text>",
questions={"q": Noul(instructions="<your query>")},
)
score = response.nouls["q"].noul # already a float in [0.0, 1.0]
# async
async with AsyncTypeSafeClient(api_key=..., model="jev-latest") as client:
response = await client.system_one(
state="<your context text>",
questions={"q": Noul(instructions="<your query>")},
)
score = response.nouls["q"].noul
Jev returns a calibrated probability directly — no prompt engineering, no JSON parsing, no clamping needed.
Limitations
- Each uncached call makes a remote API request (latency + cost).
jev()is synchronous and opens a new SDK client per call. Useajev()on an event loop, orconfigure(cache_enabled=True)to reduce call volume.- Scores are calibrated probabilities from Jev, not from a general LLM. Validate thresholds on a labeled evaluation set before using them for consequential decisions.
- Avoid unbounded retry loops such as
while jev(...) < threshold; scores may never cross the threshold. - Context text may contain personal data. Review TypeSafe's data handling terms.
Development
pip install -e ".[dev]"
pytest
python -m build
python -m twine check dist/*
Metadata
Release files for jev-fn 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| jev_fn-0.1.0.tar.gz | 12.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jev_fn-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.1 kB
Release files / jev_fn-0.1.0.tar.gz
| Download URL | jev_fn-0.1.0.tar.gz |
|---|---|
| Size | 12.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f50822784aef0c59a06f0ba46f379d7064f193bcd634957c324b553d4168af53
|
|
BLAKE2b-256 checksum How to use checksums |
1b59f17d9d11f7eb2eaaa1ee3064aa3235db6a9460cfa1be8db845a73d91406d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Release files / jev_fn-0.1.0-py3-none-any.whl
| Download URL | jev_fn-0.1.0-py3-none-any.whl |
|---|---|
| Size | 4.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2f4625444a20d021fdd21436302726c7688e3cd0003ddbbccd575465ea2a8f32
|
|
BLAKE2b-256 checksum How to use checksums |
fa0d972b50b22c747d56257c4c1ee8920f9da619198c4219feeed2f3c374530e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Security for jev-fn 0.1.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.