# Changelog
## 0.4.3
- Moves to subc-protocol 0.33; take subc-protocol 0.33, subc-transport 0.14, subc-control 0.33 and subc-client-rs 0.31 together.
- Patch release: subc-protocol is used only by the capability-identifier parity
test, not the public API.
- No behaviour changes; existing test-vector bytes are unchanged.
## 0.4.2
- Added `provider::models::JEV_1_13_0` (`"jev-1.13.0"`), the `model` value
for catalog row `typesafe/jev-1.13.0`. The crate offered only
`JEV_LATEST` (`"jev-latest"`), TypeSafe's moving alias, and the contract
and vectors paired the pinned row with it, so a runner following them
sent the alias. When TypeSafe moves the alias, the row's pricing and
behaviour no longer match the model that answers. TypeSafe accepts
versioned ids in `model` and names the answering version in the
response's `model`.
- `JEV_LATEST` stays, documented as the alias for callers that do not pin
a version; a runner serving a catalog row never sends it.
- CONTRACT.md §9 maps the row to `"jev-1.13.0"` and adds a rule: a runner
sends the pinned `model` value of the row it serves, never a moving
alias, and when a response's `model` names a different model than the
one sent, it keeps the answers and logs the mismatch with both values.
- Vectors: every TypeSafe provider request and response `model` is now
`"jev-1.13.0"`, `provider-examples.json` no longer names the alias in its
`about`, and `SHA256SUMS` is refreshed. No type or wire shape changed.
## 0.4.1
- `CONTRACT.md` now directs callers to the shared rules for role-specific
management routes and model-id discovery. A caller selects the serving
module by checking which matching role's `role.describe` lists the model,
not from a catalog row's serving-module field.
## 0.4.0
- Breaking: `request::check_catalog_row` takes the shared `CatalogRow`
(re-exported from `request`) instead of `Option<&str>`, because an
`Option` could not tell a missing row from a row that states no kind. No
catalog row is `model_unknown`; a row with no `kind` is
`model_not_classifier` with `detail.model` and no invented kind, as the
shared batch rules already said. Added a matching test vector.
## 0.3.0
- Depends on `cortexkit-model-batch` 0.2.0, whose estimator changed
(`RULES.md` §5: the schema bills once, the state on every pass, up to a
module's `max_passes`). This crate re-exports `cortexkit-model-batch`
items at their `classifier/v1` paths (`ItemError`, `Refusal`, the image
shape and the rest), and those are now 0.2.0's types, so the bump is
breaking for a consumer that names both crates. Nothing else changed:
`classifier/v1` does not use the shared estimate (its spend guard's
estimate is the module's own, CONTRACT.md §6.9), and every vector keeps
its bytes.
## 0.2.0
Breaking, in behaviour: two validation and identity changes, and four clef
error mappings. No wire shape changes, and every 0.1.0 vector passes byte
for byte.
- The batch rules this role shares with `extractor/v1` moved to the new
crate `cortexkit-model-batch`, whose `RULES.md` states them once;
`CONTRACT.md` points to it. Every moved item keeps its old path through a
`pub use`: `errors::{INVALID_PARAMS, BATCH_ID_REUSE, MODEL_UNKNOWN,
MODEL_NOT_CLASSIFIER, COST_EXCEEDED, ACCOUNT_WALLED, BATCH_IN_PROGRESS,
AUTH_FAILED, MODEL_UNAVAILABLE, RATE_LIMITED, PROVIDER_ERROR,
PROVIDER_REFUSED, INVALID_ITEM, admission_class, item_class, classes,
ErrorClass, ItemError, RefusalDetail, Refusal}`,
`identity::{canonical_json, digest, IdentityError}`,
`question::{Image, InlineImage, content_types, IMAGE_MAX_BYTES,
IMAGES_MAX_TOTAL_BYTES, IMAGE_MAX_PIXELS, check_image}`,
`request::{BATCH_ID_MAX_LEN, is_batch_id, effective_ceiling}` and
`provider::item_code_for_status`. `ADMISSION_CODES`, `ITEM_CODES`,
`BatchIdentity` and the rest stay this crate's own.
- **Image validation decodes the base64.** 0.1.0 read only the base64's
length, so malformed base64 reached the provider, against §6.7. Now
malformed base64, a `data:` URL without `;base64`, and an image of no
bytes are refused `invalid_params` naming `items[i].images[j]`, and image
sizes are decoded bytes.
- **Batch identity hashes images in canonical form** (content type and
decoded bytes). 0.1.0 hashed an image as written, so one image sent as a
`data:` URL and re-sent as `{content_type, base64}` was a false
`batch_id_reuse`. Migration cost: a batch recorded under 0.1.0 with image
items, re-sent after the upgrade, is refused `batch_id_reuse`; re-send it
under a new `batch_id`. Batches without images keep their identity.
- **clef's errors map by Workers AI code first**
(`provider::workers_ai_item_code`), so they say what the account or model
did rather than what the HTTP status suggests. Changed, old to new:
- 429 `3036` (daily free allocation used): `rate_limited` to
`account_limit_exhausted`. 0.1.0 stored `rate_limited` on the item when
the whole account hit its limit, so a re-send after the reset got the
stored error back instead of an answer. The new code is batch-level:
never stored, it stops the call, and on the call's first provider call
it refuses the call at admission.
- 403 `5035` (needs a paid plan), `5016` (model agreement), `3023`
(account blocked): `auth_failed` to `account_not_entitled`, permanent
and batch-level. The credentials were fine; the account may not use the
model.
- 400 `5007` (no such model): `invalid_item` to `model_unavailable`.
0.1.0 stored a permanent `invalid_item`, which tells a caller to drop a
good item, for a model-level failure.
- Unchanged: 404 `3042` stays `model_unavailable`, 429 `3040`
`rate_limited`, 413 `3006` `invalid_item`, 408 `3007` and `3008`
`provider_error`. TypeSafe's status mapping is unchanged.
- New in the vocabulary, re-exported: `ACCOUNT_LIMIT_EXHAUSTED`,
`ACCOUNT_NOT_ENTITLED`, `BATCH_LEVEL_CODES`, `is_batch_level`,
`ANSWER_TOO_LARGE` (never answered by this role, which states no answer
bound) and `ItemErrorDetail`; `ItemError` has an optional `detail`,
absent on every code this role answers with.
- New vectors: `image-identity.json` and `workers-ai-codes.json`.
- `CONTRACT.md` §12 lists two open readings: a catalog row that states no
kind, and adopting the shared cost estimate.
## 0.1.0
- First published release of the `classifier/v1` role contract: the
`role.describe` and `classify.run` wire types, the providers' shared
question and answer shapes, the batch body identity, error codes and their
classes, the request validators, and `CONTRACT.md`. Stability: alpha.
- The role's vectors ship inside the crate package under
`test-vectors/classifier-v1/`, pinned by `SHA256SUMS`. The opt-in
`test-vectors` feature compiles them into the `vectors` module
(`vectors::FILES`, plus a constant per file), so a classifier module's
tests can use the same bytes without a repository checkout.
- Catalog ids are models.dev's own provider keys and pin a model version:
`cloudflare-workers-ai/@cf/cloudflare/clef`,
`cloudflare-workers-ai/@cf/cloudflare/clef-flash` and `typesafe/jev-1.13.0`.
- A `role.describe` model entry's `context_tokens` is optional: it is absent
when the catalog states no exact figure, and absent is not zero.
- `ErrorClass` is non-exhaustive. Error codes are open strings, so later
codes are additive.