Connect Jev to a Python project
This chapter turns a terminal request into a Python program that reads a file, checks the response, and selects a proposed local queue. It prints a suggestion. It does not issue refunds, send messages, or change accounts.
Both the main example here and the companion bundle use standard-library HTTP. The official SDK appears later as an optional alternative. You can complete all three cases without installing it. Working directly with JSON first makes the boundary visible: what you send, what you receive, and where your application begins making policy decisions.
Prepare the environment and input
Follow Quickstart to prepare Python and TYPESAFE_API_KEY. Check that python works in the current terminal. A newly opened terminal may need its environment activated and key set again.
Create a UTF-8 file called ticket.txt in a practice directory:
My subscription was charged twice this month.
Could someone check the duplicate payment?
Create hello_jev_once.py beside it and paste the program below. This exercise makes one live request and requires a valid key. To run a full workflow without credentials, use the bundle's --mode offline option. Replacing the HTTP result with a sample does not turn that sample into live evidence.
A complete minimal program
import json
import math
import os
import sys
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.request import HTTPRedirectHandler, Request, build_opener
class NoRedirects(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
raise HTTPError(req.full_url, code, "Redirect refused", headers, fp)
opener = build_opener(NoRedirects())
key = os.environ.get("TYPESAFE_API_KEY", "").strip()
if not key:
raise SystemExit("Set TYPESAFE_API_KEY before this live call.")
source = Path(sys.argv[1] if len(sys.argv) > 1 else "ticket.txt")
try:
message = source.read_text(encoding="utf-8").strip()
except (OSError, UnicodeError):
raise SystemExit("Cannot read the input as UTF-8 text.")
if not message:
raise SystemExit("The input is empty.")
criteria = {
"billing": "Only charges, invoices, or subscriptions.",
"technical": "Only login failures or broken product features.",
"mixed": "Both billing and technical concerns are present.",
"other": "Neither category fits, or the message is unclear.",
}
payload = {
"model": os.environ.get("TYPESAFE_MODEL", "jev-latest"),
"state": {"customer_message": message},
"questions": {
"department": {
"type": "choice",
"instructions": (
"Select the queue for `customer_message`. "
"Use only concerns stated in that message."
),
"criteria": criteria,
}
},
}
request = Request(
"https://api.typesafe.ai/v1/systemone",
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": "Bearer " + key,
"Content-Type": "application/json",
},
method="POST",
)
try:
with opener.open(request, timeout=30) as http_response:
result = json.loads(http_response.read().decode("utf-8"))
except HTTPError as exc:
raise SystemExit(f"HTTP {exc.code}; check access and request shape.")
except (URLError, TimeoutError):
raise SystemExit("Network error or timeout; no queue was selected.")
except (json.JSONDecodeError, UnicodeError):
raise SystemExit("The service response was not valid UTF-8 JSON.")
try:
answer = result["answers"]["department"]
choice = answer["choice"]
confidence = answer["confidence"]
valid = (
answer["type"] == "choice"
and choice in criteria
and type(confidence) in (int, float)
and 0 <= confidence <= 1
and math.isfinite(confidence)
)
except (KeyError, TypeError):
valid = False
if not valid:
raise SystemExit("Invalid department answer; manual review needed.")
# Teaching policy only: validate this threshold on your own data.
queue = choice
if confidence < 0.75 or choice in {"mixed", "other"}:
queue = "review"
print(json.dumps({
"mode": "live",
"source_file": source.name,
"suggested_queue": queue,
"raw_response": result,
}, ensure_ascii=False, indent=2))
Run it and save the output:
python hello_jev_once.py ticket.txt > first-live-result.json
python -m json.tool first-live-result.json
Windows can run the same file with the virtual environment's interpreter. A failed command with redirection can leave an empty file, so read the terminal's exit message before treating that file as a result. This program has no background worker and no automatic retry loop. Each execution attempts one HTTP request.
The request uses a fixed API endpoint. NoRedirects rejects server redirects so credentials cannot be forwarded to another destination or plain HTTP. A 3xx response stops the program; check the official endpoint and network configuration before retrying.
The endpoint and fields follow the TypeSafe API reference. The program validates fields used by its local branch and retains other fields for inspection. It is not a comprehensive validator for every possible API response property.
Identify the policy you own
The criteria dictionary lists four allowed values. A returned choice must belong to that set. Its type must match, and confidence must be a finite number between 0 and 1. A malformed response stops the program instead of quietly becoming a default department.
The 0.75 threshold is an illustrative application policy, not a universal TypeSafe safety boundary. This exercise also sends mixed and other to review because it has no cross-team handling procedure. Before changing either rule, write down the cases you intend to improve and check the effect on examples you did not use to tune it.
A valid review result is not a program error. The program may be doing exactly what you specified. Inspect whether the message is ambiguous, the categories overlap, or the policy deliberately reserves that situation for a person. Otherwise, you may “fix” a useful fallback by forcing it into an inappropriate queue.
Read a result in three passes: the original message, raw_response.answers.department, and suggested_queue. Retain the model answer and your derived suggestion separately. If you save only the final queue, you lose evidence needed to analyze a threshold change or explain a disagreement later.
Adapt one boundary at a time
Replacing file input with a database query is one change. Replacing the question with a business-specific rubric is another. Make and verify those changes separately. Pass known, trusted fields directly rather than asking the model to reconstruct them from unrelated prose.
Several questions about the same input can share a questions object. If a later judgment requires an earlier answer, code must arrange the follow-up request; ordering keys in one object does not create a sequential reasoning chain. Building guide
Before production use, develop controlled retries, fuller validation, request records, and durable result storage. A timeout should remain a failed attempt, not become a default class. Authentication failures should not trigger an endless retry loop. The complete cases develop these boundaries, and Troubleshooting gives a practical investigation order.
Practice three boundaries
First, temporarily make ticket.txt empty and run the program. It should stop before making an HTTP request. This verifies an input guard without spending a model call. Restore the message before continuing. Second, run from a fresh terminal without the key variable. The program should identify a configuration problem; missing credentials must not become an other classification.
Third, retain the duplicate-charge report and add “I also cannot log in.” Write the expected behavior before calling the service: the rubric permits mixed, and the local policy sends that outcome to review. With account access, execute and compare. If the result disagrees, inspect the raw response rather than changing only the final queue to hide the discrepancy.
| Layer | Question | Evidence to retain |
|---|---|---|
| Input | Did the intended text reach the program? | Input copy and filename |
| Request | Did it send this rubric and model name? | Request object without credentials |
| Response | Did required fields satisfy their constraints? | Raw response or redacted error |
| Policy | Why did this queue win? | Threshold version and branch rule |
For response-error checks, a coding assistant can use a test double that omits answers and verify that the program stops. Another authored response can exercise the review branch. These establish code behavior, not model quality. Keep invented test values out of performance statistics, and state whether each check made a network request.
Optional alternative: the official Python SDK
The SDK provides typed objects and a client wrapper. This is an alternative integration route, not a dependency needed to run the companion bundle.
python -m pip install typesafe-sdk
python -m pip show typesafe-sdk
from typesafe_sdk import Choice, TypeSafeClient
with TypeSafeClient() as client:
response = client.system_one(
model="jev-latest",
state="Please send a receipt for my subscription.",
questions={
"queue": Choice(
instructions="Choose a queue for the stated request.",
criteria={
"billing": "Receipts and payment questions.",
"other": "Every other request or unclear input.",
},
)
},
)
print(response.answers["queue"].choice)
The distribution is named typesafe-sdk, the import is typesafe_sdk, and the method is system_one. This synchronous pattern was checked against the official documentation, but not executed with credentials for this edition. SDK default retries differ from the one-request HTTP exercise. Record the version you actually install. Python SDK, client overview
Give a coding assistant an inspectable task
Use this brief when asking an assistant to adapt the example. It defines both the deliverable and the failure behavior, so the assistant has less reason to guess a familiar chat API shape.
Build a local support-queue suggestion tool in this project.
Read current TypeSafe API docs: use state/questions/answers.
Call /v1/systemone with jev-latest as the default model.
Read TYPESAFE_API_KEY from the environment; never print its value.
Use Python standard-library HTTP and read a UTF-8 ticket file.
Save the raw response with the derived local queue suggestion.
Keep questions, enum values, and thresholds in one visible place.
Reject malformed input and malformed response fields.
Label offline fixtures and live requests differently.
Never invent usage, billing, latency, or live model outputs.
Write local suggestions only; do not message customers or refund.
Demonstrate normal, empty, malformed, and review-needed cases.
TypeSafe also publishes a coding-agent skill. If you choose to install it in your own environment, the official skill page documents npx skills add typesafe-ai/skills --skill typesafe-ai and agent selection. This optional context package is separate from the Python SDK. Continue reviewing generated request fields and judgment criteria; installing a skill is not integration verification.
The companion bundle's stable entry point is python -m examples.hello_jev, followed by tickets, content, or route, with --mode offline or --mode live. TYPESAFE_MODEL overrides the default model. Preserve both the requested model name and returned identifier so a later comparison has a traceable version.