Appearance
TypeSafe Classifier
Package: elsai-model v2.1.3
Jev is TypeSafe’s System One classifier. One classify call answers typed questions about a state and returns probabilities. It does not generate chat text.
Installation
bash
pip install --extra-index-url https://core-packages.elsai.ai/root/elsai-model/ "elsai-model[typesafe]==2.1.3"Requirements: Python >= 3.10
Create an API key in the TypeSafe console and export it:
bash
export TYPESAFE_API_KEY=sk-...When client_args omits api_key, the SDK reads TYPESAFE_API_KEY.
TypeSafeClassifier
Classifies a state with the Jev model. The default model id is jev-latest. Pass model_id or set TYPESAFE_DEFAULT_MODEL to pin a version such as jev-1.13.0.
python
import os
from elsai_model.typesafe import TypeSafeClassifier
classifier = TypeSafeClassifier(
model_id="jev-latest",
client_args={"api_key": os.environ["TYPESAFE_API_KEY"]},
)Constructor parameters:
| Parameter | Description |
|---|---|
model_id | TypeSafe model id. Falls back to TYPESAFE_DEFAULT_MODEL, then jev-latest |
client_args | Forwarded to the TypeSafe clients (api_key, timeout, retry, base_url, and other SDK client options) |
Methods:
| Method | Description |
|---|---|
classify(state, questions, **kwargs) | Evaluates questions against state and returns the SDK response |
aclassify(state, questions, **kwargs) | Async variant of classify |
list_models(**kwargs) | Returns the SDK model listing |
close() | Closes the synchronous client when this classifier created it |
aclose() | Closes the asynchronous client when this classifier created it |
state may be text, a JSON object, or an array. A per-call model keyword overrides the client default. Rate limits and HTTP 529 are raised as ModelThrottledException.
Environment variables: TYPESAFE_API_KEY, TYPESAFE_DEFAULT_MODEL
Question types
Pass a map of question name to a Noul, Choice, or Score object. The same map can use plain dictionaries (type, instructions, criteria) instead of those objects. Every question is evaluated against the same state.
Noul
A yes-or-no question. The answer is a probability from 0 to 1 on response.nouls[name].noul. criteria is optional. Use it when you need a calibrated check, such as “does this need a human?” or “is this a billing request?”.
Choice
Picks one label from a fixed set. The answer includes choice, probabilities, and confidence on response.choices[name]. criteria is required: a map of label to description (None is allowed). Use it to route a request to a team, a tool, or a model.
Score
Places the state on an ordered rubric. The answer includes score, legend, probabilities, and confidence on response.scores[name]. criteria is required: an ordered list of 2–10 level descriptions. Use it for urgency, risk, or quality.
Example
This ticket is classified with all three question types in one call.
python
import os
from elsai_model.typesafe import Choice, Noul, Score, TypeSafeClassifier
classifier = TypeSafeClassifier(
model_id="jev-latest",
client_args={"api_key": os.environ["TYPESAFE_API_KEY"]},
)
response = classifier.classify(
state="I was charged twice. Please fix this ASAP.",
questions={
"billing": Noul(
instructions="Is this ticket about billing?",
criteria={
"true": "The request is about a charge, invoice, or refund",
"false": "The request is not about billing",
},
),
"department": Choice(
instructions="Which team should handle this?",
criteria={
"billing": "Payments, invoices, refunds",
"technical": "Bugs, outages, integrations",
"sales": None,
},
),
"urgency": Score(
instructions="How urgent is this ticket?",
criteria=["can wait", "this week", "today"],
),
},
)
print(response.nouls["billing"].noul)
print(response.choices["department"].choice)
print(response.choices["department"].probabilities)
print(response.scores["urgency"].score)
print(response.scores["urgency"].legend)
classifier.close()The same questions can be dictionaries. Objects and dictionaries can share one map.
python
response = classifier.classify(
state={"message": "I was charged twice. Please fix this ASAP."},
questions={
"billing": {
"type": "noul",
"instructions": "Is this ticket about billing?",
"criteria": {
"true": "The request is about a charge, invoice, or refund",
"false": "The request is not about billing",
},
},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoices, refunds",
"technical": "Bugs, outages, integrations",
"sales": None,
},
},
"urgency": {
"type": "score",
"instructions": "How urgent is this ticket?",
"criteria": ["can wait", "this week", "today"],
},
},
)List models
list_models returns the SDK listing for the account.
python
listing = classifier.list_models()
print(listing)
for model in listing.models:
print(model.name, model.description)Async
aclassify is the async form of classify. Close the client with aclose.
python
import asyncio
import os
from elsai_model.typesafe import Noul, TypeSafeClassifier
async def main() -> None:
classifier = TypeSafeClassifier(
model_id="jev-latest",
client_args={"api_key": os.environ["TYPESAFE_API_KEY"]},
)
try:
response = await classifier.aclassify(
state="I was charged twice. Please fix this ASAP.",
questions={
"billing": Noul(instructions="Is this ticket about billing?"),
},
)
print(response.nouls["billing"].noul)
finally:
await classifier.aclose()
asyncio.run(main())Using Jev with AgentKit
Jev is not a replacement for LLM(...). Keep a normal chat model on the agent. Call TypeSafeClassifier from a hook when you want to route a request, skip or run the agent, or gate a side-effect tool. See TypeSafe Classifier.