What lmjtfy asks Jev, and the record each call leaves for the page.
There are two requests. The first asks for facts about the input, as many
as the rules want at once ([rules::Fact]): can Jev judge it, what kind
of question is it, is it several, does it need a scale written for it, is
it fit to show publicly, and the answer if it is a yes-or-no question, a
how-much one, or a how-likely one. The second asks the questions the LLM wrote ([llm::Draft]),
all in one request. Every question is asked about the same state, the
visitor's input.
A call is prepared before it is sent, so the page can show the request
while Jev is still answering it, and [send] returns the response.
Nothing here does I/O: it runs on whatever Transport and Runtime the
Client was built with.
15#![forbid(unsafe_code)]
17use std::time::Duration; 18 19use jev_client::{Client, Observer, Runtime, Transport}; 20use jev_protocol::{ 21 Choice, ChoiceAnswer, Json, Key, ModelId, Noul, NoulAnswer, ProtocolError, Question, Questions, Score, 22 Response, ScoreAnswer, Usage, request_bytes, worst_case_dollars, 23}; 24use llm::Draft; 25use rules::{Fact, Kind, Value}; 26use serde::{Deserialize, Serialize};
The longest input sent on. A search box, not a document.
29pub const MAX_INPUT_CHARS: usize = 500;
At or above this, a Noul reads as yes.
32pub const THRESHOLD: f64 = 0.5;
Whether a Noul's probability reads as yes.
A Noul is sure when the likelier answer has at least this much. Below
it the page ends the answer with a question mark instead of a full stop
(the user, 2026-10-02: "is AI a bit stupid?" came back 0.55, "he's not
that confident").
43pub const SURE: f64 = 0.7;
Whether a Noul's probability is far enough from even to state flatly.
A probability in a word or two, for a how-likely answer: the number is the answer, and this is how to read it.
The visitor's text as Jev sees it, trimmed and capped.
The labels of the kind question's options, and the [Kind] each means.
68const KINDS: [(&str, Kind, &str); 4] = [ 69 ("yes_or_no", Kind::Noul, "It asks whether something is so. A yes or a no answers it."), 70 ( 71 "pick_one", 72 Kind::Choice, 73 "It asks which, who or what: the best or right one among several possibilities. Naming one answers it.", 74 ), 75 ( 76 "how_much", 77 Kind::Score, 78 "It asks for a degree, an amount or a rating. A position on a scale answers it.", 79 ), 80 ( 81 "how_likely", 82 Kind::Chance, 83 "It asks for the chance, the odds or the probability of something. A percentage answers it.", 84 ), 85];
Jev's runner-up reading is taken too when it has at least this much: Jev is split, and both readings are answered (the user, 2026-10-02: at 90% do that one; at 40 / 40 / 20, do the top two).
90pub const ALSO: f64 = 0.3;
Jev's probability that the input is this kind of question.
The kinds the input is read as, likeliest first: the top one, and the
runner-up if Jev gave it at least [ALSO].
100pub fn readings(answer: &ChoiceAnswer) -> Vec<Kind> { 101 let mut ranked: Vec<(Kind, f64)> = 102 Kind::ALL.into_iter().filter_map(|kind| Some((kind, kind_probability(answer, kind)?))).collect(); 103 ranked.sort_by(|a, b| b.1.total_cmp(&a.1)); 104 ranked.iter().enumerate().filter(|(rank, (_, p))| *rank == 0 || (*rank == 1 && *p >= ALSO)).map(|(_, (kind, _))| *kind).collect() 105}
The id of the question that teaches a fact. The three readings are one question, answered with a probability for each.
The scale a how-much question is answered on when nobody writes one for it, lowest first. Fixed, so Jev can be asked without an LLM (the user, 2026-10-02, knowing it gives up levels written for the question).
119pub const DEGREES: [&str; 5] = ["Not at all", "Slightly", "Moderately", "Very", "Extremely"];
The question that teaches a fact. Only facts whose source is Jev have one.
122fn fact_question(fact: Fact) -> Result<Question, ProtocolError> { 123 Ok(match fact { 124 Fact::Answerable => Question::Noul( 125 Noul::new(Json::text("Is the input a question you can answer with your types?")) 126 .yes_means(Json::text( 127 "The input asks something that can be judged: a yes-or-no question, a question with a best \ 128 answer among options, or a question of degree.", 129 )) 130 .no_means(Json::text( 131 "The input is anything else: a greeting, a statement, a command, or a request to write, \ 132 explain, summarise or do something.", 133 )), 134 ), 135 Fact::Reads(_) => Question::Choice(Choice::new( 136 Json::text("What kind of question is the input?"), 137 KINDS.iter().map(|(label, _, means)| ((*label).to_owned(), Some(Json::text(means)))), 138 )?), 139 Fact::Several => Question::Noul( 140 Noul::new(Json::text("Does the input ask more than one separate question?")) 141 .yes_means(Json::text("It asks two or more separate questions, each needing its own answer.")) 142 .no_means(Json::text("It asks one question, even if that question mentions several things.")), 143 ), 144 Fact::Yes => Question::Noul( 145 Noul::new(Json::text("Answer the question in the input.")) 146 .yes_means(Json::text("The answer to the question in the input is yes.")) 147 .no_means(Json::text("The answer to the question in the input is no.")), 148 ), 149 Fact::Degree => Question::Score(Score::new( 150 Json::text("Answer the question in the input as a degree: how much, how good, how strong, how likely."), 151 DEGREES.iter().map(|level| Json::text(level)), 152 )?), 153 Fact::Scale => Question::Noul( 154 Noul::new(Json::text("Would the input be answered badly on a general scale from 'Not at all' to 'Extremely'?")) 155 .yes_means(Json::text( 156 "Yes. What it asks about has its own units, ranges, grades or named levels, and a good \ 157 answer would use them.", 158 )) 159 .no_means(Json::text( 160 "No. A general scale answers it well enough, or it is not a how-much question at all.", 161 )), 162 ), 163 Fact::Chance => Question::Noul( 164 Noul::new(Json::text("Is the thing the input asks the chances of so?")) 165 .yes_means(Json::text("It is so, or it will happen.")) 166 .no_means(Json::text("It is not so, or it will not happen.")), 167 ), 168 Fact::Fit => Question::Noul( 169 Noul::new(Json::text("Is the input fit to show to strangers on a public page?")) 170 .yes_means(Json::text("It is harmless to show anyone.")) 171 .no_means(Json::text( 172 "It has a slur, harassment, a threat, sexual content, or a private person's name or \ 173 personal details.", 174 )), 175 ), 176 Fact::Drafted | Fact::Judged => { 177 return Err(ProtocolError::Invalid(format!("{} is not a fact Jev is asked for", fact.name()))); 178 } 179 }) 180}
182fn drafted_question(draft: &Draft) -> Result<Question, ProtocolError> { 183 Ok(match draft { 184 Draft::Noul { instructions, yes_means, no_means } => Question::Noul( 185 Noul::new(Json::text(instructions)).yes_means(Json::text(yes_means)).no_means(Json::text(no_means)), 186 ), 187 Draft::Choice { instructions, options } => Question::Choice(Choice::new( 188 Json::text(instructions), 189 options.iter().map(|option| (option.label.clone(), Some(Json::text(&option.description)))), 190 )?), 191 Draft::Score { instructions, levels } => { 192 Question::Score(Score::new(Json::text(instructions), levels.iter().map(|level| Json::text(level)))?) 193 } 194 }) 195}
Whether Jev's own protocol takes a question the LLM wrote. One that it refuses is left out of the request, and the page says why.
The answer's key, typed by the question it belongs to.
One question in a request.
212pub struct Part {
The question's id in the request and on the page.
214 pub id: String,
A call that is ready to send: one request, one or more questions.
221pub struct Prepared {
What the call is for, on the page: facts or answers.
223 pub id: &'static str,
The questions, in the order they are asked.
225 pub parts: Vec<Part>,
The request body exactly as it will be sent.
227 pub request: String,
The most this call can cost, every retry billed
(jev_protocol::worst_case_dollars): what a budget holds before it
is sent.
236fn prepare( 237 model: &ModelId, 238 id: &'static str, 239 input: &str, 240 asked: impl IntoIterator<Item = (String, Question)>, 241) -> Result<Prepared, ProtocolError> { 242 let state = Json::canonical(&serde_json::json!({ "input": input }).to_string())?; 243 let mut questions = Questions::new(); 244 let mut parts = Vec::new(); 245 for (id, question) in asked { 246 let (kind, asked) = match question { 247 Question::Noul(noul) => ("noul", Asked::Noul(questions.noul(&id, noul)?)), 248 Question::Choice(choice) => ("choice", Asked::Choice(questions.choice(&id, choice)?)), 249 Question::Score(score) => ("score", Asked::Score(questions.score(&id, score)?)), 250 }; 251 parts.push(Part { id, kind, asked }); 252 } 253 let request = String::from_utf8(request_bytes(model, &state, &questions)?) 254 .map_err(|e| ProtocolError::Invalid(e.to_string()))?; 255 let worst_case_dollars = worst_case_dollars(model, &state, &questions); 256 Ok(Prepared { id, parts, request, worst_case_dollars, state, questions }) 257}
What to ask Jev about an input, in a form that can be sent to the archive: it prepares the same request from this as the Worker did.
The questions that teach these facts, in one request.
264 Facts(Vec<Fact>),
The questions the LLM wrote, in one request, each with its id.
The request for wanted about input (already [clean]).
270pub fn wanted(model: &ModelId, input: &str, wanted: &Wanted) -> Result<Prepared, ProtocolError> { 271 match wanted { 272 Wanted::Facts(facts) => { 273 // Several facts can share a question; it is asked once. 274 let mut asked: Vec<(String, Question)> = Vec::new(); 275 for fact in facts { 276 let id = question_id(*fact); 277 if asked.iter().all(|(known, _)| known != id) { 278 asked.push((id.to_owned(), fact_question(*fact)?)); 279 } 280 } 281 prepare(model, "facts", input, asked) 282 } 283 Wanted::Drafted(drafts) => { 284 let asked: Result<Vec<_>, _> = 285 drafts.iter().map(|(id, draft)| Ok((id.clone(), drafted_question(draft)?))).collect(); 286 prepare(model, "answers", input, asked?) 287 } 288 } 289}
What the answer to a fact's question ([question_id]) makes the fact.
None when the answer is not of the type that question is.
293pub fn learned(fact: Fact, judged: &Judged) -> Option<Value> { 294 match (fact, judged) { 295 (Fact::Answerable | Fact::Several | Fact::Scale | Fact::Fit, Judged::Noul(p_yes)) => { 296 Some(Value::Bool(is_yes(*p_yes))) 297 } 298 (Fact::Yes | Fact::Chance, Judged::Noul(_)) | (Fact::Degree, Judged::Score(_)) => Some(Value::Given), 299 (Fact::Reads(kind), Judged::Choice(answer)) => Some(Value::Bool(readings(answer).contains(&kind))), 300 _ => None, 301 } 302}
Probability that the answer is yes.
Whether this ask put the request on the wire.
It did, just now.
317 Now,
It did not: the same request was answered at at_ms (Unix
milliseconds), and this is that response.
The response body exactly as it arrived.
The whole round trip, retries and waits included.
What a budget should count the failure as: nothing when it certainly cost nothing, the call's worst case otherwise.
348impl Outcome {
What the call cost, or may have.
359impl Prepared {
The state and the questions, for a client that takes them and not the
body: jev-http's, which the eval asks through.
The answers in a response, one per question of prepared, in order.
368pub fn judged(prepared: &Prepared, response: &Response) -> Vec<Judged> { 369 prepared 370 .parts 371 .iter() 372 .map(|part| match part.asked { 373 Asked::Noul(key) => Judged::Noul(response.get(key).noul), 374 Asked::Choice(key) => Judged::Choice(response.get(key).clone()), 375 Asked::Score(key) => Judged::Score(response.get(key).clone()), 376 }) 377 .collect() 378}
Sends a prepared call. runtime is the clock the round trip is timed on.
381pub async fn send<T: Transport, R: Runtime, O: Observer>( 382 client: &Client<T, R, O>, 383 runtime: &impl Runtime, 384 prepared: &Prepared, 385) -> Outcome { 386 let started = runtime.now(); 387 let asked = client.ask(&prepared.state, &prepared.questions).await; 388 let took = runtime.now() - started; 389 match asked { 390 Ok(answered) => Outcome::Answered { 391 judged: judged(prepared, &answered.response), 392 sent: Sent::Now, 393 body: String::from_utf8_lossy(&answered.body).into_owned(), 394 request_id: answered.request_id, 395 attempts: answered.attempts, 396 usage: answered.response.usage(), 397 took, 398 }, 399 Err(error) => Outcome::Failed { 400 request_id: error.request_id().map(str::to_owned), 401 dollars: if error.nothing_billed() { 0.0 } else { prepared.worst_case_dollars }, 402 error: error.to_string(), 403 took, 404 }, 405 } 406}
A response body as it arrived for a call, with what the call recorded beside it.
Reads a kept response as the answer to prepared: the same check of the
body against the questions that send makes, with nothing sent. A body
that does not answer these questions is a failure, not an answer.
421pub fn read(model: &ModelId, prepared: &Prepared, kept: Kept<'_>) -> Outcome { 422 match Response::parse(model, &prepared.questions, kept.body.as_bytes()) { 423 Ok(response) => Outcome::Answered { 424 judged: judged(prepared, &response), 425 sent: kept.sent, 426 body: kept.body.to_owned(), 427 request_id: kept.request_id, 428 attempts: kept.attempts, 429 usage: response.usage(), 430 took: kept.took, 431 }, 432 Err(error) => Outcome::Failed { 433 error: format!("the kept response does not answer this request: {error}"), 434 request_id: kept.request_id, 435 took: kept.took, 436 dollars: 0.0, 437 }, 438 } 439}