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;
The confidence a Score needs to be stated flatly. A first guess, like
SURE: below it Jev says the levels are ambiguous (TypeSafe's docs), and
the headline asks instead of stating.
48pub const CONFIDENT: f64 = 0.5;
Whether a Score's confidence is high enough to state its level flatly.
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.
78const KINDS: [(&str, Kind, &str); 4] = [ 79 ("yes_or_no", Kind::Noul, "It asks whether something is so. A yes or a no answers it."), 80 ( 81 "pick_one", 82 Kind::Choice, 83 "It asks which, who or what: the best or right one among several possibilities. Naming one answers it.", 84 ), 85 ( 86 "how_much", 87 Kind::Score, 88 "It asks for a degree, an amount or a rating. A position on a scale answers it.", 89 ), 90 ( 91 "how_likely", 92 Kind::Chance, 93 "It asks for the chance, the odds or the probability of something. A percentage answers it.", 94 ), 95];
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).
100pub 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].
110pub fn readings(answer: &ChoiceAnswer) -> Vec<Kind> { 111 let mut ranked: Vec<(Kind, f64)> = 112 Kind::ALL.into_iter().filter_map(|kind| Some((kind, kind_probability(answer, kind)?))).collect(); 113 ranked.sort_by(|a, b| b.1.total_cmp(&a.1)); 114 ranked.iter().enumerate().filter(|(rank, (_, p))| *rank == 0 || (*rank == 1 && *p >= ALSO)).map(|(_, (kind, _))| *kind).collect() 115}
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).
129pub 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.
132fn fact_question(fact: Fact) -> Result<Question, ProtocolError> { 133 Ok(match fact { 134 Fact::Answerable => Question::Noul( 135 Noul::new(Json::text("Is the input a question you can answer with your types?")) 136 .yes_means(Json::text( 137 "The input asks something that can be judged: a yes-or-no question, a question with a best \ 138 answer among options, or a question of degree.", 139 )) 140 .no_means(Json::text( 141 "The input is anything else: a greeting, a statement, a command, or a request to write, \ 142 explain, summarise or do something.", 143 )), 144 ), 145 Fact::Reads(_) => Question::Choice(Choice::new( 146 Json::text("What kind of question is the input?"), 147 KINDS.iter().map(|(label, _, means)| ((*label).to_owned(), Some(Json::text(means)))), 148 )?), 149 Fact::Several => Question::Noul( 150 Noul::new(Json::text("Does the input ask more than one separate question?")) 151 .yes_means(Json::text("It asks two or more separate questions, each needing its own answer.")) 152 .no_means(Json::text("It asks one question, even if that question mentions several things.")), 153 ), 154 Fact::Yes => Question::Noul( 155 Noul::new(Json::text("Answer the question in the input.")) 156 .yes_means(Json::text("The answer to the question in the input is yes.")) 157 .no_means(Json::text("The answer to the question in the input is no.")), 158 ), 159 Fact::Degree => Question::Score(Score::new( 160 Json::text("Answer the question in the input as a degree: how much, how good, how strong, how likely."), 161 DEGREES.iter().map(|level| Json::text(level)), 162 )?), 163 Fact::Scale => Question::Noul( 164 Noul::new(Json::text("Would the input be answered badly on a general scale from 'Not at all' to 'Extremely'?")) 165 .yes_means(Json::text( 166 "Yes. What it asks about has its own units, ranges, grades or named levels, and a good \ 167 answer would use them.", 168 )) 169 .no_means(Json::text( 170 "No. A general scale answers it well enough, or it is not a how-much question at all.", 171 )), 172 ), 173 Fact::Chance => Question::Noul( 174 Noul::new(Json::text("Is the thing the input asks the chances of so?")) 175 .yes_means(Json::text("It is so, or it will happen.")) 176 .no_means(Json::text("It is not so, or it will not happen.")), 177 ), 178 Fact::Fit => Question::Noul( 179 Noul::new(Json::text("Is the input fit to show to strangers on a public page?")) 180 .yes_means(Json::text("It is harmless to show anyone.")) 181 .no_means(Json::text( 182 "It has a slur, harassment, a threat, sexual content, or a private person's name or \ 183 personal details.", 184 )), 185 ), 186 Fact::Whole => Question::Noul( 187 Noul::new(Json::text("Is the input finished, or was it cut off part way through being typed?")) 188 .yes_means(Json::text( 189 "Finished. Its last word is a whole word, and nothing it needs is missing after it. A \ 190 greeting, a statement or a few words can be finished; it need not be a question.", 191 )) 192 .no_means(Json::text( 193 "Cut off. It ends in part of a word, or on a word that needs more after it, such as a, \ 194 the, to, of, or, and, is.", 195 )), 196 ), 197 Fact::Drafted | Fact::Judged => { 198 return Err(ProtocolError::Invalid(format!("{} is not a fact Jev is asked for", fact.name()))); 199 } 200 }) 201}
203fn drafted_question(draft: &Draft) -> Result<Question, ProtocolError> { 204 Ok(match draft { 205 Draft::Noul { instructions, yes_means, no_means } => Question::Noul( 206 Noul::new(Json::text(instructions)).yes_means(Json::text(yes_means)).no_means(Json::text(no_means)), 207 ), 208 Draft::Choice { instructions, options } => Question::Choice(Choice::new( 209 Json::text(instructions), 210 options.iter().map(|option| (option.label.clone(), Some(Json::text(&option.description)))), 211 )?), 212 Draft::Score { instructions, levels } => { 213 Question::Score(Score::new(Json::text(instructions), levels.iter().map(|level| Json::text(level)))?) 214 } 215 }) 216}
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.
233pub struct Part {
The question's id in the request and on the page.
235 pub id: String,
A call that is ready to send: one request, one or more questions.
242pub struct Prepared {
What the call is for, on the page: facts or answers.
244 pub id: &'static str,
The questions, in the order they are asked.
246 pub parts: Vec<Part>,
The request body exactly as it will be sent.
248 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.
257fn prepare( 258 model: &ModelId, 259 id: &'static str, 260 input: &str, 261 asked: impl IntoIterator<Item = (String, Question)>, 262) -> Result<Prepared, ProtocolError> { 263 let state = Json::canonical(&serde_json::json!({ "input": input }).to_string())?; 264 let mut questions = Questions::new(); 265 let mut parts = Vec::new(); 266 for (id, question) in asked { 267 let (kind, asked) = match question { 268 Question::Noul(noul) => ("noul", Asked::Noul(questions.noul(&id, noul)?)), 269 Question::Choice(choice) => ("choice", Asked::Choice(questions.choice(&id, choice)?)), 270 Question::Score(score) => ("score", Asked::Score(questions.score(&id, score)?)), 271 }; 272 parts.push(Part { id, kind, asked }); 273 } 274 let request = String::from_utf8(request_bytes(model, &state, &questions)?) 275 .map_err(|e| ProtocolError::Invalid(e.to_string()))?; 276 let worst_case_dollars = worst_case_dollars(model, &state, &questions); 277 Ok(Prepared { id, parts, request, worst_case_dollars, state, questions }) 278}
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.
285 Facts(Vec<Fact>),
The questions the LLM wrote, in one request, each with its id.
The request for wanted about input (already [clean]).
291pub fn wanted(model: &ModelId, input: &str, wanted: &Wanted) -> Result<Prepared, ProtocolError> { 292 match wanted { 293 Wanted::Facts(facts) => { 294 // Several facts can share a question; it is asked once. 295 let mut asked: Vec<(String, Question)> = Vec::new(); 296 for fact in facts { 297 let id = question_id(*fact); 298 if asked.iter().all(|(known, _)| known != id) { 299 asked.push((id.to_owned(), fact_question(*fact)?)); 300 } 301 } 302 prepare(model, "facts", input, asked) 303 } 304 Wanted::Drafted(drafts) => { 305 let asked: Result<Vec<_>, _> = 306 drafts.iter().map(|(id, draft)| Ok((id.clone(), drafted_question(draft)?))).collect(); 307 prepare(model, "answers", input, asked?) 308 } 309 } 310}
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.
314pub fn learned(fact: Fact, judged: &Judged) -> Option<Value> { 315 match (fact, judged) { 316 (Fact::Answerable | Fact::Several | Fact::Scale | Fact::Fit | Fact::Whole, Judged::Noul(p_yes)) => { 317 Some(Value::Bool(is_yes(*p_yes))) 318 } 319 (Fact::Yes | Fact::Chance, Judged::Noul(_)) | (Fact::Degree, Judged::Score(_)) => Some(Value::Given), 320 (Fact::Reads(kind), Judged::Choice(answer)) => Some(Value::Bool(readings(answer).contains(&kind))), 321 _ => None, 322 } 323}
Probability that the answer is yes.
Whether this ask put the request on the wire.
It did, just now.
338 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.
369impl Outcome {
What the call cost, or may have.
380impl 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.
389pub fn judged(prepared: &Prepared, response: &Response) -> Vec<Judged> { 390 prepared 391 .parts 392 .iter() 393 .map(|part| match part.asked { 394 Asked::Noul(key) => Judged::Noul(response.get(key).noul), 395 Asked::Choice(key) => Judged::Choice(response.get(key).clone()), 396 Asked::Score(key) => Judged::Score(response.get(key).clone()), 397 }) 398 .collect() 399}
Sends a prepared call. runtime is the clock the round trip is timed on.
402pub async fn send<T: Transport, R: Runtime, O: Observer>( 403 client: &Client<T, R, O>, 404 runtime: &impl Runtime, 405 prepared: &Prepared, 406) -> Outcome { 407 let started = runtime.now(); 408 let asked = client.ask(&prepared.state, &prepared.questions).await; 409 let took = runtime.now() - started; 410 match asked { 411 Ok(answered) => Outcome::Answered { 412 judged: judged(prepared, &answered.response), 413 sent: Sent::Now, 414 body: String::from_utf8_lossy(&answered.body).into_owned(), 415 request_id: answered.request_id, 416 attempts: answered.attempts, 417 usage: answered.response.usage(), 418 took, 419 }, 420 Err(error) => Outcome::Failed { 421 request_id: error.request_id().map(str::to_owned), 422 dollars: if error.nothing_billed() { 0.0 } else { prepared.worst_case_dollars }, 423 error: error.to_string(), 424 took, 425 }, 426 } 427}
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.
442pub fn read(model: &ModelId, prepared: &Prepared, kept: Kept<'_>) -> Outcome { 443 match Response::parse(model, &prepared.questions, kept.body.as_bytes()) { 444 Ok(response) => Outcome::Answered { 445 judged: judged(prepared, &response), 446 sent: kept.sent, 447 body: kept.body.to_owned(), 448 request_id: kept.request_id, 449 attempts: kept.attempts, 450 usage: response.usage(), 451 took: kept.took, 452 }, 453 Err(error) => Outcome::Failed { 454 error: format!("the kept response does not answer this request: {error}"), 455 request_id: kept.request_id, 456 took: kept.took, 457 dollars: 0.0, 458 }, 459 } 460}