Chapter 3: Getting Started: Your First System One Request

Learning Objectives

Pre-Quiz: Environment Setup

A Python script that classifies syslog lines runs perfectly when you execute it by hand, but the same script fails immediately when triggered by a systemd timer. What is the most likely cause?

TYPESAFE_API_KEY is exported in your interactive shell but is not inherited by the systemd service's environment
The TypeSafe API key has expired and must be reissued from the console
systemd services cannot make outbound HTTPS requests
The typesafe-sdk package was installed with uv instead of pip

Before writing any triage logic, you want the cheapest possible way to confirm your API key is accepted and the TypeSafe API is reachable. Which check should you run first?

Send a full system_one request with three Choice, Score, and Noul questions
Call GET /v1/models with your API key
Run pip install typesafe-sdk again to force a reinstall
Print the raw value of TYPESAFE_API_KEY to the terminal

A triage script raises TypeSafeAPIConnectionError. Based on the SDK's exception types, what does that tell you about where the problem lies?

The request was malformed, such as an empty questions mapping
The server received the request but returned an unsuccessful response, such as a 401 or 429
The request never successfully reached and returned from the server — likely a proxy, firewall, or DNS issue
The API key has exceeded its published rate limit of 1,200 requests per minute

Environment Setup

Key Points

Every example in this book runs through one Python package and one environment variable. The SDK "automatically reads your TYPESAFE_API_KEY environment variable and uses the jev-latest model by default." Treat the key like an SNMPv3 credential or a TACACS+ key: export it for interactive work, keep it out of Git via .gitignore, and store it in a real secret manager (Ansible Vault, Kubernetes Secret, CI encrypted variables) for anything unattended.

export TYPESAFE_API_KEY="ts_live_your_key_here"
echo "${TYPESAFE_API_KEY:0:8}..."   # print only a prefix, never the whole key

A detail that bites automation engineers constantly: a variable exported in your interactive shell is not automatically visible to a systemd service, a cron job, or a Docker container. When a script works by hand but fails under the scheduler, check the environment first — the same way you'd check whether a route exists in the VRF the traffic actually lands in.

The SDK requires Python 3.10 or higher and installs with either of the two common package managers. Use a per-project virtual environment — the equivalent of putting each service in its own VRF instead of letting dependencies leak into the global routing table.

python3 -m venv ~/venvs/noc-triage
source ~/venvs/noc-triage/bin/activate
pip install typesafe-sdk          # or: uv add typesafe-sdk

Before writing triage logic, prove three things independently: the package imports, the key is readable, and the API answers. The cheapest live check is GET /v1/models — it consumes no input tokens, so it's the network engineer's equivalent of a ping before a traceroute.

curl -s https://api.typesafe.ai/v1/models \
  -H "Authorization: Bearer $TYPESAFE_API_KEY"

The most reliable end-to-end smoke test is the smallest possible real request: one short state, one Noul question. If it returns, your key, network path, Python version, and billing account are all confirmed live in one round trip. Log the returned request_id from this first call on — it is the only identifier that ties your side of a conversation to TypeSafe's.

The SDK raises three distinct exception types: TypeSafeError for validation issues such as empty questions or empty criteria, TypeSafeAPIError for unsuccessful server responses, and TypeSafeAPIConnectionError for connection or timeout failures. Knowing which one you got tells you whether the problem is in your request, on the server, or in the path between them.

SymptomLikely causeWhat to check
KeyError at client constructionTYPESAFE_API_KEY unset in the running process, even if set in your shellRe-export it; confirm the scheduler, container, or service unit inherits it
Install or import failsPython older than 3.10Check python3 --version; rebuild the venv on a supported interpreter
HTTP 401 as TypeSafeAPIErrorKey wrong, revoked, or truncated by copy/pasteRe-issue the key; confirm no trailing whitespace or shell quoting mangled it
HTTP 429 as TypeSafeAPIErrorExceeded 250,000 tokens/sec or 1,200 requests/minBatch more questions per request, back off and retry, or raise your plan's limits
TypeSafeAPIConnectionErrorConnection or timeout failureProxy, egress firewall, or DNS — then consider raising timeout
TypeSafeError before any network trafficEmpty questions mapping or empty criteriaEvery Choice needs at least one label; questions must be non-empty
Key Takeaway: The entire setup is one package and one environment variable: install typesafe-sdk on Python 3.10+, export TYPESAFE_API_KEY, and let TypeSafeClient() pick the key up on its own. Prove access with the smallest possible real request rather than assuming it works, and log the returned request_id from day one.

