Jev call copilot CurrencyTransfer research and architecture built 2026-09-24

Jev on Cloudflare — builder's guide

Audience: an engineer building a substantial Cloudflare Workers project on typesafe/jev. Dense reference, not a tutorial. Citation prefixes: TS/ = /tmp/claude-1000/-home-stevan-dev/4bdbd082-4e99-49ae-a4be-bacdb74171a7/scratchpad/docs/typesafe/, CF/ = /tmp/claude-1000/-home-stevan-dev/4bdbd082-4e99-49ae-a4be-bacdb74171a7/scratchpad/docs/cloudflare/. Full paths in section 9. Where sources disagree, both are cited and the conflict is stated. "Community" = third-party repos and issues, not vendor docs.

Snapshot: 2026-09-24. Every documented Cloudflare sample answers as jev-1.13.0 [CF/ai__models__typesafe__jev.md].


1. What Jev is

  1. Jev is TypeSafe's "System One" model: it reads natural language like an LLM but never generates text; it returns typed decisions with calibrated probabilities [TS/concepts__system-one.md].
  2. Input is one state (string, JSON object, or array; text only, English-first) plus a map of named questions [TS/concepts__state.md] [TS/models.md].
  3. Three question types: Noul (yes/no → P(yes) in 0..1), Choice (one of up to 255 options → per-option probabilities + confidence), Score (2–10 ordered levels → probability-weighted score + legend + probabilities + confidence) [TS/api.md] [TS/primitives.md].
  4. Every question in a request is evaluated in parallel and in isolation against the same state; no answer is hidden context for another [TS/primitives.md].
  5. Calibration is the promise: across many predictions, outcomes assigned 0.8 occur about 80% of the time. A population property, not a per-answer guarantee [TS/introduction__machine-learning-primer.md].
  6. Trained with RLCD ("reinforcement learning for calibrated decisions"), not RLHF; same weights for every account, no per-customer fine-tuning [TS/models.md] [TS/introduction__machine-learning-primer.md].
  7. Latency claim: about 100 ms per request on TypeSafe's endpoint [TS/concepts__how-to-build-with-system-one.md]. No Cloudflare-route latency figure exists in any source.
  8. Direct-API price: $0.042 per million input tokens, output free [TS/models.md]. Cloudflare's price is dashboard-only [CF/ai__models__typesafe__jev.md].
  9. Intended architecture: code owns control flow, rules and side effects; Jev answers atomic questions; you combine answers with weights and thresholds in code [TS/concepts__how-to-build-with-system-one.md].
  10. Not a drop-in LLM for coding agents and not a text generator; forcing generation by chaining Choices is documented as slow and poor [TS/introduction__coding-agents.md] [TS/model-jaggedness__jev-1.13.md].

2. Request/response contract

2.1 Input schema (Cloudflare input object)

From [CF/jev-schema-input.json]:

Structured instructions/criteria are how you embed data from code and reference paths with backticks: `ticket.messages[0].text` into state, or `potential_duplicate` into a sibling instruction field [TS/concepts__how-to-build-with-system-one.md] [TS/primitives__advanced.md]. Field names used in the docs (question, focus, compare, inspect, what, not_for, examples, signals) are conventions, not reserved keywords; the model sees them as labels [TS/primitives__choice.md] [TS/primitives__advanced.md]. Question ids are never sent to the model; put the whole question in instructions [TS/api.md] [TS/primitives.md].

2.2 Output schema

From [CF/jev-schema-output.json]:

2.3 Full request (Cloudflare REST)

[CF/ai__models__typesafe__jev.md]

curl https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/ai/run \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "model": "typesafe/jev",
  "input": {
    "state": "Help! My payouts have been failing for 3 days.",
    "questions": {
      "is_urgent": {
        "type": "noul",
        "instructions": "Does this convey urgency?",
        "criteria": { "true": "Explicitly time-sensitive", "false": "No urgency expressed" }
      },
      "department": {
        "type": "choice",
        "instructions": "Which team should handle this?",
        "criteria": {
          "billing": "Payments, invoicing, refunds",
          "technical": "Bugs, outages, integrations",
          "sales": "Pricing, upgrades, new accounts"
        }
      },
      "frustration": {
        "type": "score",
        "instructions": "How frustrated is the customer?",
        "criteria": ["Calm", "Frustrated", "Very angry"]
      }
    }
  }
}'

