Guía

Guía de Jev con Python

El SDK oficial de Python de TypeSafe funciona con este sitio sin cambios: instala typesafe-sdk, apunta la base URL a jev-api.org y llama a system_one con una clave de jev-api.org. Esta guía cubre instalación, configuración, los tres tipos de pregunta, lectura de respuestas y el cliente async.

Actualizado

Instalación

El paquete es typesafe-sdk en PyPI y requiere Python 3.10 o superior. El código está en github.com/typesafe-ai/typesafe-sdk-python.

pip install typesafe-sdk
# or: uv add typesafe-sdk
# requires Python 3.10+

Configura la clave y la base URL

El SDK lee dos variables de entorno. TYPESAFE_API_KEY toma una clave de tu panel de jev-api.org — las claves de la consola de TypeSafe no funcionan aquí — y TYPESAFE_BASE_URL debe ser https://jev-api.org para que las peticiones lleguen al endpoint compatible /v1/systemone en lugar de api.typesafe.ai.

# .env — a key from your jev-api.org dashboard
TYPESAFE_API_KEY=jeva_...
TYPESAFE_BASE_URL=https://jev-api.org

También puedes pasarlas inline: TypeSafeClient(api_key=..., base_url="https://jev-api.org"). El modelo por defecto es jev-latest; cámbialo con TYPESAFE_DEFAULT_MODEL o el argumento model. El timeout por defecto es 10 segundos.

Primera llamada: Noul, Choice y Score

client.system_one toma un state — cadena, objeto o array — y un mapa questions construido con las clases Noul, Choice y Score. La llamada de abajo vuelve dentro de la banda de latencia documentada de 70 a 500 ms y usa 1 crédito en este sitio.

from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

with TypeSafeClient() as client:  # reads TYPESAFE_API_KEY and TYPESAFE_BASE_URL
    response = client.system_one(
        state={"document": "I was charged twice. Please fix this ASAP."},
        questions={
            "billing": Noul(instructions="Is this ticket about billing?"),
            "tone": Choice(
                instructions="What is the customer's tone?",
                criteria={"calm": None, "frustrated": None, "angry": None},
            ),
            "urgency": Score(
                instructions="How urgent is this ticket?",
                criteria=["can wait", "this week", "today"],
            ),
        },
    )

print(response.nouls["billing"].noul)      # 0.0 .. 1.0
print(response.choices["tone"].choice)     # winning option
print(response.scores["urgency"].score)    # fractional level

Leer las respuestas

La respuesta expone vistas tipadas: response.nouls, response.choices y response.scores, indexadas por tus ids de pregunta. Una respuesta Noul lleva .noul, la probabilidad de sí entre 0 y 1. Una Choice lleva .choice, .probabilities y .confidence. Una Score lleva un .score fraccionario más .legend, .probabilities y .confidence.

Usa confidence como señal de derivación. Cuando es baja o dos opciones quedan cerca, manda el caso a una persona o formula una pregunta de seguimiento más estrecha en lugar de bajar el umbral a ciegas. También puedes leer el objeto crudo por pregunta con response.answers["id"].

Cliente async

AsyncTypeSafeClient tiene la misma interfaz con llamadas awaitables — útil dentro de endpoints FastAPI o workers que despliegan varios states a la vez.

from typesafe_sdk import AsyncTypeSafeClient, Noul

async with AsyncTypeSafeClient() as client:
    response = await client.system_one(
        state="I was charged twice for my subscription.",
        questions={"refund": Noul(instructions="Is the customer asking for money back?")},
    )
    print(response.answers["refund"])

Sin el SDK: requests puro

Si no quieres la dependencia, haz tú mismo el POST con el JSON. El endpoint simple de este sitio, /api/v1/decisions, acepta la misma forma state+questions con el modelo jev-1.13 y límites algo más estrictos (de 1 a 6 preguntas, de 2 a 8 opciones de Choice).

import os
import requests

res = requests.post(
    "https://jev-api.org/api/v1/decisions",
    headers={"Authorization": f"Bearer {os.environ['JEV_API_KEY']}"},
    json={
        "model": "jev-1.13",
        "state": "I was charged twice for my subscription.",
        "questions": {
            "refund": {"type": "noul", "instructions": "Is the customer asking for money back?"}
        },
    },
    timeout=30,
)
print(res.json()["answers"]["refund"])

Errores y timeouts

jev-api.org devuelve errores JSON con un campo detail. Los códigos que verás:

  • 401 — clave ausente o rechazada. Revisa la cabecera Authorization: Bearer y que la clave sea de este sitio.
  • 402 — la clave es válida pero el saldo no cubre la llamada. Añade créditos en la página de precios.
  • 422 — el cuerpo falló la validación o el modelo lo rechazó (por ejemplo, por los límites de 64k/32k tokens). El mensaje nombra el campo.
  • 429 — límite de velocidad. Espera y reintenta.
  • 502 — el servicio de decisión no devolvió respuestas. No se usa crédito.

Preguntas frecuentes

¿Puedo usar mi clave de la consola de TypeSafe aquí?

No. Las claves no son intercambiables: las de TypeSafe funcionan en api.typesafe.ai y las del panel de jev-api.org funcionan aquí. Pon TYPESAFE_API_KEY a una clave de jev-api.org y TYPESAFE_BASE_URL a https://jev-api.org.

¿Qué endpoint llama el SDK en este sitio?

Con base_url en https://jev-api.org, el SDK envía a /v1/systemone — el endpoint compatible con TypeSafe, que acepta el conjunto completo de preguntas (hasta 256 preguntas, 255 opciones de Choice, instructions estructuradas) y cobra 1 crédito por llamada correcta.

¿El SDK reintenta automáticamente?

El SDK de TypeSafe trae una política de reintentos por defecto para errores transitorios como 429; puedes cambiarla con la opción retry del cliente. Los errores de validación (422) se corrigen en el cuerpo, no con reintentos.

¿Qué versión de Python necesito?

Python 3.10 o superior. El paquete typesafe-sdk incluye los clientes síncrono (TypeSafeClient) y asíncrono (AsyncTypeSafeClient).

¿Cómo sé qué versión del modelo respondió?

El campo model de la respuesta siempre informa el id versionado que sirvió la petición — hoy jev-1.13.0 — aunque hayas enviado un alias como jev-latest.

Envía la primera decisión

Crea una clave, define las dos variables de entorno y ejecuta el snippet de arriba.