Figure 3.1: Environment setup sequence, from interpreter check to a verified live request

flowchart TD A["Check Python version is 3.10 or higher"] --> B["Create a virtual environment"] B --> C["Install typesafe-sdk with pip or uv"] C --> D["Set TYPESAFE_API_KEY environment variable"] D --> E["Verify with GET /v1/models"] E --> F["Run smallest real request as smoke test"]

Visual animation — coming soon

Post-Quiz: Environment Setup

A Python script that classifies syslog lines runs perfectly when you execute it by hand, but the same script fails immediately when triggered by a systemd timer. What is the most likely cause?

TYPESAFE_API_KEY is exported in your interactive shell but is not inherited by the systemd service's environment
The TypeSafe API key has expired and must be reissued from the console
systemd services cannot make outbound HTTPS requests
The typesafe-sdk package was installed with uv instead of pip

Before writing any triage logic, you want the cheapest possible way to confirm your API key is accepted and the TypeSafe API is reachable. Which check should you run first?

Send a full system_one request with three Choice, Score, and Noul questions
Call GET /v1/models with your API key
Run pip install typesafe-sdk again to force a reinstall
Print the raw value of TYPESAFE_API_KEY to the terminal

A triage script raises TypeSafeAPIConnectionError. Based on the SDK's exception types, what does that tell you about where the problem lies?

The request was malformed, such as an empty questions mapping
The server received the request but returned an unsuccessful response, such as a 401 or 429
The request never successfully reached and returned from the server — likely a proxy, firewall, or DNS issue
The API key has exceeded its published rate limit of 1,200 requests per minute
Pre-Quiz: Anatomy of a Request

Your NOC triage service needs to answer "is this the fourth interface flap on this port in the last ten minutes?" about an event. Why can't a single raw syslog line, by itself, answer that question reliably as the state argument?

state only accepts plain text, never structured data, so flap history cannot be represented
Jev can only answer from what is present in state, and a single line contains no record of prior flaps
Noul questions cannot be asked about anything except a single log line
The model field must be set to jev-preview to reason about historical context

The SDK usage documentation shows Choice(instructions="What is the tone?", criteria={"calm": None, "angry": None}) as a valid form. Why does the chapter recommend writing full descriptions for each label instead, in a production triage pipeline?

Choice questions fail validation and raise TypeSafeError if any criteria value is None
A bare label name like 'routing' means one thing to the engineer and may be ambiguous to the model without a description, the same way an ACL remark clarifies intent that a rule number alone doesn't
Descriptions are required to compute the confidence field on a ChoiceAnswer
Without descriptions, the response will omit the probabilities dictionary for that question

A NOC triage service writes severity and team assignments directly into ServiceNow. Which model-selection approach best matches the change-control discipline the chapter recommends for this production path?

Always use jev-latest everywhere so the service automatically benefits from every model update
Pin the production path to jev-1.13.0 and use jev-latest or jev-preview only in staging or lab environments
Set model to None in every call so the client decides which version to use at random
Use jev-preview in production because it is the newest and most capable build

Anatomy of a Request

Key Points

You give Jev something to look at (state) and a set of named, typed questions about it (questions). Everything else — model selection, timeouts, retries, extra headers — is optional tuning.

Figure 3.2: Anatomy of a System One request and response

flowchart LR State["state"] --> Request["POST /v1/systemone"] Questions["questions dictionary"] --> Request Model["model (optional)"] --> Request Request --> Answers["answers"] Request --> Usage["usage"]

