Using Jev in LangChain and LangGraph
LangChain maintains a first-party package, langchain-typesafe. It gives you one Runnable, TypeSafeClassifier, plus two experimental agent middleware built on it. The current release is 0.0.1a3, an alpha: the classifier's call signature changed between alphas, and the middleware may change without notice.
Jev is not wrapped as a chat model. You send state and typed questions and get back typed answers with probabilities, which your chain, graph, or agent then acts on.
Plain pip install and uv add resolve the alpha without a --pre flag because no stable release exists. Pin the exact version while the package is pre-1.0.
Which API to use
| You need | Use | Status |
|---|---|---|
| A typed judgment in a chain, graph node, or your own code | TypeSafeClassifier | Beta |
To pick which model handles a create_agent run | ModelRouterMiddleware | Experimental |
To block risky calls to specific tools in a create_agent agent | AutoModeMiddleware | Experimental |
| A judgment at another agent hook, or a fallback on low confidence | Your own middleware or node calling TypeSafeClassifier | Beta |
“Beta” is LangChain's own marker: the classifier is decorated @beta() and emits a LangChainBetaWarning when first constructed. Both middleware live under langchain_typesafe.experimental, which the package says “may change without notice.”
TypeSafeClassifier
Construct the classifier once and pass state and questions together on every call. Questions moved into the call in 0.0.1a3 (PR #40659 ↗), so an example that passes questions= to the constructor is from an earlier alpha. In 0.0.1a3 that raises a validation error, and invoking with only a state string raises TypeError.
Questions must be Choice, Noul, or Score objects. Plain dicts such as {"type": "noul", ...}, which TypeSafe's own SDK accepts, raise AttributeError here. For which primitive fits a decision, see Choice, Score, and Noul.
Reading the result
| Field | What it holds |
|---|---|
answers | Every answer keyed by your question IDs |
choices, nouls, scores | The same answers filtered by type, so result.choices["team"].choice is typed |
ChoiceAnswer | choice, probabilities per label, confidence (0–1, the shape of the distribution, not the winner's probability) |
NoulAnswer | noul, the probability of yes. No separate confidence. |
ScoreAnswer | score (expected level, can be fractional), legend, probabilities, confidence |
model, usage, request_id | Model that answered, input/output tokens, and the x-typesafe-request-id header for support |
Thresholds like the 0.7 above are yours to set from your own data. The API design behind them is covered in the classification task.
Contract details
BaseMessage or message list, at the root or nested in a dict, is converted to role/content JSON (message IDs dropped). A bare number, boolean, or None at the root raises TypeError.model (default jev-latest), api_key (else TYPESAFE_API_KEY), base_url (else TYPESAFE_BASE_URL, else https://api.typesafe.ai), timeout (30 s), and optional client / async_client (httpx2). A missing key fails at construction, not at the first call.POST /v1/systemone to TypeSafe carrying all your questions. The package calls the HTTP API directly; it does not use TypeSafe's Python SDK.invoke, ainvoke, batch, and abatch all work and return ClassifierResponse."questions": {} is not rejected locally; the request is still sent. A fix is proposed in issue #40706 ↗.ModelRouterMiddleware Experimental
Jev picks which of your chat models handles an agent run. Once, before the agent starts (before_agent), the middleware asks a single Choice with one label per ModelChoice. It stores the full ChoiceAnswer in agent state as model_route, then swaps that model into every model call for the run (wrap_model_call).
invoke re-routes.confidence is; in our mocked test a 0.05-confidence answer still routed. For “uncertain → strong model,” write your own middleware on TypeSafeClassifier.agent.invoke rather than falling back to the agent's default model.create_agent model is effectively a placeholder. Every call goes to a routed model. Model strings go through init_chat_model when the middleware is built, so the provider package (for example langchain-openai) must be installed.Whether routing saves money or time depends on your models and traffic; the package makes no such claim and neither do we. The design questions (label criteria, fallbacks, measuring misroutes) are on the model routing task page.
AutoModeMiddleware Experimental
Jev estimates whether a proposed tool call is risky or insufficiently authorized, and the middleware blocks it if so. It hooks wrap_tool_call. It classifies only the tools you list, and each call separately.
Noul over the proposed call's name, ID, and arguments, the tool's description, and up to 30 recent messages, including earlier tool results. The default instructions tell Jev that only explicit user messages authorize execution.ToolMessage saying the call was blocked and continues. There is no threshold argument in 0.0.1a3, although the docstring mentions one.criteria yourself. The source defines default risky and safe descriptions, but in 0.0.1a3 the constructor overrides them with None, so omitting criteria sends the question with instructions only.What Jev decides, what you enforce
AutoMode is a risk estimate, not a security boundary. TypeSafe's own jev-1.13 limitations page ↗ says injected instructions or text arguing for its own classification “can move the answer.” Those messages and tool results are exactly what this middleware sends. LangChain also warns against putting secrets in tool arguments or state unless sending them to TypeSafe is acceptable.
| Layer | Owns |
|---|---|
| Jev | A probability that this call, in this conversation, is risky |
| AutoModeMiddleware | The policy: which tools are checked, the 0.5 cutoff, block vs. run |
| Your tool and backend | Authorization, credentials, allowlists, and the side effect itself. Keep these checks even with AutoMode on. |
Both middleware: configuration
Each middleware builds its own TypeSafeClassifier() from the environment when you construct it. The constructors take no API key, Jev model, timeout, or HTTP client. So TYPESAFE_API_KEY must be set before the middleware is created, calls use jev-latest, and TYPESAFE_BASE_URL is honored. The instance is exposed as .classifier. Replacing it (to pin a Jev version or inject a test transport) works in 0.0.1a3 but isn't a documented option.
Both need the [experimental] extra, which adds langchain (create_agent). Without it the import fails with an explicit install hint. Both set a trace policy (omit_payload) that keeps the middleware step's inputs out of traces.
LangGraph
There is no LangGraph-specific Jev API. Two things are true instead. An agent from create_agent runs as a LangGraph graph, so both middleware already work there. And in a hand-built StateGraph, TypeSafeClassifier is an ordinary Runnable: call it in a node and branch on the answer in a conditional edge. Low confidence becomes an explicit route instead of a silent guess.
Errors, retries, tracing
HTTP failures raise TypeSafeAPIError subclasses that also inherit LangChain's standard model errors: a 429 is both TypeSafeRateLimitError and ModelRateLimitError, with retry_after_ms and request_id. Import the TypeSafe classes from langchain_typesafe.client. The package README imports them from the top level, which fails in 0.0.1a3.
There is no built-in retry, unlike TypeSafe's SDK. Use the Runnable wrapper (it backs off on its own schedule and ignores retry_after_ms):
With LangSmith tracing on, each call is recorded as an llm run with provider typesafe, the Jev model name, and token usage. Without tracing nothing extra happens; a tracing failure never fails a classification.
LangChain or the TypeSafe SDK?
Neither is better in general. The same Jev call sits underneath both; what differs is what you get around it.
| Choose | When |
|---|---|
langchain-typesafe | You already run LangChain or LangGraph and want Jev as a Runnable (batch, with_retry, composition), LangSmith traces with usage, messages passed straight in as state, errors that match your other providers, or the prebuilt middleware. |
TypeSafe's SDK (typesafe-sdk, @typesafe-ai/sdk) | You don't use LangChain, want fewer dependencies, dict questions, a model list, or configurable retries, or you'd rather not build on an alpha. |
For keys, endpoints, and limits, see API & access. For Node, see the TypeScript SDK guide.
LangChain.js and LangSmith
LangChain.js has a separate package, @langchain/typesafe (0.0.1 on npm). Its contract differs from Python's: questions go to the TypeSafeClassifier constructor and invoke takes only state. Don't port the examples on this page line for line; use its README ↗.
LangSmith can also use Jev as an evaluator that scores traces, set up in the LangSmith UI with a TypeSafe key. That is an evaluation workflow, separate from this runtime package; see Jev as a LangSmith evaluator.
Verified September 23, 2026 against langchain-typesafe 0.0.1a3 from PyPI (released September 20; identical to the source on master ↗), with langchain 1.4.2, langchain-core 1.6.4, and langgraph 1.2.12 on Python 3.12. Every example ran against a local mock of the TypeSafe API, with fake chat models for the agents. Request bodies and middleware behavior were inspected there; no live Jev calls were made.