2.4 Full response (evaluation object)

[CF/ai__models__typesafe__jev.md]

{
  "model": "jev-1.13.0",
  "answers": {
    "is_urgent": { "type": "noul", "noul": 0.95 },
    "department": {
      "type": "choice",
      "choice": "billing",
      "confidence": 0.8,
      "probabilities": { "billing": 0.87, "sales": 0, "technical": 0.13 }
    },
    "frustration": {
      "type": "score",
      "score": 1.04,
      "confidence": 0.94,
      "legend": { "0": "Calm", "1": "Frustrated", "2": "Very angry" },
      "probabilities": { "0": 0, "1": 0.96, "2": 0.04 }
    }
  },
  "usage": { "input_tokens": 426, "output_tokens": 73 }
}

Returned probabilities key order differs from request order; zero-probability options are included [CF/ai__models__typesafe__jev.md].

2.5 Native /v1/systemone vs Cloudflare /ai/run

TypeSafe direct Cloudflare
Endpoint POST https://api.typesafe.ai/v1/systemone [TS/api.md] POST https://api.cloudflare.com/client/v4/accounts/{id}/ai/run, or env.AI.run('typesafe/jev', {state, questions}) [CF/ai__models__typesafe__jev.md]
Auth Bearer $TYPESAFE_API_KEY from console.typesafe.ai/keys [TS/introduction__quickstart.md] Bearer $CLOUDFLARE_API_TOKEN [CF/ai-gateway__usage__rest-api.md]
Body {state, model, questions}, all required; model ∈ jev-latest, jev-preview, jev-1.13.0 [TS/api.md] [TS/models.md] {"model":"typesafe/jev","input":{state, questions}}; model inside input violates the schema [CF/jev-schema-input.json]
Version pinning Pass the versioned id [TS/models.md] None; only typesafe/jev is documented [CF/ai__models__typesafe__jev.md]
Response Bare {model, answers, usage} [TS/api.md] Binding: bare per docs. REST: see 4.7
Errors 401, 422 (names the field), 429, 529 Overloaded [TS/api.md] {success:false, errors:[{code,message}]} (community, 4.8)
Context 64k/request; 32k for state + longest question [TS/models.md] "32,000 tokens" [CF/ai__models__typesafe__jev.md]. Conflict; budget 32k
SDKs @typesafe-ai/sdk v0.6.0 (Node 20+), typesafe-sdk 0.7.1 [TS/sdk__javascript.md] [TS/sdk__python__changelog.md] baseURL/base_url works only against endpoints that implement the TypeSafe OpenAPI spec; the Python docs show OpenRouter (~typesafe/jev-latest) and Vercel AI Gateway (typesafe-ai/jev) [TS/sdk__python__usage.md] [TS/sdk__python__changelog.md]. Cloudflare's /ai/run wraps the body in {model, input} while the SDK posts {state, model, questions} to /v1/systemone, so it does not fit; untested in any source [TS/sdk__javascript__api__interfaces__SystemOneRequestPayload.md] [CF/jev-schema-input.json]

Hand-build the question JSON (identical to what the SDK helpers emit) and call env.AI.run directly. Hand-building is a Cloudflare-specific cost, not a Jev-wide one: if SDK typing, retry policy and request ids matter more than staying inside Cloudflare billing, Vercel AI Gateway and OpenRouter are the documented SDK-compatible alternatives [TS/sdk__python__usage.md]. Derived types (from the two schemas, not verbatim from any doc):

type JsonValue = string | number | boolean | null | JsonValue[] | { [k: string]: JsonValue };
type Entry = string | { [k: string]: JsonValue } | JsonValue[] | null;

export type JevQuestion =
  | { type: 'noul'; instructions: Entry; criteria?: { true?: Entry; false?: Entry } | null }
  | { type: 'choice'; instructions: Entry; criteria: Record<string, Entry> }     // native max 255
  | { type: 'score'; instructions: Entry; criteria: [Entry, Entry, ...Entry[]] }; // 2..10 native

export interface JevInput { state: string | { [k: string]: JsonValue } | JsonValue[] | null; questions: Record<string, JevQuestion>; }