state is "text, a JSON object, or an array to evaluate." The best mental model for network engineers is packet classification: state is the packet header plus payload; the questions are the ACL entries and QoS classifiers matched against it. A classifier can only act on fields present in the packet — Jev can only answer from what you put in state. For a single syslog event the raw line is usually enough; when triage depends on context ("is this the fourth flap in ten minutes?"), that context has to be structured into the state as an object, because Jev evaluates what you give it and nothing else. Keep the state tight — you're billed on input tokens at $42 per billion, with output free.

questions is "a non-empty mapping of question names to question objects." The keys are names you invent; the values are Noul (yes/no), Choice (multiple options), or Score (rating on a scale).

Question typeConstructor patternCriteria shapeReturns
NoulNoul(instructions="...")noneProbability of yes, 0–1
ChoiceChoice(instructions="...", criteria={...})dict: label → descriptionSelected label, confidence, probabilities per label
ScoreScore(instructions="...", criteria=[...])ordered list of rubric levelsExpected score, confidence, legend, probabilities per integer score

Choice criteria are a dict; Score criteria are an ordered list where position 0 is the bottom of the scale. Descriptions on Choice labels are technically optional (the SDK accepts None per label), but write them anyway for production triage — a label like routing means one thing to you and may be ambiguous to a model inferring intent from six characters. Descriptions are the equivalent of an ACL remark line: optional, but operationally essential. The question-name keys you choose become the keys of the response's answers dictionary, so pick names that will survive contact with downstream systems like ServiceNow.

model is optional and overrides the client default; when None, the call inherits the client's configured model, which defaults to jev-latest.

NameWhat it points toWhen to use it
jev-latestjev-1.13.0 — most recent stable release; the SDK defaultDefault choice for most users
jev-1.13.0A specific version IDPinning, so an update can't silently shift classifications
jev-previewCurrently identical to latest; used for future preview buildsTesting upcoming builds before they become default

For a NOC service writing severity and team assignments into ServiceNow, pin jev-1.13.0 in production while running jev-latest in staging — the same change-control discipline that stops you from pushing a new vendor release to sixty access switches without a deliberate promotion.

response = client.system_one(
    state=syslog_line,
    questions=triage_questions,
    model="jev-1.13.0",
)

Other optional per-call parameters: retry (retry policy), timeout (override in seconds), extra_headers, and extra_body (shallow-merged extra fields). In a syslog pipeline, timeout and retry matter most — a triage service that blocks indefinitely on one call is worse than one that drops the event and logs it.

Key Takeaway: A request is state plus questions. state accepts text, a JSON object, or an array, and questions is a non-empty mapping whose keys become the keys of the response. Everything else, including model, is optional — but pinning jev-1.13.0 in production instead of riding jev-latest is the same change-control instinct you already apply to device firmware.
Post-Quiz: Anatomy of a Request

Your NOC triage service needs to answer "is this the fourth interface flap on this port in the last ten minutes?" about an event. Why can't a single raw syslog line, by itself, answer that question reliably as the state argument?

state only accepts plain text, never structured data, so flap history cannot be represented
Jev can only answer from what is present in state, and a single line contains no record of prior flaps
Noul questions cannot be asked about anything except a single log line
The model field must be set to jev-preview to reason about historical context

The SDK usage documentation shows Choice(instructions="What is the tone?", criteria={"calm": None, "angry": None}) as a valid form. Why does the chapter recommend writing full descriptions for each label instead, in a production triage pipeline?

Choice questions fail validation and raise TypeSafeError if any criteria value is None
A bare label name like 'routing' means one thing to the engineer and may be ambiguous to the model without a description, the same way an ACL remark clarifies intent that a rule number alone doesn't
Descriptions are required to compute the confidence field on a ChoiceAnswer
Without descriptions, the response will omit the probabilities dictionary for that question

A NOC triage service writes severity and team assignments directly into ServiceNow. Which model-selection approach best matches the change-control discipline the chapter recommends for this production path?

Always use jev-latest everywhere so the service automatically benefits from every model update
Pin the production path to jev-1.13.0 and use jev-latest or jev-preview only in staging or lab environments
Set model to None in every call so the client decides which version to use at random
Use jev-preview in production because it is the newest and most capable build
Pre-Quiz: Worked Example: Classifying a Cisco Syslog Line

