//Como usar

SDK Python do Jev: cliente, tipos e retentativa

O SDK oficial dá tipos para as perguntas, acessores por primitiva e uma política de retentativa que já vem ligada.

A API do Jev é simples o bastante para usar com curl, e a primeira chamada mostra esse caminho. Em aplicação de verdade, o SDK oficial paga o próprio peso: ele dá tipos para as perguntas, acessores por primitiva, retentativa configurada e exceções que dizem o que aconteceu.

Instalação e chave

A documentação indica Python 3.10 ou superior.

pip install typesafe-sdk
# ou
uv add typesafe-sdk

O cliente lê a chave da variável de ambiente TYPESAFE_API_KEY. Existem outras três variáveis reconhecidas, e vale conhecer porque elas evitam código desnecessário:

VariávelConfiguraPadrão
TYPESAFE_API_KEYA chave da API (obrigatória)
TYPESAFE_BASE_URLA raiz da APIhttps://api.typesafe.ai
TYPESAFE_DEFAULT_MODELO modelo padrãojev-latest
TYPESAFE_LOG_LEVELO nível do logger typesafe_sdknão definido

A chamada síncrona

O método é system_one, e ele recebe o state e o mapa de perguntas. As perguntas são objetos tipados: Choice, Score e Noul.

from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

estado = {
    "mensagem": "A fatura veio com um valor que não reconheço e ninguém me explica.",
    "politica": "Cobranças não reconhecidas são apuradas em até cinco dias úteis.",
}

perguntas = {
    "cobranca": Noul(instructions="`mensagem` trata de cobrança?"),
    "tom": Choice(
        instructions="Qual é o tom de quem escreveu `mensagem`?",
        criteria={"calmo": None, "frustrado": None, "irritado": None},
    ),
    "urgencia": Score(
        instructions="Qual a urgência de `mensagem`?",
        criteria=["pode esperar", "esta semana", "hoje"],
    ),
}

with TypeSafeClient() as client:
    resultado = client.system_one(estado, perguntas)

print(resultado.nouls["cobranca"].noul)
print(resultado.choices["tom"].choice)
print(resultado.scores["urgencia"].score)

Dois detalhes desse trecho. Primeiro, criteria={"calmo": None, ...}: a documentação aceita descrição nula quando o nome da opção já diz tudo. Segundo, as crases em `mensagem` apontam a parte do state que cada pergunta deve julgar — prática recomendada quando o state é um objeto, explicada em state.

A chamada assíncrona

Mesmo contrato, com async with e await:

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, Choice, Noul


async def main() -> None:
    async with AsyncTypeSafeClient() as client:
        resultado = await client.system_one(
            "Fui cobrado duas vezes. Resolvam hoje, por favor.",
            {
                "cobranca": Noul(instructions="É sobre cobrança?"),
                "tom": Choice(
                    instructions="Qual é o tom?",
                    criteria={"calmo": None, "irritado": None},
                ),
            },
        )
        print(resultado.nouls["cobranca"].noul, resultado.choices["tom"].choice)


asyncio.run(main())

Use o assíncrono quando a sua aplicação já é assíncrona, ou quando você vai disparar muitas chamadas em paralelo — lembrando que, dentro de uma chamada, as perguntas já correm em paralelo, então o assíncrono serve para vários states, não para várias perguntas do mesmo state.

Ler respostas tipadas

Você pode ler por answers, com o identificador da pergunta, ou pelos acessores por tipo — nouls, choices e scores —, que dão o objeto certo para cada primitiva. Um ScoreAnswer traz score, confidence, probabilities e legend como campos tipados, e no SDK as chaves de probabilities e legend são níveis inteiros, não strings como no JSON cru.

resposta = resultado.scores["urgencia"]
print(resposta.score)          # posição na régua, pode cair entre níveis
print(resposta.confidence)     # 0 a 1, derivado da distribuição
print(resposta.probabilities)  # {0: ..., 1: ..., 2: ...}
print(resposta.legend[2])      # "hoje"

Para escolher o modelo, passe no construtor: TypeSafeClient(model="jev-1.13.0"). Fixar a versão é a recomendação da documentação quando você já calibrou limiares, porque um alias se move quando sai versão nova. Para listar o que a sua conta pode chamar, client.models.list().

Retentativa

A RetryPolicy já vem ligada com valores padrão. Conhecer esses padrões evita duplicar lógica de retry por cima:

CampoPadrãoO que faz
max_retries2Tentativas adicionais depois da primeira; 0 desliga
backoff_initial0,5 sPrimeiro atraso, dobrando a cada tentativa
backoff_max5,0 sTeto do atraso
backoff_jitter0,25Fração do atraso sorteada para baixo
http_statuses408, 429 e 500 a 599Códigos que provocam nova tentativa
respect_retry_afterverdadeiroHonra Retry-After e retry-after-ms
timeout30,0 sOrçamento total por chamada, incluindo esperas

