Try one useful integration
Build a small issue-triage assistant: read a report, propose labels, and defer unclear cases. Edit a request, inspect an example response, and practise evaluating the result before connecting it to real issues.
The project and its issues are fictional; the builder runs locally and sends nothing.
Choose the serving surface
The shared frame is portable; transport and operational settings need their own configuration. Pick a model in the builder below. It generates Jev SDK examples, a Workers AI binding for Clef, a Kev SDK client for a local server, or HTTP examples for Laya.
Models and providers covers the contracts. The response reader and detailed retry example later on this page remain explicitly Jev examples.
Start with Jev’s hosted API
Keep the TypeSafe key in your server environment. Choose an SDK or send HTTP requests directly. The snippets below show the starting point; follow the linked SDK reference for version-specific configuration.
pip install typesafe-sdk # or: uv add typesafe-sdk
export TYPESAFE_API_KEY=sk-... # from the TypeSafe consolenpm install @typesafe-ai/sdk # needs Node 20+
export TYPESAFE_API_KEY=sk-...export TYPESAFE_API_KEY=sk-...
# POST https://api.typesafe.ai/v1/systemone with Authorization: Bearer $TYPESAFE_API_KEYThese examples pin jev-1.14.0. As checked October 15, jev-latest still selects 1.13; jev-preview selects 1.14. See the current aliases before choosing one.
The optional TypeSafe agent skill supplies API guidance to a coding assistant. You can use this tutorial without installing it.
Prepare a local decision service
The Kev and Laya builder examples assume a separately running service on the loopback address. These commands are for your environment; the guide does not run them. Model loading downloads weights and needs suitable hardware. Follow the project’s setup reference for runtime versions and serving configuration.
git clone https://github.com/jaredpalmer/kev.git
cd kev
uv sync --extra serve
uv run --extra serve python -m kev.serve --run jaredpalmer/kev-4b@v1.0 --port 8009The Hub revision pins the loaded weights. Record the serving-code revision and calibration settings too. See Kev setup.
pip install "laya[serve]"
LAYA_HOST=127.0.0.1 LAYA_PORT=8000 laya-serveThe builder selects english or multilingual explicitly. For an authenticated server, configure LAYA_API_KEY and give the client that value as DECISION_API_KEY. Record the installed library and model revisions. See Laya serving.
For hosted Clef, configure a Workers AI binding for the TypeScript example, or supply CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_AUTH_TOKEN for REST. See the model’s usage examples.
A Jev request, annotated
A fictional queue library receives a report that its consumer stops after a broker restart. We want to propose an issue type, identify missing information, and assess the described impact if it is a defect. Keep the first version modest: suggest labels for a maintainer to review. Sending comments or paging an engineer adds consequences that need their own acceptance rules and duplicate-action protection.
{
"state": {
"repo": "meridian/relay-queue",
"issue": {
"title": "Consumer stops pulling after broker restart",
"body": "After we restart the broker, the consumer logs reconnected once and then idles forever. Jobs pile up until the process is restarted. Happens on 3.2.0, worked on 3.1.x. No stack trace, CPU flat. Config attached.",
"author_association": "CONTRIBUTOR",
"comments": 0
}
},
"model": "jev-1.14.0",
"questions": {
"kind": {
"type": "choice",
"instructions": "What kind of issue is `issue`, judged from its title and body?",
"criteria": {
"bug": "Behaviour that used to work or is documented does not behave as described",
"feature": "A request for behaviour the project does not claim to have",
"question": "The author is asking how to do something, not reporting a fault",
"docs": "The code is fine but the documentation is wrong or missing",
"none_of_these": "Spam, empty, or off-topic for this repository"
}
},
"needs_info": {
"type": "noul",
"instructions": "Would a maintainer have to ask the author for more before they could start work on `issue`?",
"criteria": {
"true": "Something needed to act is missing: a version, a reproduction, logs, or configuration",
"false": "The report contains enough to reproduce the problem or to decide what to do"
}
},
"severity": {
"type": "score",
"instructions": "If `issue` describes a defect, how bad is its impact on a user of the library?",
"criteria": [
"Cosmetic or a minor annoyance",
"A feature is degraded but a workaround exists",
"A feature is unusable and no workaround is described",
"Data loss, corruption, or a security exposure"
]
}
}
}
- The state is a JSON object, not a blob. Named fields let the instructions point at parts of it with backticked paths such as
issue. Facts code already knows, like the author's association, ride along as fields rather than being asked. - Jev IDs exist for your code. The docs are explicit that the id never reaches the model, so the meaning has to be complete inside
instructionsandcriteria. A key likekindtells the model nothing. - Each type has its own criteria shape. Choice takes a map of option to description, with
nullallowed when an option needs none. Score takes an ordered array of at least two level descriptions. Noul's criteria are optional and describe what yes and no mean. - There is always somewhere for the mass to go.
none_of_thesegives the Choice an exit. Without it, an off-topic issue is forced into the closest wrong label with an honest-looking probability. - The severity question is speculative. It only matters when
kindcomes back as a bug, but it is asked in the same request because the state is already there. See fan-out.
Request builder
Choose a model, then edit the state and questions; the JSON body, curl command, Python, and TypeScript update as you type. Prefilled with the triage example. Nothing is sent anywhere.Adds the opt-in header. Both context limits become 262k; TypeSafe reports slower responses and lower accuracy. When to use it →
Prototype photo intake with Jev 1.14
The repair desk can ask a customer for a wheel photo, then propose a close-up for a mechanic. Ask presence and location together; code consumes the box only when presence is supported. Keep the full photo beside the proposed crop.
TypeSafe marks images and Box as experimental, with changing formats, limits, and accuracy. Both need the images header; Box needs 1.14. Prepare an upright PNG or JPEG yourself. Each side must be 256–1568 pixels and total area at least 78,400 pixels; a 256 × 256 image fails. The API rejects oversized images rather than resizing them. See all image and expanded-request limits.
Save your prepared image as bike-intake.png. The curl example uploads it as a multipart file. The Python example uses the standard library and base64, validates the answer, and returns a proposal for human inspection. Run these on your server with TYPESAFE_API_KEY; this page sends nothing.
curl https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "TypeSafe-Danger-Zone-Images: true" \
-F 'request={
"model": "jev-1.14.0",
"state": {"intake_photo": "f44ac49b-b7a1-4614-9d98-01865b847bc8"},
"assets": {"f44ac49b-b7a1-4614-9d98-01865b847bc8": {"type": "image", "part": "intake-file"}},
"questions": {
"wheel_present": {"type": "noul", "instructions": "Is the bicycle front wheel visible in `intake_photo`?"},
"wheel": {"type": "box", "instructions": "The bicycle front wheel in `intake_photo`"}
}
};type=application/json' \
-F 'intake-file=@bike-intake.png;type=image/png'import base64
import json
import math
import os
import uuid
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
def probability(value):
return type(value) in (int, float) and math.isfinite(value) and 0 <= value <= 1
def propose_crop(payload):
try:
if payload["model"] != "jev-1.14.0":
raise ValueError("Unexpected model")
answers = payload["answers"]
presence = answers["wheel_present"]
p = presence["noul"]
if presence["type"] != "noul" or not probability(p):
raise ValueError("Invalid presence answer")
if p < 0.2: # Example rules; tune with labelled photos.
return {"status": "no_target", "next": "ask_for_photo"}
if p < 0.8:
return {"status": "review", "next": "inspect_original"}
answer = answers["wheel"]
b = answer["box"]
if answer["type"] != "box" or not probability(answer["confidence"]):
raise ValueError("Invalid Box answer")
if not all(probability(b[k]) for k in ("left", "top", "right", "bottom")):
raise ValueError("Invalid edge")
if not (b["left"] < b["right"] and b["top"] < b["bottom"]):
raise ValueError("Reversed or empty box")
if answer["confidence"] < 0.8:
return {"status": "review", "next": "inspect_original"}
return {"status": "proposed_crop", "box": b, "next": "human_inspection"}
except (KeyError, TypeError, ValueError):
return {"status": "no_judgment", "next": "inspect_original"}
def inspect_photo():
# Supply an upright, prepared PNG. Base64 is convenient for this small example;
# multipart avoids its size overhead for larger uploads.
image = Path("bike-intake.png").read_bytes()
ref = str(uuid.uuid4())
body = {
"model": "jev-1.14.0",
"state": {"intake_photo": ref},
"assets": {ref: {"type": "image", "format": "png",
"data_base64": base64.b64encode(image).decode("ascii")}},
"questions": {
"wheel_present": {"type": "noul", "instructions":
"Is the bicycle front wheel visible in `intake_photo`?"},
"wheel": {"type": "box", "instructions":
"The bicycle front wheel in `intake_photo`"},
},
}
request = Request("https://api.typesafe.ai/v1/systemone",
data=json.dumps(body).encode("utf-8"), method="POST",
headers={"Authorization": f"Bearer {os.environ['TYPESAFE_API_KEY']}",
"Content-Type": "application/json",
"TypeSafe-Danger-Zone-Images": "true"})
try:
with urlopen(request, timeout=15) as response:
payload = json.load(response)
except HTTPError as error:
if error.code not in (408, 429) and error.code < 500:
raise # Correct rejected inputs or credentials before trying again.
return {"status": "no_judgment", "next": "inspect_original"}
except (URLError, TimeoutError, OSError, ValueError):
return {"status": "no_judgment", "next": "inspect_original"}
return propose_crop(payload)
if __name__ == "__main__":
print(json.dumps(inspect_photo()))For a crop, convert left/right × width and top/bottom × height to pixels in the prepared image. Round outer edges outward and check the resulting crop is nonempty. A box from a resized or cropped input needs mapping back to the original coordinates. Explore the coordinate lab.
The example cutoffs are teaching rules, not calibrated defaults. Evaluate presence errors, missing targets, multiple wheels, blur, cut-off edges, box overlap, and reviewer decisions on your own photos. An upload error is a failed request, never evidence that a wheel is absent. Sources: image inputs and Box semantics.
Opt into long context only when needed
Keep the builder’s default 64k request and 32k state-plus-question budgets for the first version. If a repair history truly needs more room, enable long context to add TypeSafe-Danger-Zone-Long-Context: true to curl and both SDK examples. It raises both limits to 262k on Jev 1.13 and 1.14. It does not raise image byte or pixel limits.
TypeSafe reports slower responses and lower accuracy in this mode; pricing per token is unchanged. Compare against retrieving only the relevant service visits. Measure answer quality and full request time separately, and record the mode with the model version in evaluation and cache metadata. Long-context reference.
Response reader
An illustrative Jev response to the triage request. Click a field to see what it means and the line of code that reads it.type. Choice and Score answers carry confidence, derived from the distribution; Noul answers carry only the probability. For Jev, output tokens are billed at zero under the documented pricing, so the number that matters for cost is input_tokens.Failures and service operations
Normalize the chosen service’s outer response, validate every expected answer, and retain missing or malformed answers as failures. Cloudflare REST wraps the model payload in result; Workers bindings return the model payload. Local servers have their own configured authentication, deadlines, and concurrency. The status table and SDK retry code below describe Jev.
Treat a failed request separately from an uncertain answer. A timeout gives you no judgment at all. Keep the issue in a review queue so the failure remains visible.
| Status | Documented meaning | What your code should do |
|---|---|---|
| 400 / 413 / 415 | Request rules or context exceeded, payload too large, unsupported encoding | Fix the input before retrying. Long context does not increase the 32 MiB body limit or image size limits. |
| 401 | Missing or invalid API key | Do not retry. Fix the Authorization header or the environment variable, and alert: this is a deploy problem, not a traffic problem. |
| 422 | Request failed validation; the body names the offending field | Do not retry. Inspect a sanitized diagnostic and fix the question shape. Common causes: a Score with one level, a Choice with no criteria map, a missing type. |
| 429 | Rate limit exceeded | Retry with exponential backoff and honor retry-after when present. Both SDKs do this by default. |
| 529 | Service temporarily overloaded | Same as 429. Cap the retries and fall through to your cascade's next stage rather than blocking the request path. |
Use bounded retries and a deadline covering attempts and waits. The current Python RetryPolicy reference defines its timeout as a total SDK-call retry budget. Connection failures, exhausted retries, and invalid successful responses all need a no-judgment path. Prefer the SDK for application integrations; our evaluation helpers have their own retry configuration.
Retries can repeat evaluation. Protect downstream actions with the issue ID and a processing version so a retry cannot post the same comment or apply the same operation twice.
Watch both request rate and token rate. Batching reduces repeated input and request count, but larger batches still consume tokens. Consult current limits when sizing traffic.
from typesafe_sdk import (
RetryPolicy, TypeSafeClient, TypeSafeAPIError,
TypeSafeAPIConnectionError, TypeSafeAPIResponseValidationError,
)
# QUESTIONS and build_state come from your reviewed request.
MODEL = "jev-1.14.0" # evaluate this version before enabling actions
ACCEPT_CONFIDENCE = 0.85 # illustrative; select on your labelled data
client = TypeSafeClient(retry=RetryPolicy(max_retries=2, timeout=3.0))
def propose_label(issue):
try:
result = client.system_one(
state=build_state(issue), questions=QUESTIONS, model=MODEL
)
except (TypeSafeAPIConnectionError, TypeSafeAPIResponseValidationError):
return {"status": "no_judgment", "next": "manual_triage"}
except TypeSafeAPIError as error:
if error.status not in (408, 429) and error.status < 500:
raise # request or configuration fault; surface it
return {"status": "no_judgment", "next": "manual_triage"}
answer = result.choices["kind"]
if answer.choice == "none_of_these":
return {"status": "no_match", "next": "manual_triage"}
if answer.confidence < ACCEPT_CONFIDENCE:
return {"status": "review", "answer": answer.choice}
return {"status": "suggestion", "answer": answer.choice}
# A suggestion is data. Authorize and deduplicate any later write in code.
Evaluate the suggested action before enabling writes
Replay historical issues with the reviewed frame and action policy. Report accepted accuracy, coverage over all issues, class recall, no-match cases, and failures. Make sure historical labels do not depend on evidence your request omits.
Use the evaluation protocol to separate design, threshold selection, and final reporting. The small sheet below explains cumulative counts; it is a visual aid for proposing a cutoff, not evidence that the cutoff is safe.
Eval sheet
Forty synthetic labelled issues with the model'skind answer and confidence. Change the band width and the target accuracy to see where an "act" threshold would land. The sample is generated in the page and is illustrative only.
| Confidence band | n | Accuracy | Acc. above | Coverage above |
|---|
| Issue | Label | Answer | Conf. |
|---|
Keep the production contract explicit
- Keep credentials server-side. Sanitize diagnostics. Raw responses and evaluation records can contain source text; key redaction does not remove all sensitive content.
- Version the inputs. Record the question version, state-building version, policy version, and service’s reported
model, plus the loaded weights revision, tokenizer, calibration, and runtime for local models. An echoed alias is not a weights pin. Pin the tested deployment after selecting thresholds; evaluate replacements before switching. - Cache the complete judgment input. Include a digest of the actual evidence and relevant policy, roster, or related records, together with question ID/version and resolved model version. Include all jointly scored questions for Clef, not only the answer being read. An unchanged issue ID does not mean unchanged content.
- Handle aliases deliberately. Record image content digests and opt-in modes as part of the request identity. A cache keyed only by
jev-latestcannot distinguish model releases. Resolve/version entries before reuse; do not combine multiple versions into one evaluation claim. - Make effects idempotent. Use a separate operation key for applying a label, posting a comment, or sending an alert. Retrying inference must not duplicate downstream work.
- Check freshness and authorization. Before writing, confirm the issue and relevant policy still match the evaluated state. A typed suggestion or high confidence does not authorize an operation.
- Retain a visible fallback. Distinguish no match, low acceptance, and no judgment. Collect corrections with suitable access and retention so changes can be evaluated.
The production contract in our agent skill explains these boundaries. Use the official SDK reference for installed-version behaviour.