The example syslog line is stamped %LINEPROTO-5-UPDOWN, and Cisco's own severity digit is 5 (Notice) regardless of whether the affected port is an unused desk drop or a voice uplink. Why does the worked example ask a Score question about operational_severity instead of just reading the device's severity digit?

Score questions are the only question type that can process syslog-formatted text
The device's severity digit is fixed per mnemonic and can't distinguish scope of impact, while a Score question can weigh context like which port and what it carries
Cisco severity digits are unreliable and are frequently transmitted incorrectly by IOS-XE
operational_severity must be a Score because Choice questions cannot accept numeric criteria

In the worked example, subsystem is modeled as a Choice rather than a Noul question. Why is Choice the right fit here?

The answer must be one of a fixed set of NOC teams (interface, routing, system, wireless), which is a labeled-category decision, not a yes/no one
Choice questions are cheaper to run than Noul questions because they return fewer output tokens
Noul questions cannot be combined with Score questions in the same request
subsystem needs a numeric rubric like operational_severity, so it must use the same question type

A Splunk forwarder starts handing your triage service hundreds of syslog events per minute, and single-threaded synchronous calls are now the throughput bottleneck. What does the chapter recommend?

Switch every call to AsyncTypeSafeClient so many requests can be in flight at once, rather than waiting on each round trip before starting the next
Keep using TypeSafeClient but reduce the number of questions per call to speed up each request
Switch to jev-preview, which processes requests faster than jev-latest
Increase the timeout parameter so each synchronous call completes further apart

Worked Example: Classifying a Cisco Syslog Line

Key Points

Cisco IOS-XE syslog follows %FACILITY-SEVERITY-MNEMONIC: Message-text. FACILITY names the subsystem (LINEPROTO for line protocol, LINK for physical interface, SYS, OSPF, BGP); the severity digit runs 0–7 (lower = more severe); MNEMONIC is a short uppercase code like UPDOWN.

*Feb 14 09:40:12.418: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet1/0/24, changed state to down

LINEPROTO means a Layer 2 event, not Layer 1 (LINK-3-UPDOWN is the physical-layer counterpart). The 5 is Cisco's own severity, Notice — "normal but significant," typically used for interface state changes. This is exactly where the device's built-in severity stops being enough: Cisco stamps every LINEPROTO-5-UPDOWN as a 5 whether the port is an unused desk drop or a voice uplink. GigabitEthernet1/0/24 is an access-stack port, a strong hint this is an edge port — but the severity digit can't express that. A Score question about operational severity produces a judgment the facility code structurally cannot. The same pattern scales across the NOC: Arista EOS, Junos, and Aruba AOS-CX all implement essentially the same eight severity levels, so one set of typed questions covers the whole multi-vendor estate.

Three questions, one call: subsystem is a Choice (the answer is one of a fixed set of teams), operational_severity is a Score (a graded judgment on an ordered scale), and actionable_tonight is a Noul (paging is yes/no).

triage_questions = {
    "subsystem": Choice(
        instructions="Which NOC team owns the subsystem this message came from",
        criteria={
            "interface": "Physical port, line protocol, or link state events",
            "routing": "OSPF, BGP, EIGRP adjacency and convergence events",
            "system": "Config changes, reloads, process crashes, hw faults",
            "wireless": "Access point, WLAN, or controller events",
        },
    ),
    "operational_severity": Score(
        instructions=(
            "How severe this event is for network operations, considering "
            "scope of impact rather than the severity digit in the message"
        ),
        criteria=[
            "Routine state change on a single edge port, no user impact",
            "Localized issue affecting one access port or one user",
            "Service-affecting issue on an uplink, trunk, or shared segment",
            "Widespread outage affecting a site or many users",
        ],
    ),
    "actionable_tonight": Noul(
        instructions="Needs a human to act during the current shift",
    ),
}

response = client.system_one(state=syslog_line, questions=triage_questions)

Every Choice label carries a description in the vocabulary of a network engineer, the same signal a runbook gives a new NOC hire. The Score criteria are ordered least to most severe, and the instruction explicitly tells the model to reason about scope of impact rather than copy the 5 out of the message — without that instruction, "severity 5" is a reasonable literal answer, and you'd get what you asked for rather than what you meant. One request, three answers, is also the cost argument: you're billed on input tokens, so bundling questions avoids re-sending the same syslog line three times.

