What Jev is asked, and the rules that turn its answers into what happens.
Three judgments, each a module: a shell command before it runs
([command]), a turn before it ends ([stop]), and a prompt before its
turn starts ([model]). Each is the same three things:
- Facts: what can be known. Most are Jev's to give, and a threshold is applied where an answer is read, so a fact is a plain yes or no (or one of a few named values) and a rule can test it.
- Questions: how Jev is asked for those facts (
jev_facts::Source). Every word that matters is defined in the option or level it belongs to, as concrete acts, never as an adjective. - Rules: tests on facts and what to do when they hold, compiled into
a
retenetwork. Order is priority. The network is what decides, and it is what the website draws, so the picture cannot show other rules than the ones that run.
Nothing here does I/O or is async. The daemon asks a network what to do next, does it (asks Jev, measures memory), records what it learned, and asks again.
21#![forbid(unsafe_code)]
The Jev model every question goes to. Pinned, and pinned here, because the thresholds in this crate were set against this model's answers.
31pub const MODEL: &str = "jev-1.13.0";
[MODEL] as the protocol's type.
How much of a command, a prompt or a final message Jev is shown.
39pub(crate) const SHOWN_CHARS: usize = 6000;
A domain with no effect, or no note, says so with this: it has no value.
The first count characters of text.
The last count characters of text.
A Score's answer as the response body carries it, for answers made up in
a test: score spread over the two levels either side of it.
62#[cfg(feature = "made-up")] 63pub(crate) fn made_up_score(score: f64, levels: usize) -> String { 64 let score = score.clamp(0.0, (levels - 1) as f64); 65 let below = (score.floor() as usize).min(levels - 1); 66 let share = score - below as f64; 67 let probability = |level: usize| match level { 68 l if l == below => 1.0 - share, 69 l if l == below + 1 => share, 70 _ => 0.0, 71 }; 72 let each = |value: &dyn Fn(usize) -> String| (0..levels).map(|level| format!(r#""{level}":{}"#, value(level))).collect::<Vec<_>>().join(","); 73 format!( 74 r#"{{"type":"score","score":{score},"confidence":1.0,"legend":{{{}}},"probabilities":{{{}}}}}"#, 75 each(&|level| format!(r#""level {level}""#)), 76 each(&|level| probability(level).to_string()), 77 ) 78}
A Choice's answer as the response body carries it, for answers made up in a test: the likeliest option is the choice.
82#[cfg(feature = "made-up")] 83pub(crate) fn made_up_choice(probabilities: &[(&str, f64)]) -> String { 84 let (choice, confidence) = probabilities.iter().copied().max_by(|a, b| a.1.total_cmp(&b.1)).unwrap_or(("", 0.0)); 85 let each = probabilities.iter().map(|(label, p)| format!(r#""{label}":{p}"#)).collect::<Vec<_>>().join(","); 86 format!(r#"{{"type":"choice","choice":"{choice}","confidence":{confidence},"probabilities":{{{each}}}}}"#) 87}
A whole response body with these answers, for answers made up in a test.
90#[cfg(feature = "made-up")] 91pub(crate) fn made_up_body(answers: &[(&str, String)]) -> String { 92 let each = answers.iter().map(|(id, answer)| format!(r#""{id}":{answer}"#)).collect::<Vec<_>>().join(","); 93 format!(r#"{{"model":"{MODEL}","answers":{{{each}}},"usage":{{"input_tokens":0,"output_tokens":0}}}}"#) 94}
Commands and messages are clipped by characters, never bytes: a cut inside a multi-byte character would panic on real text.