Chapter 17½: inside jev-protocol/src — ten files, no side effects
Every file here is pure: functions take what they need (a clock reading, a
jitter value, a byte slice) as arguments and return a value. Nothing opens a
socket, reads a file or asks the time: the dependency list has nothing that
could, and lib.rs opens with #![forbid(unsafe_code)].
Read them in the order a request lives: name the model, build the questions, write the bytes, then read what came back.
flowchart LR model["model.rs<br/>ModelId"] --> request json["json.rs<br/>Json"] --> question jcs["jcs.rs<br/>RFC 8785"] --> json question["question.rs<br/>Noul, Choice, Score"] --> request["request.rs<br/>Questions, Key, request_bytes"] request --> response["response.rs<br/>Response::parse"] answer["answer.rs<br/>*Answer, Usage"] --> response error["error.rs<br/>ApiError, ProtocolError"] retry["retry.rs<br/>next_delay"]
The files, in reading order
- lib.rs: start here. The module list, every public
re-export, and four constants:
SYSTEM_ONE_PATH(/v1/systemone),ENDPOINT(https://api.typesafe.ai/v1/systemone),REQUEST_ID_HEADER(x-typesafe-request-id) andRETRY_COUNT_HEADER(x-typesafe-retry-count).request_id(headers)reads the id a response carries. At the bottom, thenever_panicsproperty tests. - model.rs:
ModelId::pinned, which accepts a versioned model such asjev-1.13.0and refuses an alias. - json.rs:
Json, a JSON value kept as the exact bytes it will be sent as.Json::textwraps a string,Json::verbatimkeeps the caller's bytes after checking they are JSON,Json::canonicalrewrites them in RFC 8785 form. - question.rs: the
Questionenum and its three kinds.Noul::new(..).yes_means(..).no_means(..),Choice::new(instructions, options)(2 toMAX_CHOICE_OPTIONS= 255),Score::new(instructions, levels)(2 to 10). Also each kind's JSON shape,criteriaincluded: aNoul's{"true", "false"}, aChoice's label-to-description map in the caller's order, aScore's array of levels. - request.rs:
Questions, a named set sent together.questions.noul(id, q)(andchoice,score) returns a typedKey; ids must be non-empty and unique.request_bytes(model, state, questions)writes{"model","questions","state"};question_byteswrites one question alone;worst_case_tokensandworst_case_dollarsprice a request before it is sent, capped byMAX_REQUEST_TOKENS(64,000). - response.rs:
Response::parse(pinned, questions, body), which verifies a 200 body against the questions and the pinned model, andResponse::get(key), which reads one answer by its typed key.model()andusage()read the rest. - answer.rs: what a verified answer holds.
NoulAnswer { noul },ChoiceAnswer { choice, confidence, probabilities }(in option order),ScoreAnswer { score, confidence, probabilities, legend }withnormalized()putting the score on 0 to 1.Usage { input_tokens, output_tokens }withdollars(), andDOLLARS_PER_MTOK($0.042). The sealedFromAnswertrait is what lets aKey<A>read only its own kind. - error.rs:
ProtocolError(a request that cannot be built, a response that does not answer, a model mismatch) andApiError, built from any non-200 byApiError::from_response(status, headers, body), with itsApiErrorKindand anyFieldErrors. - retry.rs: the vendor SDKs' retry policy as constants
(
MAX_RETRIES= 2,ATTEMPT_TIMEOUT= 10 s,BUDGET= 30 s) and one function,next_delay(outcome, retries_so_far, jitter, now), which returns how long to wait before the next attempt, orNonefor no retry.retryable(status)is 408, 429 and 5xx. - jcs.rs: the deep end. A small JSON parser that keeps each
number's original text, and a writer that emits RFC 8785 canonical JSON.
Keys are sorted by UTF-16 code units, as RFC 8785 says, and a repeated
key is refused. A number is written the ECMAScript way (through
ryu-js) when that text has exactly the original value (1.10becomes1.1); otherwise it becomes a string with every significant digit (9007199254740993becomes"9007199254740993"), so abigintloses nothing and equal values give equal bytes. Nesting deeper than 256 is refused.
Aside.
Questions::newstamps each set with a number from a global counter, and everyKeycarries it. That is howResponse::getcan tell a key from another set of questions: the type system knows the kind of answer, the stamp knows which set.
Try it
cargo test -p jev-protocol
Each file carries its own tests at the bottom, in a mod tests block. The
exact_bytes test in request.rs is the shortest complete
example: one question, one canonical state, and the exact line that goes on
the wire.
For the people who maintain it
In this folder
| File | What |
|---|---|
| lib.rs | Module list, re-exports, endpoint and header constants, fuzz tests. |
| model.rs | ModelId: pinned versions only. |
| json.rs | Json: exact bytes, verbatim or canonical. |
| question.rs | Noul, Choice, Score, with the API's limits. |
| request.rs | Questions, Key, request_bytes, question_bytes, worst-case cost. |
| response.rs | Response::parse and get. |
| answer.rs | The answer types, Usage, the price. |
| error.rs | ProtocolError, ApiError, ApiErrorKind, FieldError. |
| retry.rs | The retry policy. |
| jcs.rs | RFC 8785 canonical JSON that never rounds a number. |
| CLAUDE.md | Invariants for agents. |
← Previous: Chapter 17: jev-protocol · Up: jev-protocol · Next: Chapter 18: jev-client →