Figure 3.3: The syslog triage call as a request/response sequence

sequenceDiagram participant Client as Python Client participant API as TypeSafe API Client->>API: state plus subsystem, operational_severity, actionable_tonight API-->>Client: typed answers with confidence and probabilities Client->>Client: Check confidence threshold Client->>Client: Route to team or escalate to human

The SDK offers both a synchronous TypeSafeClient and an asynchronous AsyncTypeSafeClient, used with await. Synchronous blocking is right for a cron job, a CLI tool, or any script where simplicity beats throughput. Async lets many requests be in flight at once — what you want when a Splunk forwarder hands you hundreds of events a minute.

async def triage(syslog_line: str):
    client = AsyncTypeSafeClient()
    response = await client.system_one(
        state=syslog_line,
        questions=triage_questions,
    )
    return response

response = asyncio.run(triage(syslog_line))

The published rate limits are 250,000 tokens/second and 1,200 requests/minute. A synchronous single-threaded loop hits wall-clock latency as its ceiling long before the API's rate limit — that's usually the trigger to move to async. Note: the documentation confirms the class name and await usage but doesn't publish full async lifecycle details (context manager vs. explicit close); if unsure, start synchronous.

Visual animation — coming soon

Key Takeaway: A complete triage call is one raw syslog line as state and three typed questions — a Choice for owning team, a Score for operational severity, a Noul for whether to page — sent in a single request. Write descriptions and rubric levels in operational language, and state explicitly when you want judgment about impact rather than the severity digit already in the message. Start synchronous; move to async only when volume demands it.
Post-Quiz: Worked Example: Classifying a Cisco Syslog Line

The example syslog line is stamped %LINEPROTO-5-UPDOWN, and Cisco's own severity digit is 5 (Notice) regardless of whether the affected port is an unused desk drop or a voice uplink. Why does the worked example ask a Score question about operational_severity instead of just reading the device's severity digit?

Score questions are the only question type that can process syslog-formatted text
The device's severity digit is fixed per mnemonic and can't distinguish scope of impact, while a Score question can weigh context like which port and what it carries
Cisco severity digits are unreliable and are frequently transmitted incorrectly by IOS-XE
operational_severity must be a Score because Choice questions cannot accept numeric criteria

In the worked example, subsystem is modeled as a Choice rather than a Noul question. Why is Choice the right fit here?

The answer must be one of a fixed set of NOC teams (interface, routing, system, wireless), which is a labeled-category decision, not a yes/no one
Choice questions are cheaper to run than Noul questions because they return fewer output tokens
Noul questions cannot be combined with Score questions in the same request
subsystem needs a numeric rubric like operational_severity, so it must use the same question type

A Splunk forwarder starts handing your triage service hundreds of syslog events per minute, and single-threaded synchronous calls are now the throughput bottleneck. What does the chapter recommend?

Switch every call to AsyncTypeSafeClient so many requests can be in flight at once, rather than waiting on each round trip before starting the next
Keep using TypeSafeClient but reduce the number of questions per call to speed up each request
Switch to jev-preview, which processes requests faster than jev-latest
Increase the timeout parameter so each synchronous call completes further apart
Pre-Quiz: Reading the Response

Your code only needs to check whether an event is actionable tonight, and you want to iterate over just the yes/no answers without touching Choice or Score results. Which response attribute fits best?

response.answers, since it contains every answer type together
response.nouls, the typed collection holding only yes/no answers keyed by question name
response.usage, which reports token counts, not answer values
response.raw_http_response, since it exposes every field the API returned

In the worked example, actionable_tonight.noul returns 0.22 and operational_severity.score returns 1.12. What do these values actually represent?

noul is a boolean True/False, and score is always a whole rubric level such as 0, 1, 2, or 3
noul is the probability of a yes answer on a 0-1 scale, and score is an expected value that can land between rubric levels rather than exactly on one
noul and score are both raw token counts consumed by that specific question
noul is the model's confidence in its Choice answer, and score is the rubric legend text