Para ajustar, passe no cliente ou na chamada:

from typesafe_sdk import RetryPolicy, TypeSafeClient

client = TypeSafeClient(
    retry=RetryPolicy(max_retries=3, timeout=10.0, http_statuses={429, 500, 502, 503, 504})
)

O orçamento total é a parte que mais gente esquece: o SDK para antes de uma retentativa cuja espera alcançaria o limite, e repropaga o último erro. Em fluxo interativo, um timeout curto vale mais que muitas tentativas.

Exceções

TypeSafeError é a base. TypeSafeAPIError representa resposta HTTP sem sucesso e traz status, body, headers, endpoint e request_id — esse último vem do cabeçalho x-typesafe-request-id e é o que você quer no log para abrir suporte.

from typesafe_sdk import TypeSafeAPIError, TypeSafeRateLimitError

try:
    resultado = client.system_one(estado, perguntas)
except TypeSafeRateLimitError as erro:
    esperar(erro.retry_after_ms)
except TypeSafeAPIError as erro:
    registrar(erro.status, erro.request_id, erro.endpoint)

As especializações seguem os códigos: TypeSafeBadRequestError (400), TypeSafeAuthenticationError (401), TypeSafePermissionDeniedError (403), TypeSafeNotFoundError (404), TypeSafeUnprocessableEntityError (422), TypeSafeRateLimitError (429, com retry_after_ms) e TypeSafeInternalServerError (5xx). Falha sem resposta HTTP levanta TypeSafeAPIConnectionError, e estouro de tempo levanta TypeSafeAPITimeoutError. Quando a resposta chega bem-formada no HTTP mas sem os dados obrigatórios, vem TypeSafeAPIResponseValidationError, com field_path apontando o campo problemático.

Log: um aviso que importa no Brasil

O SDK registra no logger typesafe_sdk. Em info, uma linha de resumo por requisição; em debug, também cabeçalhos e corpos. A documentação é explícita: cabeçalhos secretos são mascarados, os corpos não são.

Se o seu state carrega dado pessoal — mensagem de cliente, currículo, fatura —, debug em produção significa dado pessoal no arquivo de log. Isso é decisão de governança, não de conveniência, e conversa direto com o motivo pelo qual vale guardar probabilidade e confiança de cada decisão automatizada.

Compatibilidade para frente

Duas válvulas úteis quando a API anda mais rápido que o SDK: extra_body manda campos que a sua versão do SDK ainda não modela, e uma pergunta pode ir como dicionário puro em vez de objeto tipado. Respostas de tipo desconhecido são ignoradas com aviso no log, e raw_http_response dá acesso ao JSON completo.

O próximo passo depende do que você vai construir: para desenhar as perguntas, vá para primitivas; para o índice da seção, volte para como usar o Jev.

Perguntas frequentes

Como instalo o SDK Python do Jev?

Com pip install typesafe-sdk ou uv add typesafe-sdk. A documentação indica Python 3.10 ou superior. O cliente lê a chave da variável de ambiente TYPESAFE_API_KEY.

Qual a diferença entre TypeSafeClient e AsyncTypeSafeClient?

São o mesmo contrato em dois modos: o síncrono usa with e chamadas diretas, o assíncrono usa async with e await. Escolha pelo resto da sua aplicação; a forma das perguntas e das respostas é idêntica.

Como leio a resposta no SDK?

Por answers, com o identificador que você escolheu, ou pelos acessores por tipo: nouls, choices e scores. No SDK, probabilities e legend de um Score vêm indexados por nível inteiro, não por string.

O SDK já repete quando dá erro?

Sim. A RetryPolicy padrão faz até 2 novas tentativas, com backoff inicial de 0,5 s dobrando até 5 s, jitter de 0,25, repetindo nos status 408, 429 e 500 a 599, respeitando Retry-After, com orçamento total de 30 s por chamada.

Quais exceções eu preciso tratar?

TypeSafeError é a base. TypeSafeAPIError traz status, body, headers e request_id; dela descendem os erros por código, como TypeSafeAuthenticationError (401), TypeSafeUnprocessableEntityError (422) e TypeSafeRateLimitError (429), que expõe retry_after_ms.

O log do SDK pode vazar conteúdo?

No nível debug, sim: a documentação avisa que cabeçalhos secretos são mascarados, mas os corpos de requisição e resposta não são. Se o state carrega dado pessoal, não deixe debug ligado em produção.

Quer dominar decisões com IA em português? O curso da comunidade está em pré-venda.

Garantir pré-venda por R$ 499,00

Pré-venda: R$ 499,00 · Após o lançamento: R$ 799,00