export type JevAnswer =
  | { type: 'noul'; noul: number }
  | { type: 'choice'; choice: string; probabilities: Record<string, number>; confidence: number }
  | { type: 'score'; score: number; legend: Record<string, Entry>; probabilities: Record<string, number>; confidence: number };
  // legend: the Cloudflare output schema types values as `string`, narrower than observed behaviour; structured levels
  // (objects such as {what, examples}) are echoed back as objects [TS/primitives__score.md] [CF/jev-schema-output.json].

export interface JevOutput { model: string; answers: Record<string, JevAnswer>; usage: { input_tokens: number; output_tokens: number } }

Overflow is undocumented on both sides: no tokenizer is published, no error code is specified for an over-budget state, and truncation is not described. TypeSafe's generic 422 "failed validation" is the closest documented error, and how Cloudflare relays it is unknown [TS/models.md] [TS/model-jaggedness__jev-1.13.md] [CF/ai__models__typesafe__jev.md] [TS/api.md] [CF/jev-schema-input.json]. Estimate tokens conservatively (≈ 4 chars/token), keep state + longest question well under 32k, and chunk long documents in code (one request per chunk, or the Noul relevance filter from the RAG cookbook [TS/cookbooks__classifying_rag_passages.md]). Treat any 4xx from an oversized request as a hard failure, not a retry.


3. Semantics

3.1 Probability vs confidence, per type

3.2 The confidence formula

Unpublished. The docs' demo approximates it as (n × max_probability − 1) / (n − 1) clamped to [0,1] and promises a cookbook [TS/confidence.md]. It reproduces every published Choice example and every 3-level Score example to within rounding (0.87/3 → 0.805 ≈ 0.8; 0.96/3 → 0.94; 0.85/3 → 0.775 ≈ 0.78) [CF/ai__models__typesafe__jev.md] [TS/introduction__quickstart.md], but not the 4-level Score example (max 0.52 → reported 0.52, formula 0.36) or the 5-level one (max 0.86 → 0.89, formula 0.83) [TS/primitives__score.md]. Do not depend on it; if you need a defined statistic, compute your own from probabilities, which the docs explicitly permit [TS/confidence.md].

3.3 Calibration and repeatability

Across groups of predictions, a 0.2 fires ~20% of the time and a 0.95 ~95% [TS/introduction__machine-learning-primer.md]; a single 0.95 can still be wrong. Repeatability is high but not deterministic: identical-except-uid calls gave per-question std ≈ 0.01, yet one Noul ranged 0.43–0.53 across 15 calls and two of eight Choice argmaxes flipped [TS/cookbooks__consistency_noul_cookbook.md] [TS/cookbooks__consistency_choice_cookbook.md]. Design bands, not knife-edges.

No structural invariants hold across questions: Noul "refund" + Noul "not refund" summed to 1.19; Noul refund = 0.22 while the same ticket as a yes/no Choice gave no = 0.99 at confidence 0.97. Thresholds do not transfer between types [TS/model-jaggedness__jev-1.13.md].

3.4 How to threshold

3.5 Legend, level ordering, option ordering

Levels are zero-indexed by array position: a 3-item rubric scores in [0, 2]. The model sees only level descriptions, never numbers or neighbours; "worse than the previous level" means nothing, and numbers-only criteria ['0','1','2'] split probability (0.55, confidence 0.33) where descriptive levels gave 0.0 at 1.0 [TS/primitives__score.md]. One dimension per Score; give rare extremes their own level [TS/primitives__score.md]. Do not interpolate a score to recover a magnitude between levels; thresholding is fine, reconstruction is not [TS/model-jaggedness__jev-1.13.md]. Choice option order is part of the question [TS/cookbooks__hierarchical_classification.md].


4. Cloudflare integration

4.1 Catalog facts

Model id typesafe/jev, labelled "Text Generation" / "Third-party", context window 32,000 tokens, terms link to https://docs.typesafe.ai/legal.md, pricing "View pricing in the Cloudflare dashboard" (https://dash.cloudflare.com/?to=/:account/ai/models/typesafe/jev) [CF/ai__models__typesafe__jev.md]. "Text Generation" is a catalog category only; there is no response or streaming field.

4.2 Wrangler binding

[CF/workers-ai__configuration__bindings.md] [CF/workers-ai__get-started__workers-wrangler.md]

// wrangler.jsonc
{ "ai": { "binding": "AI" } }
# wrangler.toml
[ai]
binding = "AI"
export interface Env { AI: Ai }