Your NOC triage service currently sends three separate system_one calls, each resending the full syslog line to ask one question at a time. Why does the chapter say this is an expensive mistake, and what should you do instead?

Output tokens are billed per call, so three calls generate three times the output cost; switch to Noul-only questions to reduce output
Only input_tokens are billed, and resending the same state three times triples the only number that drives cost; bundle all three questions into a single call instead
Each separate call resets the request_id counter, corrupting log correlation; switch to the async client to fix it
Score and Choice questions cannot be billed accurately when issued in separate calls, only when combined

Reading the Response

Key Points

The call returns a SystemOneResponse. answers is "a dictionary of all answer objects keyed by question name" — ask about subsystem, read back answers["subsystem"]. Alongside it, three type-specific collections narrow the view: nouls (yes/no answers), choices, and scores, each keyed by question name. Use answers to iterate uniformly; use the typed collections when code only cares about one kind of answer, such as a paging rule that reads only nouls.

Figure 3.4: Structure of a SystemOneResponse for the syslog triage example

graph TD Response["SystemOneResponse"] --> Answers["answers dictionary"] Response --> Usage["usage"] Response --> RequestId["request_id"] Response --> Model["model"] Answers --> Subsystem["subsystem: choice, confidence, probabilities"] Answers --> Severity["operational_severity: score, confidence, legend"] Answers --> Actionable["actionable_tonight: noul"] Usage --> InputTokens["input_tokens"] Usage --> OutputTokens["output_tokens"]

