Présentation

Jevper est un wrapper Python qui expose l’interface Jev – un système de classification basé sur le format System One – au-dessus de tout client compatible OpenAI. Le projet fonctionne avec les modèles hébergés par TypeSafe, les serveurs llama.cpp auto‑hébergés ou toute API proposant responses.create ou chat.completions.create. Cette indépendance d’exécution repose sur le « duck‑typing » : le client n’a aucune dépendance directe à openai ou typesafe-sdk au runtime.

from openai import OpenAI
from jevper import Choice, SystemOneClient
client = SystemOneClient(OpenAI(), model="gpt-5.6-terra", method="logprobs")
response = client.system_one(
    state="I was charged twice for the same subscription this month.",
    questions={
        "intent": Choice(
            instructions="Pick the intent of the message.",
            criteria={
                "billing": "money, invoices, refunds, charges",
                "technical": "errors, crashes, login or performance problems",
                "sales": "pricing, plans, purchasing, upgrades",
            },
        )
    },
)
answer = response.answers["intent"]
print(answer.choice)        # "billing"
print(answer.probabilities) # {"billing": 0.88, "technical": 0.08, "sales": 0.03}
print(answer.confidence)    # 0.83

Architecture et fonctionnement

Le client crée d’abord un system prompt combinant l’état fourni et les questions typées. Selon le paramètre method, le flux passe par l’une des quatre voies : logprobs, grammar, structured ou discrete. Les deux premières utilisent des libellés à un caractère (ex. "A", "B") et limitent le nombre d’options à 26 ; elles activent logprobs=true et top_logprobs=20 ou un grammar GBNF dans extra_body. Les deux dernières retournent un JSON strict contenant soit un dictionnaire de probabilités, soit un label unique. Le schéma JSON garantit la cohérence du type de réponse, ce qui simplifie le post‑traitement.

Types de questions et contraintes

Jevper supporte trois types de requêtes : Noul (oui/non avec probabilité), Choice (sélection parmi jusqu’à 255 libellés) et Score (échelle ordonnée de 2 à 10 niveaux). Le champ Score.score représente la moyenne pondérée des indices de niveau (Σ i·pᵢ), reproduisant le calcul de l’API Jev d’origine. Les limites de 255 options pour Choice et de 26 options pour logprobs/grammar sont imposées par le format de tokenisation : au-delà de 26, le token initial « AA » ne peut plus être mappé à un seul caractère, ce qui déclenche une InvalidQuestionError. Le mode auto contourne ce problème en privilégiant le format JSON, évitant ainsi la restriction alphabétique.

Analyse des performances et limites

Le wrapper lance chaque question comme un appel indépendant au fournisseur LLM, autorisant une exécution concurrente contrôlée par max_concurrency (valeur par défaut = 8). Cette parallélisation réduit la latence globale lorsque plusieurs questions sont posées simultanément, mais elle dépend de la capacité du serveur LLM à gérer des requêtes parallèles. La dépendance unique à pydantic>=2.7 assure une validation stricte des schémas, mais introduit un coût d’instanciation supplémentaire à chaque appel. Enfin, l’absence de support natif pour les modèles qui ne retournent pas de log‑probabilités oblige à recourir à la méthode structured, qui peut être moins précise pour les classifications à très grand nombre d’options.