export default {
  async fetch(req: Request, env: Env): Promise<Response> {
    const r = (await env.AI.run('typesafe/jev', {
      state: { ticket: { message: 'I was charged twice for order A-104.' } },
      questions: {
        refund_requested: { type: 'noul', instructions: 'Does `ticket.message` request a refund?' },
      },
    })) as JevOutput;
    return Response.json(r);
  },
} satisfies ExportedHandler<Env>;

Scaffold: npm create cloudflare@latest -- hello-ai (Hello World, Worker only, TypeScript); Wrangler needs Node ≥ 16.17.0. npx wrangler dev always accesses your Cloudflare account to run AI models and incurs usage charges even in local development [CF/workers-ai__get-started__workers-wrangler.md]. Pages Functions cannot declare the AI binding in wrangler config; use the dashboard [CF/workers-ai__configuration__bindings.md]. No source says whether @cloudflare/workers-types types typesafe/jev; cast to the types in 2.5.

4.3 Gateway option

Third-party requests route through the auto-created default gateway with no header; pass cf-aig-gateway-id: <id> (REST) or { gateway: { id } } as the third env.AI.run argument to choose a gateway, whose caching, rate limiting, guardrails and logging then apply [CF/ai-gateway__usage__rest-api.md] [CF/ai-gateway__features__unified-billing.md]. Per-request headers: cf-aig-skip-cache, cf-aig-cache-ttl, cf-aig-cache-key, cf-aig-collect-log, cf-aig-request-timeout (ms), cf-aig-max-attempts (≤ 5), cf-aig-retry-delay (ms, ≤ 60000), cf-aig-backoff (constant|linear|exponential), cf-aig-metadata (JSON) [CF/ai-gateway__usage__rest-api.md]. The Unified Billing page lists six provider-native endpoints (OpenAI, Anthropic, Google AI Studio, Google Vertex AI, xAI, Groq) and the get-started page says the provider list is longer ("and more...") [CF/ai-gateway__features__unified-billing.md] [CF/ai-gateway__get-started.md]. No local source documents a TypeSafe provider-native path; env.AI.run and /ai/run are the only documented routes for typesafe/jev. Community measurement: path-style POST /ai/run/typesafe/jev returns 400 "No route for that URI"; only the body form works [https://github.com/yottayoshida/jev-intent-review].

4.4 REST endpoint

POST https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/run with Authorization: Bearer <token> and the {model, input} envelope [CF/ai-gateway__usage__rest-api.md]. The /ai/v1/* chat-shaped endpoints do not fit Jev. Background mode (options: { background: true, webhookUrl }) exists but is intended "for long-running models — such as image, video, or audio generation". Do not use it for Jev: the call takes ~100 ms [TS/concepts__how-to-build-with-system-one.md], "webhook delivery is best-effort and is not retried", and the destination must be an HTTPS URL that does not resolve to a private network address; webhookUrl without background: true → 400 [CF/ai-gateway__usage__rest-api.md]. Call synchronously. Account id: wrangler whoami [CF/ai-gateway__usage__rest-api.md] or the Workers AI "Use REST API" panel [CF/workers-ai__get-started__rest-api.md]; it is 32 hex chars, which community clients validate before interpolating [https://github.com/clouatre-labs/decisions-judge-mcp].

4.5 Token: exact steps and permissions

You create it yourself; nothing can mint it for you.

Choose the token kind first. The Workers AI template and the Custom flow under My Profile → API Tokens create user tokens bound to you (the template token "will be visible on your profile"); for a service integration create an Account API token (Manage Account → API Tokens) so it is not tied to a person, after checking the compatibility matrix for the /ai/* endpoints [CF/fundamentals__api__get-started__create-token.md] [CF/workers-ai__get-started__rest-api.md]. A production Worker/REST integration on a personal token breaks when that person leaves. /user/tokens/verify (below) is the user-token verify path; account tokens have their own (not in the captured sources).

  1. Template: Workers AI page → Use REST API → Create a Workers AI API Token → Create → copy. The same panel shows the Account ID [CF/workers-ai__get-started__rest-api.md].
  2. Custom: https://dash.cloudflare.com/profile/api-tokens/ → Create Token → Custom → name → permissions → select the resources (the account) the token may access → optional IP filter and TTL → Continue to summary → Create Token → copy. Shown once; cfut_ prefix [CF/fundamentals__api__get-started__create-token.md].
  3. Local: npx wrangler login then npx wrangler auth token. Cloudflare's own /ai/run examples use wrangler auth token as the bearer token, so it is documented as working for this endpoint; no page lists which permission scopes it carries [CF/ai-gateway__usage__rest-api.md] [CF/ai-gateway__get-started.md].

Verify: GET https://api.cloudflare.com/client/v4/user/tokens/verify → status: "active", message code 10000 "This API Token is valid and active" [CF/fundamentals__api__get-started__create-token.md]. Code 10000 is also the 401 error code on /ai/* for a token lacking Workers AI permission [CF/ai-gateway__usage__rest-api.md]; do not branch on the code alone.

Permissions — sources conflict: Account > Workers AI > Read suffices for all /ai/* endpoints; AI-Gateway-only tokens get 401 code 10000 [CF/ai-gateway__usage__rest-api.md]. A custom token needs Workers AI Read and Edit [CF/workers-ai__get-started__rest-api.md]. Recommended set: AI Gateway Read + Edit, Workers AI Read [CF/ai-gateway__get-started.md]. Community live-verified: Workers AI Edit alone works; 403 = missing permission [https://github.com/clouatre-labs/decisions-judge-mcp/issues/40]. Safe choice: Workers AI Read + Edit plus AI Gateway Read + Edit.

4.6 Unified billing: credits, fee, top-up, limits, BYOK

4.7 Response envelope (REST vs binding)

The Jev page shows a bare {model, answers, usage} after both examples [CF/ai__models__typesafe__jev.md]; the generic Workers AI REST doc shows {result, success, errors, messages} [CF/workers-ai__get-started__rest-api.md]. Community live capture of /ai/run (2026-09-21):

{
  "result": {
    "state": "Completed",
    "result": { "model": "jev-1.13.0", "answers": { "q1": { "type": "noul", "noul": 0.99 } },
                "usage": { "input_tokens": 292, "output_tokens": 21 } },
    "gatewayMetadata": { "keySource": "Unified" }
  },
  "success": true, "errors": [], "messages": []
}

[https://github.com/clouatre-labs/decisions-judge-mcp/issues/40], independently matched by [https://github.com/yottayoshida/jev-intent-review]. Over REST the evaluation is at result.result, two levels down, not the bare sample the Jev page shows. This matches the run object Cloudflare documents for background webhooks — {id, state, result, error, provider, model, usage} — placed inside the standard v4 {result, success, errors, messages} envelope [CF/ai-gateway__usage__rest-api.md] [CF/workers-ai__get-started__rest-api.md]; the shape is vendor-supported, not merely community-observed. So for sync REST read the evaluation at body.result.result, check body.result.state === "Completed" and body.result.error, and log body.result.id as the Cloudflare-side request id. Expect a run-level usage as well as result.result.usage; treat the inner one as the TypeSafe figure. An unmerged community PR reads json.result.answers and was never tested against a real success [https://github.com/gargpratyush/jev-router]. No captured binding response has been published. Unwrap defensively: walk .result until an object with answers appears.

4.8 Errors you will see

The binding has none of the SDK's retry policy. Mirror the SDK defaults in your Worker: retry on 408, 429 and 500–599 (529 included), max 2 retries, backoff 500 ms doubling to 5 s with 25% jitter, honour Retry-After/retry-after-ms up to 60 s; fail fast on 400/401/402/403/422 [TS/sdk__javascript__api__interfaces__RetryPolicy.md] [TS/sdk__python__api__retries.md]. There is no idempotency key in either API and none is needed: evaluation has no side effects, so a duplicate attempt only costs another ≈ 300+ input tokens, which is what is charged [TS/api.md] [TS/models.md]. Do not retry 422 (bad question shape); fix the request. On REST the gateway can do this for you with cf-aig-max-attempts (≤ 5), cf-aig-backoff: exponential, cf-aig-retry-delay [CF/ai-gateway__usage__rest-api.md].

Timeouts: the vendor SDKs default to 10 s per attempt (JS timeout: 10000, "without a total retry budget"; Python DEFAULT_TIMEOUT = 10.0) and Python caps the whole retry sequence at 30 s (RetryPolicy.timeout = 30.0) [TS/sdk__javascript__api__interfaces__TypeSafeClientConfig.md] [TS/sdk__python__api__constants.md] [TS/sdk__python__api__retries.md]. Over REST set cf-aig-request-timeout: 10000 [CF/ai-gateway__usage__rest-api.md]. env.AI.run exposes no timeout in any captured source; race it against your own deadline (Promise.race with a timer/AbortController) and treat a timeout as "unknown, do not act", the same branch as low confidence. Typical latency is ~100 ms [TS/concepts__how-to-build-with-system-one.md], so a 10 s budget is generous, not tight.

4.9 Rate limits

Undocumented for typesafe/jev on Cloudflare. TypeSafe direct: 250,000 tokens/s and 1,200 rpm, "adjusting dynamically" [TS/models.md]. Cookbook thread pools range from 4 to 16 workers: entity alignment 6, with a code comment that "the public endpoint rate-limits above roughly eight" [TS/cookbooks__entity_alignment.md]; RAG classification 4 [TS/cookbooks__classifying_rag_passages.md]; autoresearch 8 [TS/cookbooks__autoresearch_feature_discovery.md]; skill suggestion 8 [TS/cookbooks__skill_suggestion.md]; rerank 12 [TS/cookbooks__rerank_typesafe.md]; both consistency cookbooks 16 [TS/cookbooks__consistency_noul_cookbook.md] [TS/cookbooks__consistency_choice_cookbook.md]. Cloudflare's 50 rpm (prepaid) vs 20 rpm (standard) figures apply to frontier Workers AI models only [CF/changelog__post__2026-08-07-workers-ai-unified-billing.md]. No source gives a concurrency limit for the Cloudflare route; measure it.

4.10 Pricing

TypeSafe direct: $42 per billion = $0.042 per million input tokens, output free [TS/models.md]. Cloudflare: dashboard only [CF/ai__models__typesafe__jev.md]; Unified Billing passes provider pricing through with no markup [CF/ai-gateway__features__unified-billing.md], so $0.042/Mtok is the expected figure, but nothing confirms it or whether output_tokens are billed. The API reference's one-question examples on a one-sentence state report 296–318 input tokens, so budget roughly 300 tokens before your own state and questions (guide's inference from examples; no source states a fixed overhead) [TS/api.md]; short state plus 1–3 questions ran 380–430 tokens in the Cloudflare samples [CF/ai__models__typesafe__jev.md].

Reconcile on day one: note the credit balance (AI Gateway → Credits Available) [CF/ai-gateway__features__unified-billing.md], run a known batch (e.g. 100 calls) logging usage.input_tokens and usage.output_tokens (the only usage fields the output schema gives; no cost field) [CF/jev-schema-output.json], then read the balance delta and the gateway analytics cost figure ("token usage, and costs") [CF/changelog__post__2026-08-07-workers-ai-unified-billing.md]. That yields the effective $/Mtok and whether output tokens are charged. Set a per-gateway spend-limit rule before load-testing, and use cf-aig-metadata dimensions if you need per-tenant caps [CF/ai-gateway__features__unified-billing.md]. Keep usage per decision in your own store; the dashboard is the only documented price source [CF/ai__models__typesafe__jev.md].

4.11 Terms and data handling

The Cloudflare page links TypeSafe's legal index: DPA, Master Customer Agreement, Privacy Policy; ZDR for enterprise via privacy@typesafe.ai [TS/legal.md]. Jev is not trained on customer requests [TS/models.md]. No click-through acceptance is documented on Cloudflare, and no source says whether TypeSafe's DPA/no-training/ZDR commitments cover traffic proxied through Workers AI. Unresolved; do not send private data until it is. Separately from that question, AI Gateway logging applies to every Jev request through default and stores request and response bodies regardless of ZDR; disable request/response logging on the gateway or send cf-aig-collect-log: false before sending customer data (4.3) [CF/ai-gateway__usage__rest-api.md] [CF/ai-gateway__features__unified-billing.md].


5. Design patterns

All pattern code in the docs is Python; answer shapes are identical on Cloudflare, so port 1:1 with r.answers.x.choice and string keys for score probabilities [TS/patterns.md] [CF/ai__models__typesafe__jev.md].

5.1 Speculative fan-out

When: several judgments, some only relevant on certain branches. Put all in one request; extra questions barely change latency, and a second request is justified only when you cannot build it without the first answer [TS/patterns__fan-out.md] [TS/primitives.md]. Measured: 13 questions batched = 12.2× cheaper, 10.0× faster, answers unchanged [TS/cookbooks__parallel_questions.md]; primitives.md quotes the same cookbook as 11.5× / 9.6× [TS/primitives.md].

const r = await env.AI.run('typesafe/jev', { state: ticketText, questions: {
  category: { type: 'choice', instructions: 'Determine the broad category of this support ticket',
    criteria: { bug_report: 'Broken or producing errors', billing: 'Charges, invoices, refunds, subscriptions',
                feature_request: 'Requesting new functionality', account: 'Login, permissions, profile, security' } },
  bug_severity: { type: 'score', instructions: 'How severe is the reported issue',
    criteria: ['Cosmetic; no impact to functionality', 'Broken or degraded feature; workaround exists', 'Blocking issue; no workaround exists'] },
  has_reproducible_steps: { type: 'noul', instructions: 'The user describes specific steps to reproduce the issue' },
  refund_requested: { type: 'noul', instructions: 'The user is explicitly asking for a refund or credit' },
  frustration: { type: 'score', instructions: 'How frustrated the user appears', criteria: ['Calm, matter-of-fact', 'Frustrated but civil', 'Very angry'] },
}}) as JevOutput;
const a = r.answers as any;
if (a.category.choice === 'bug_report') {
  if (a.bug_severity.score > 1.5 && a.has_reproducible_steps.noul > 0.6) escalate(ticketId, 'high'); else backlog(ticketId);
} else if (a.category.choice === 'billing') routeBilling(ticketId, { refundLikely: a.refund_requested.noul > 0.7 });
if (a.frustration.score > 1.5) flagPriority(ticketId);

Thresholds from [TS/patterns__fan-out.md]; port derived from that page plus the Cloudflare response shape.

5.2 Confidence-gated routing

When: the cost of a wrong action varies by branch. "The answer tells you what; confidence tells you whether to act" [TS/patterns__confidence-routing.md].

const intent = a.intent; // choice over check_balance | approve_transfer | other
if (intent.confidence < 0.6) return toHuman();
if (intent.choice === 'check_balance') return showBalance();          // low stakes
if (intent.choice === 'approve_transfer')
  return intent.confidence > 0.85 ? approve() : askToConfirm();       // high stakes
return toHuman();

Numbers from [TS/patterns__confidence-routing.md]; the confidence page uses 0.5 / 0.9 for the same example [TS/confidence.md]. Tune on your data.

5.3 Composite scoring

When: one judgment hides several dimensions. Score each separately, normalise by levels − 1, weight in code; reuse the same answers under different weight sets [TS/patterns__composite-scoring.md] [TS/primitives__score.md].

const norm = (q: string) => (a[q].score as number) / (QUESTIONS[q].criteria.length - 1);
const py = norm('python_depth'), lead = norm('team_leadership'), arch = norm('system_design'), gen = norm('generalist');
const seniorIC = 0.40 * py + 0.10 * lead + 0.40 * arch + 0.10 * gen;
const engManager = 0.15 * py + 0.40 * lead + 0.20 * arch + 0.25 * gen;

Weights verbatim from [TS/patterns__composite-scoring.md]. Nouls compose the same way: spam_risk = 0.45·requests_credentials + 0.30·sender_identity_mismatch + 0.25·unexpected_reward, 0.4–0.6 to review [TS/concepts__how-to-build-with-system-one.md].

5.4 Intent routing

When: a cheap classifier in front of expensive handlers (deterministic code, specialist LLM, human) [TS/patterns__intent-routing.md].

const { intent, complexity } = a; // choice: order_status|product_question|return_exchange|complaint ; score: 3 levels
if (intent.confidence < 0.5) return human(ticketId);
switch (intent.choice) {
  case 'order_status': return lookupOrder(ticketId);                 // no LLM
  case 'product_question': return llm(ticketId, PRODUCT_SPECIALIST);
  case 'return_exchange': return llm(ticketId, RETURNS_SPECIALIST);
  case 'complaint': return (complexity.score > 1 || complexity.confidence < 0.5) ? human(ticketId) : llm(ticketId, COMPLAINT_RESOLUTION);
}

The docs' intent Choice has no other option although the primitives page says to add one when coverage is uncertain; the confidence floor does that job [TS/patterns__intent-routing.md] [TS/primitives.md]. Pair a relative Choice with an absolute Noul ("does any option apply?") when out-of-scope input is possible [TS/cookbooks__semantic_find.md].

5.5 Supporting idioms


6. Cookbook index

All Python against api.typesafe.ai, mostly pinned to jev-1.12; the two consistency cookbooks ran on jev-1.13.0 [TS/cookbooks__parallel_questions.md] [TS/cookbooks__consistency_noul_cookbook.md]. Titles from [TS/cookbooks.md].


7. Known limits and jaggedness (jev-1.13)

From the jaggedness page, reviewed 2026-09-17 [TS/model-jaggedness__jev-1.13.md], plus community reports:

  1. Literal reading — answers the question as written. When you find yourself explaining what you "really meant", that explanation is the missing half of the instruction.
  2. Counting and arithmetic — unreliable, error grows with size; count in code with one Noul per candidate. Numeric representations underperform semantic ones. Do not interpolate a Score to reconstruct a magnitude.
  3. Dates — read as text, not ordered quantities; extract components as closed-set Choices with a "not stated" option and do arithmetic in code [TS/cookbooks__date_extraction_cookbook.md].
  4. Indirection — weak; name the relevant state fields directly.
  5. Large irrelevant state — lowers accuracy ("context rot"); filter in code first, or use a Noul relevance filter [TS/model-jaggedness__jev-1.13.md] [TS/concepts__how-to-build-with-system-one.md].
  6. Adversarial content — state is data, not hostile; injected instructions can move the answer. Mitigation is precise criteria and edge-case testing; an injection Noul "is a filter... Nothing here is a security boundary" [TS/cookbooks__classifying_rag_passages.md].
  7. Contradictory instructions vs criteria — a Noul whose true maps to "no" performs worse.
  8. No structural invariants — P(x) + P(not x) ≠ 1; Noul and yes/no Choice disagree; Choice is relative, Noul absolute.
  9. No generation — chaining Choices to force generation is slow and poor; generate candidates elsewhere and let Jev pick.

The page promises "many of these will be fixed in later versions" and says to "test your integration thoroughly before deploying it to many users" [TS/model-jaggedness__jev-1.13.md]; Cloudflare moves versions for you [TS/models.md]. Keep an edge-case regression set (adversarial injections, negations, boundary cases, the P(x)+P(not x) pairs you rely on) with recorded answers and model ids, and re-run it whenever response.model changes, not just the thresholds. For unit tests, stub env.AI.run with recorded fixtures: wrangler dev hits the real account and incurs charges [CF/workers-ai__get-started__workers-wrangler.md].

Also: text only [TS/models.md]; non-English incl. CJK accepted with lower accuracy — test and watch confidence [TS/concepts__state.md] [TS/models.md]; Choice "works reliably up to roughly 240 options" despite the 255 cap [TS/cookbooks__classification_using_confidence.md]; a Score measuring two things at once returned 0% confidence in one community project [https://github.com/vincenth19/jev-testing]; structured level examples help only when they resemble real inputs — a mismatched example returned the same result as plain strings [TS/primitives__score.md]. The jaggedness page's snippet uses TypeSafeClient(model="jev-1.13") and result.nouls[...], while models.md lists only jev-1.13.0/jev-latest/jev-preview; do not copy jev-1.13 unverified [TS/model-jaggedness__jev-1.13.md] [TS/models.md].

Prompt-writing rules the docs recommend


8. Gotchas checklist


9. Sources

Local files: TypeSafe docs

Base: /tmp/claude-1000/-home-stevan-dev/4bdbd082-4e99-49ae-a4be-bacdb74171a7/scratchpad/docs/typesafe/

Local files: Cloudflare docs

Base: /tmp/claude-1000/-home-stevan-dev/4bdbd082-4e99-49ae-a4be-bacdb74171a7/scratchpad/docs/cloudflare/

URLs

Vendor:

Community (third-party, not vendor-verified):

10. Live verification on Cloudflare (2026-09-24, added after the research pass)

Measured by Claude on the CurrencyTransfer account with the reference copilot's real 37-question request (~/dev/jev/scripts/replay_via_cf.py, bisection in the session log):

7,794 words · Internal working documents. Do not share outside CurrencyTransfer.