The response also carries metadata: request_id (the x-typesafe-request-id header), model (which model answered), and raw_http_response (an escape hatch for anything the typed fields don't surface).

FieldPresent onWhat it holds
noulNoulAnswerProbability of a yes answer, 0–1
choiceChoiceAnswerSelected label, one of your criteria keys
scoreScoreAnswerExpected score — not necessarily an integer
confidenceChoiceAnswer, ScoreAnswerHow concentrated the model's belief is
probabilitiesChoiceAnswer, ScoreAnswerPer-label or per-integer-score distribution
legendScoreAnswerThe rubric scale the score is measured against

Two behaviors surprise people coming from traditional APIs. First, a Noul returns a probability, not a boolean — a number you threshold, not a yes you act on. Second, a Score returns an expected score that can be fractional: in the syslog example, 1.12 means the model is mostly on "localized issue affecting one access port" with meaningful weight still on the routine level below it — exactly the right read for a single edge port going down.

subsystem = response.answers["subsystem"]
severity = response.answers["operational_severity"]
actionable = response.answers["actionable_tonight"]

print(f"team: {subsystem.choice}  severity: {severity.score:.2f}  page: {actionable.noul:.3f}")

# Route to a human when the model is not sure enough to act alone
if subsystem.confidence < 0.80:
    print(f"LOW CONFIDENCE — queue for review (request_id={response.request_id})")
else:
    print(f"Assigning to team: {subsystem.choice}")

That confidence gate is the single most important habit to form early. An automation that assigns tickets at 94% confidence and escalates at 60% is trustworthy; one that assigns every ticket with equal conviction is not — the first time it silently routes a core routing failure to the wireless queue, your team stops trusting the whole pipeline.

Visual animation — coming soon

usage reports input_tokens and output_tokens, each an integer or None — handle the None case so a missing field doesn't crash a pipeline. Only input_tokens is billed, at $42 per billion, with output free.

usage = response.usage
input_tokens = usage.input_tokens if usage and usage.input_tokens is not None else 0
cost_usd = input_tokens * 42 / 1_000_000_000
print(f"input_tokens={input_tokens} cost=${cost_usd:.8f} request_id={response.request_id}")

A NOC processing a million syslog events a month at roughly 200 input tokens each lands around 200 million input tokens — a little over eight dollars. The expensive mistake isn't per-event cost; it's re-sending the same state three times because you asked three questions in three separate calls, which triples the only number you're billed on. Bundle your questions, and log input_tokens with request_id together on every call.

One honest limitation: Jev is built for fast, typed judgment, not calculation. Don't ask it to total your token spend or do arithmetic on values inside the state — do the math in Python, and let Jev classify, score, and tell you how sure it is.

Key Takeaway: answers is keyed by your question names and yields noul as a probability, choice as a label, and score as an expected value that may be fractional, each accompanied by confidence and probabilities. Gate automation on confidence rather than acting on every answer identically, and log usage.input_tokens with request_id on every call — input tokens are the only thing billed, at $42 per billion, with output free.
Post-Quiz: Reading the Response

Your code only needs to check whether an event is actionable tonight, and you want to iterate over just the yes/no answers without touching Choice or Score results. Which response attribute fits best?

response.answers, since it contains every answer type together
response.nouls, the typed collection holding only yes/no answers keyed by question name
response.usage, which reports token counts, not answer values
response.raw_http_response, since it exposes every field the API returned

In the worked example, actionable_tonight.noul returns 0.22 and operational_severity.score returns 1.12. What do these values actually represent?

noul is a boolean True/False, and score is always a whole rubric level such as 0, 1, 2, or 3
noul is the probability of a yes answer on a 0-1 scale, and score is an expected value that can land between rubric levels rather than exactly on one
noul and score are both raw token counts consumed by that specific question
noul is the model's confidence in its Choice answer, and score is the rubric legend text

Your NOC triage service currently sends three separate system_one calls, each resending the full syslog line to ask one question at a time. Why does the chapter say this is an expensive mistake, and what should you do instead?

Output tokens are billed per call, so three calls generate three times the output cost; switch to Noul-only questions to reduce output
Only input_tokens are billed, and resending the same state three times triples the only number that drives cost; bundle all three questions into a single call instead
Each separate call resets the request_id counter, corrupting log correlation; switch to the async client to fix it
Score and Choice questions cannot be billed accurately when issued in separate calls, only when combined

Key Terms

TermDefinition
TYPESAFE_API_KEYThe environment variable holding your TypeSafe API key. TypeSafeClient reads it automatically at construction.
TypeSafeClientThe synchronous Python client for the TypeSafe API. Reads TYPESAFE_API_KEY from the environment and defaults to jev-latest.
AsyncTypeSafeClientThe asynchronous counterpart to TypeSafeClient, used with await for concurrent request handling.
system_oneThe client method that sends a System One request. Takes state and questions plus optional model, retry, timeout, extra_headers, extra_body; returns a SystemOneResponse.
synchronous clientA client whose calls block until the response arrives. Appropriate for scripts, cron jobs, and CLI tools.
stateThe content Jev evaluates. Accepts text, a JSON object, or an array.
questions dictionaryA non-empty mapping of question names you choose to Choice, Score, or Noul objects. Keys become the keys of answers.
ChoiceMultiple labeled options, constructed with instructions and a criteria dict mapping label to description. Returns a selected label, confidence, and per-label probabilities.
ScoreA rating along an ordered scale, constructed with instructions and a criteria list from lowest to highest. Returns an expected score, confidence, legend, and per-integer-score probabilities.
NoulA yes/no question type, constructed with instructions alone. Returns the probability of yes on a 0–1 scale, not a boolean.
jev-latestThe alias pointing to jev-1.13.0, the default in client SDKs. Alternatives: pinned jev-1.13.0 and forward-looking jev-preview.
SystemOneResponseThe object returned by system_one: answers, typed collections nouls/choices/scores, usage, request_id, model, raw_http_response.
answersA dictionary of all answer objects keyed by question name.
probabilitiesPer-label (Choice) or per-integer-score (Score) probability distribution behind an answer.
confidenceHow concentrated the model's belief is in its selected answer; present on ChoiceAnswer and ScoreAnswer.
legendThe rubric legend returned with a ScoreAnswer.
usageReports input_tokens and output_tokens, each an integer or None. Only input_tokens is billed.
request_idThe value of the x-typesafe-request-id response header, carried on every response.
POST /v1/systemoneThe single API endpoint used by all TypeSafe models, including every Jev version.
%FACILITY-SEVERITY-MNEMONICThe Cisco IOS-XE syslog message format. FACILITY names the subsystem, SEVERITY is a digit 0–7 (lower = more severe), MNEMONIC identifies the message type.

Your Progress

Answer Explanations