Docs
CommunityJev Tools & CLIs
jev: Function-to-Question Decorator
@jev.fn compiles a Python function definition (parameters, docstring, return annotation) into a state plus typed questions, then validates the answers back into Pydantic models.
- Format
- Docs
- Reading time
- 3 min
- Published by
- Community
- Last verified
- Sep 2026
Highlights
- Install is `pip install jev` / `uv sync`; the package requires Python 3.14 and was released Sep 18, 2026 as version 0.3.0.
- Field types map to question types: bool to Noul, Literal and Enum to Choice, and bounded int or float to Score.
- Limits are 255 options per choice and 256 levels per score; exceeding either raises TypeError at decoration time.
- `.map()` applies the function to a list in one call and returns results in input order, with no documented batch limit.
- Calibrated probabilities never reach you: bool is thresholded (default 0.5), Choice takes the argmax, and Score returns the expected value.
Quickstart
python
from typing import Literal
from pydantic import BaseModel, Field
import jev
class Triage(BaseModel):
department: Literal["billing", "technical", "sales"]
is_urgent: bool
frustration: int = Field(ge=0, le=2)
jev.decide("I was charged twice. Fix this NOW.", Triage)
# Triage(department='billing', is_urgent=True, frustration=2)Watch out
Calibrated probabilities and confidence scores are discarded, and unsupported return types raise TypeError at import time, so confidence-gated routing needs the raw SDK.
Questions about this entry
Short answers drawn from the linked project or page.
It hides a real limitation. Calibrated probabilities and confidence never reach you: bool is thresholded at 0.5 by default, Choice takes the argmax, and Score returns the expected value. If you want confidence-gated routing rather than a fixed answer, use the raw SDK instead.
No. The return annotation must be a Pydantic model and anything else, including str, nested models, lists and Optional, raises TypeError at decoration time, so at import rather than at first call. Field types map onto question types: bool to Noul, Literal and Enum to Choice, and int or float with Field bounds to Score.
The package page states Python 3.14, installs with pip install jev or uv sync, and reads TYPESAFE_API_KEY from a .env file. That interpreter floor is the setup cost to weigh, alongside the fact that every real call spends money.
Yes. The function body always runs, so returning the model instance directly skips the API call entirely as a mock seam for tests, and .map() applies the function to a list in one call with results in input order. Limits are 255 options per choice and 256 levels per score, and exceeding either raises TypeError at decoration time.
From the field metadata. Field(description=...) becomes the question's instructions, and without one the field name is humanized, so is_urgent reads as 'is urgent'. Score levels default to the numbers in the Field range and are overridden with json_schema_extra levels, which is why the package page says to write descriptions: they are the questions.
From the docstring, rendered as a Jinja2 template with the bound arguments, so {{ ticket }} substitutes the parameter at call time and the rendered text is the state. With no docstring the arguments themselves are sent as a JSON state instead. The pattern on the package page keeps the prompt in the docstring and returns triage.state() from the body.
Three evaluated bodies. Return fn.state() with no value for the body-less form, where the rendered docstring is the entire state, and a bare ... or raise NotImplementedError works too. Return fn.state(value) to build the state with loops, conditionals or f-strings. Or return a Model instance to skip the call entirely and use it as a test seam.
From the Jev guides
Original write-ups on what this entry is for.
From the Jev AI model community
Posts from builders shipping with Jev right now.