{ Analytica Model Jev_library, encoding="UTF-8" } SoftwareVersion 7.2.3 {!-70199|Error("This model requires Analytica release 7.2 or later")} LinkLibrary Jev_library Title: Jev library Description: Ask TypeSafe's Jev model typed questions (Noul, Choice, Score) about text or structured state, and get back probabilities. Requests go to TypeSafe's System One API through the Vercel AI Gateway's TypeSafe-compatible endpoint, authenticated with the JEV_API_KEY secret (a Vercel AI Gateway key). Requires Analytica 7.2 or later with ReadFromURL. Author: Lonnie Chrisman, Ph.D.~ Lumina Decision Systems Date: Fri, Oct 23, 2026 3:41 PM NodeSize: 64,24 DiagState: 2,0,1,1264,476,17,10 WindState: 2,98,82,720,350 FontStyle: Arial,15 FileInfo: 0,LinkLibrary Jev_library,2,2,0,0,Jev library.ana ::Secret JEV_API_KEY Title: JEV API KEY NodeLocation: 144,80,2 NodeSize: 80,24 WindState: 2,98,82,720,350 {!70104|Att_GUID: f821aeb8-bc60-488c-82bd-7dcbae8c6e8c} ::Secret::Storage: env ::Secret::Sinks: ReadFromURL ::Secret::Destination: https://ai-gateway.vercel.sh {!70000|Struct JevState(... )} {!70000|Title: JevState} {!70000|Description: Optional structured state. Arbitrary named JSON-compatible fields, sent to Jev as a JSON object; a plain text state is also accepted by JevRequest. Do not include credentials in state.} {!70000|Definition: Self} {!70000|NodeLocation: 592,80,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|NamespaceDefaultScope: Private} Close JevState {!70000|Struct JevChoiceCriteria(...)} {!70000|Title: JevChoiceCriteria} {!70000|Description: Choice option map. Supply arbitrary named options, with text, structured descriptions, or Null descriptions (a Null description means the option is interpreted by its name alone). Example: JevChoiceCriteria(billing:'Charges and refunds', other:'Anything else'). Option names that are not identifiers, such as 'Billing and refunds', can be given with ParseJSON('{"Billing and refunds":"..."}', JevChoiceCriteria).} {!70000|Definition: Self} {!70000|NodeLocation: 144,176,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|NamespaceDefaultScope: Private} Close JevChoiceCriteria {!70000|Struct JevNoulCriteria(`true`, `false` : atom)} {!70000|Title: JevNoulCriteria} {!70000|Description: Optional descriptions of what yes and no mean for a Noul, sent to Jev as its native criteria. Example: JevNoulCriteria('Explicit refund request','No refund requested').} {!70000|Definition: Self} {!70000|NodeLocation: 144,320,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|NamespaceDefaultScope: Private} Close JevNoulCriteria {!70000|Struct JevQuestions(...)} {!70000|Title: JevQuestions} {!70000|Description: Map of user-chosen question IDs to JevChoice, JevScore, or JevNoul instances. Example: JevQuestions(urgent:JevNoul('Is this urgent?')).} {!70000|Definition: If Size(Self -> _members) = 0 Then~ Error('JevQuestions requires at least one named question.');~ For name := Self -> _members Do (~ Local q := `->`(Self, name);~ If Not (TypeOf(q) = 'JevChoice' Or TypeOf(q) = 'JevScore' Or TypeOf(q) = 'JevNoul') Then~ Error('Question ' & name & ' must be JevChoice, JevScore, or JevNoul.')~ );~ Self} {!70000|NodeLocation: 592,248,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|Att_AttsToShow: Attribute Definition} {!70000|NamespaceDefaultScope: Private} Close JevQuestions {!70000|Struct JevProbabilities(... : Number atom)} {!70000|Title: JevProbabilities} {!70000|Description: Probability map keyed by Choice option names or Score level numbers. Numeric level keys can be accessed using `->`(map,'0'). JevProbabilityArray(answer) returns the same probabilities as an Analytica array.} {!70000|Definition: Self} {!70000|NodeLocation: 144,456,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|NamespaceDefaultScope: Private} Close JevProbabilities {!70000|Struct JevLegend(...)} {!70000|Title: JevLegend} {!70000|Description: Score level descriptions keyed by zero-based level numbers, as strings. Descriptions may be structured JSON.} {!70000|Definition: Self} {!70000|NodeLocation: 144,528,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|NamespaceDefaultScope: Private} Close JevLegend {!70000|Struct JevUsage(input_tokens, output_tokens : Number atom; ... )} {!70000|Title: JevUsage} {!70000|Description: Token usage returned by the API, with any future additional usage fields preserved.} {!70000|Definition: Self} {!70000|NodeLocation: 592,568,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|NamespaceDefaultScope: Private} Close JevUsage {!70000|Struct JevChoice(instructions : atom;~ criteria : JevChoiceCriteria atom;~ type : optional named Text atom = 'choice')} {!70000|Title: JevChoice} {!70000|Description: Choice question. Criteria map contains 2–255 unordered options (the API accepts a single option, but this library requires at least two). Instructions and option descriptions can be structured JSON. Example: JevChoice('Which department?', JevChoiceCriteria(billing:'Billing', other:'Other')).} {!70000|Definition: If type <> 'choice' Then Error('JevChoice type must be choice.');~ Local n := Size(criteria -> _members);~ If n < 2 Or n > 255 Then~ Error('JevChoice requires 2 to 255 options.');~ Self} {!70000|NodeLocation: 368,176,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|Att_AttsToShow: Attribute Definition} {!70000|NamespaceDefaultScope: Private} Close JevChoice {!70000|Struct JevScore(instructions : atom;~ criteria : List;~ type : optional named Text atom = 'score')} {!70000|Title: JevScore} {!70000|Description: Score question. Pass an ordered list of 2–10 level descriptions, lowest to highest. Level numbers are zero-based. Example: JevScore('How severe?', ['Cosmetic','Workaround exists','Service unavailable']).} {!70000|Definition: If type <> 'score' Then Error('JevScore type must be score.');~ Local n := Size(criteria);~ If n < 2 Or n > 10 Then~ Error('JevScore requires an ordered list of 2 to 10 levels.');~ Self} {!70000|NodeLocation: 368,248,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|Att_AttsToShow: Attribute Definition} {!70000|NamespaceDefaultScope: Private} Close JevScore {!70000|Struct JevNoul(instructions : atom;~ criteria : optional JevNoulCriteria orNull atom = Null;~ type : optional named Text atom = 'noul')} {!70000|Title: JevNoul} {!70000|Description: Yes/no question returning the probability of yes (0–1). Criteria are optional. Example: JevNoul('Is a refund requested?'). Noul has no separate confidence field.} {!70000|Definition: If type <> 'noul' Then Error('JevNoul type must be noul.');~ Self} {!70000|NodeLocation: 368,320,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|Att_AttsToShow: Attribute Definition} {!70000|NamespaceDefaultScope: Private} Close JevNoul {!70000|Struct JevAnswer(type : Text atom;~ choice : optional Text orNull atom = Null;~ score, noul, confidence : optional Number orNull atom = Null;~ probabilities, legend : optional = Null;~ ...)} {!70000|Title: JevAnswer} {!70000|Description: Typed answer union for the three question types. Choice uses choice, probabilities, confidence; Score uses score, probabilities, legend, confidence; Noul uses noul. Fields not applicable to the type are Null. A question the model declined to answer has type 'refusal' and only Null result fields; JevCall raises an error for these unless allowIncomplete is true. Unknown API fields are preserved.} {!70000|Definition: If Not IsNull(probabilities) Then (~ Self -> probabilities := ParseJSON(MakeJSON(probabilities), JevProbabilities)~ );~ If Not IsNull(legend) Then (~ Self -> legend := ParseJSON(MakeJSON(legend), JevLegend)~ );~ Self} {!70000|NodeLocation: 368,496,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|Att_AttsToShow: Attribute Definition} {!70000|NamespaceDefaultScope: Private} Close JevAnswer {!70000|Struct JevRequest(state : atom;~ questions : JevQuestions atom;~ model : optional Text atom = 'jev-latest')} {!70000|Title: JevRequest} {!70000|Description: Request to TypeSafe's System One API. State can be text or a structured value such as JevState, which is sent as a JSON object. Questions is a JevQuestions map. Use request->JevCall() to POST it to Jev through the Vercel AI Gateway's TypeSafe-compatible endpoint and receive a JevResponse; request->JevJSON() previews the payload without network access. Model is a Jev model id such as 'jev-latest' or 'jev-1.14.0' (sent as typesafe-ai/jev or typesafe-ai/jev-1.14.0), or a full gateway id containing '/'. Authentication uses the JEV_API_KEY secret (a Vercel AI Gateway key), which is never included in this struct. Calls send the state and questions to Vercel and TypeSafe and may incur charges.} {!70000|Definition: Self} {!70000|NodeLocation: 816,176,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|DiagState: 2,0,1,1264,476,17,10} {!70000|NamespaceDefaultScope: Private} Function JevJSON(Self : JevRequest atom) Title: JevJSON Description: Serialize a request as the System One JSON body, without network access. The questions map, criteria and any structured state or instructions are sent in TypeSafe's native shapes; only the model id is mapped to its gateway name. No API key is included. Definition: Local model := Self -> model;~ Local gatewayModel := If FindInText('/', model) > 0 Then model~ Else If model = 'jev-latest' Then 'typesafe-ai/jev'~ Else 'typesafe-ai/' & model;~ MakeJSON(_Struct(model: gatewayModel, state: Self -> state, questions: Self -> questions)) NodeLocation: 112,80,2 NodeSize: 80,24 NodeInfo: 1,,,,,,,0,,,,,,0 Function JevCall(Self : JevRequest atom;~ timeout_ms : optional Number atom = 60000;~ allowIncomplete : optional Boolean atom = False) Title: JevCall Description: Member method: request->JevCall() or request->JevCall(120000). Posts to the Vercel AI Gateway's TypeSafe-compatible System One endpoint using JEV_API_KEY via secret substitution (no secret evaluation). Returns a JevResponse on success, and raises an error on a transport, HTTP or schema failure. Also raises an error if any question comes back refused or missing, unless allowIncomplete is true; then the response's unanswered field lists those question IDs. Timeout is in milliseconds. No automatic retries, to avoid duplicate billing. Calling this function sends the request data to Vercel and TypeSafe and may incur API charges. Definition: Local (body, code, status) := ReadFromUrl(~ 'https://ai-gateway.vercel.sh/typesafe/v1/systemone',~ method: 'POST',~ httpHeaders: 'Authorization: Bearer {{secret:JEV_API_KEY}}' & Chr(13) &~ 'Content-Type: application/json' & Chr(13) &~ 'Accept: application/json',~ httpContent: Self -> JevJSON(),~ connectTimeoutMs: timeout_ms,~ sendTimeoutMs: timeout_ms,~ receiveTimeoutMs: timeout_ms~ );~ If IsNull(code) Then~ Error('Jev connection failed: ' & status)~ Else If code < 200 Or code >= 300 Then~ Error('Jev HTTP ' & code & ': ' & status & Chr(10) & body)~ Else~ JevParseResponse(body, Self -> questions, allowIncomplete: allowIncomplete) NodeLocation: 336,80,2 NodeSize: 80,24 NodeInfo: 1,,,,,,,0,,,,,,0 Close JevRequest Function JevParseResponse(body : Text atom;~ questions : optional JevQuestions orNull atom = Null;~ allowIncomplete : optional Boolean atom = False) Title: JevParseResponse Description: Converts a System One response body to a JevResponse, without network access. Raises an error if the body is not a JSON object with a non-empty answers map. When questions is given, also checks that every question ID has an answer of the matching type; a refused or missing answer raises an error unless allowIncomplete is true, in which case the response's unanswered field lists those IDs. JevCall uses this; call it directly to convert a saved response body. Definition: Local j := ParseJSON(body);~ Local answers := j -> answers;~ If Size(IndexesOf(answers)) > 0 Then~ Error('Jev response has no answers map keyed by question ID:' & Chr(13) & body);~ If TypeOf(answers) <> '_JSON' Then~ Error('Jev response has no answers map keyed by question ID:' & Chr(13) & body);~ If Size(answers -> _members) = 0 Then~ Error('Jev response has no answers.');~ Local unanswered := If IsNull(questions) Then Null Else (~ Index Qid := questions -> _members;~ Local bad := For id := Qid Do (~ Local a := `->`(answers, id);~ If IsNull(a) Then True Else (a -> type) <> (`->`(questions, id) -> type)~ );~ Subset(bad)~ );~ If Not IsNull(questions) Then (~ If Not allowIncomplete And Size(unanswered) > 0 Then~ Error('Jev returned no usable answer for question(s): ' &~ JoinText(For id := unanswered Do (~ Local a := `->`(answers, id);~ id & ' (' & (If IsNull(a) Then 'missing' Else a -> type) & ')'~ ), separator: ', ') &~ '. The request was still billed. Use JevCall(allowIncomplete: True) to keep the other answers.')~ );~ JevResponse(model: j -> model, answers: answers, usage: j -> usage, unanswered: unanswered,~ provider_metadata: j -> provider_metadata) NodeLocation: 816,320,2 NodeSize: 80,24 NodeInfo: 1,,,,,,,0,,,,,,0 Function JevProbabilityArray(answer : JevAnswer atom) Title: JevProbabilityArray Description: Returns an answer's probabilities as an Analytica array over a local index named Outcome, so they can be graphed, summed or combined with other arrays. Noul: Outcome is ['true','false']. Choice: Outcome is the option names. Score: Outcome is the zero-based level numbers 0, 1, ... in order. Raises an error for answers with no probabilities, such as refusals. Definition: Local t := answer -> type;~ If t = 'noul' Then (~ Index Outcome := ['true', 'false'];~ Array(Outcome, [answer -> noul, 1 - (answer -> noul)])~ ) Else If t = 'score' Then (~ Local p := answer -> probabilities;~ Index Outcome := 0..(Size(p -> _members) - 1);~ For k := Outcome Do `->`(p, k & '')~ ) Else If t = 'choice' Then (~ Local p := answer -> probabilities;~ Index Outcome := p -> _members;~ For k := Outcome Do `->`(p, k)~ ) Else~ Error('JevProbabilityArray: an answer of type ' & t & ' has no probabilities.') NodeLocation: 816,392,2 NodeSize: 80,24 NodeInfo: 1,,,,,,,0,,,,,,0 {!70000|Struct JevAnswers(...items)} {!70000|Title: JevAnswers} {!70000|Description: Answer map keyed by the same IDs as JevQuestions. Constructor converts every value to a named JevAnswer; nested probabilities and legend also become named Jev structs. Example: response->answers->urgent->noul.} {!70000|Definition: For name := items -> _members Do (~ `->`(Self, name, rhs: ParseJSON(MakeJSON(`->`(items, name)), JevAnswer))~ );~ Self} {!70000|NodeLocation: 592,496,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|Att_AttsToShow: Attribute Definition} {!70000|NamespaceDefaultScope: Private} Close JevAnswers {!70000|Struct JevResponse(model : Text atom;~ answers;~ usage;~ unanswered : optional = Null;~ cost : optional Number orNull atom = Null;~ ...)} {!70000|Title: JevResponse} {!70000|Description: Successful System One response. Nested answers and token usage use named Jev structs. Example: response->answers->urgent->noul. Cost is the gateway-reported charge for the call in US dollars, taken from provider_metadata when present, otherwise Null. Unanswered lists the question IDs that were refused or missing (empty when every question was answered; Null when the response was not checked against its questions). HTTP failures and malformed responses raise errors rather than returning fabricated answers. Other fields returned by the gateway, such as provider_metadata, are preserved.} {!70000|Definition: Self -> answers := ParseJSON(MakeJSON(answers), JevAnswers);~ Self -> usage := ParseJSON(MakeJSON(usage), JevUsage);~ If IsNull(cost) Then (~ Local pm := Self -> provider_metadata;~ Local gw := If IsNull(pm) Then Null Else pm -> gateway;~ Local c := If IsNull(gw) Then Null Else gw -> cost;~ Self -> cost := If IsNull(c) Then Null Else If IsText(c) Then ParseNumber(c) Else c~ );~ Self} {!70000|NodeLocation: 816,528,2} {!70000|NodeSize: 80,24} {!70000|NodeInfo: 1,,,,,,,0,,,,,,0} {!70000|Att_AttsToShow: Attribute Definition} {!70000|NamespaceDefaultScope: Private} Close JevResponse Close Jev_library