Skip to content

Releases: AboveColin/jevclient

1.3.1

Choose a tag to compare

@AboveColin AboveColin released this 16 Oct 14:12
0a44d8d

An error now carries the API's message, not the whole JSON body.

  • Before: the request was rejected as invalid: {"detail":{"error_type":"api_usage_error","message":"Invalid request."}}
  • After: the request was rejected as invalid: Invalid request.

The client reads detail.message (TypeSafe, measured on 2026-10-16 for a 400 and a 401), error.message (OpenRouter, measured on 2026-10-16 for a 401), or a string detail. Any other body, such as a proxy's HTML page, still comes through as it was, cut at 400 characters.

The prefixes HTTP {status}: , the API key was rejected: , rate limited: and the others do not change.

1.3.0

Choose a tag to compare

@AboveColin AboveColin released this 16 Oct 13:37
1024bd0

Images and Box questions, for Jev 1.14.

  • Image(data, format) is a PNG or JPEG. Pass images to ask(..., assets={ref: Image(...)}) and write ref where the image belongs: in the state, an instruction or a criterion. ask raises ValueError before the request when an asset is not referenced anywhere, because the API answers that with a 400.
  • Box(instructions) asks where something is. The answer is a BoxAnswer with top, bottom, left, right as fractions of the image, and confidence. Box needs jev-1.14.0 or jev-preview.
  • The client sends the TypeSafe-Danger-Zone-Images: true header on a request with an image or a Box, and on no other.
  • JevError.status carries the HTTP status. 400, 413 and 422 raise JevValidationError, so jev-nope now raises JevValidationError("Unknown model: jev-nope") instead of a generic error.
  • MIN_IMAGE_SIDE (280), MAX_IMAGE_SIDE (1568) and MAX_IMAGE_BYTES (16 MiB) are the API's limits. The API refuses an image outside them and does not resize it.

Measured on 2026-10-16: an image is billed 2 input tokens for each 28 x 28 pixel square, so 200 at 280 x 280 and 6,272 at 1568 x 1568. A Box on a red square at 0.6/0.2/0.8/0.5 answered 0.59/0.19/0.79/0.50 at confidence 0.96.

1.2.0

Choose a tag to compare

@AboveColin AboveColin released this 21 Sep 19:30

An empty api_key sends no Authorization header.

Bearer with nothing after it is not a credential, so an endpoint that parses
the header answers 400 or 401 to it. A client built with JevClient("") now
leaves the header off the request, which is what an endpoint that asks for no
credential expects. A non-empty key is unchanged.

Measured against a local server: "a-real-key" sends Bearer a-real-key, ""
sends no Authorization header.

1.1.0

Choose a tag to compare

@AboveColin AboveColin released this 17 Sep 11:23

First release.

An async Python client for TypeSafe Jev, the System One decision model. Ask typed
questions about a piece of text and get values back rather than prose: a probability
for a yes/no question, one option out of a set with the whole distribution, or a
rating against levels you describe.

  • Three question types, mixable in one request.
  • instructions and every criteria value accept a string, an object or an array,
    because the model reads JSON field names as labels.
  • A choice takes 2 to 255 options and a score takes 2 to 10 levels, both checked
    locally so a mistake costs no request.
  • ScoreAnswer.normalized divides by the rubric's own top level, which is what a
    weighted composite needs.
  • One exception class per documented status code, and no internal retry loop, so the
    caller owns the policy.

Measured from the Netherlands: three questions took 712 ms and a hundred took 714 ms,
so batching questions into one call is nearly free in time. Question text is billed
as input at roughly 38 tokens each, so it is not free in money.