See it in a warranty answer
In the included offline example, someone asks whether a refurbished laptop has a warranty. DSPy first proposes a lifetime warranty, but the trusted policy promises 12 months. A scripted Jev decision scores the first answer low, so DSPy tries again. Your output check asks whether the cited policy passage supports the final answer. This diagram shows the checks you try offline and the receipt produced when you run the same endpoints through Millwork. For an approved live run, Millwork calls the same Python app and records which saved agent and output check handled it, plus the verdict. Your DSPy program and acceptance rules run in your Python app. The model provider and TypeSafe receive the inputs your app sends them; Millwork’s receipt stores neither the question, answer, nor passage. The saved agent and check can handle later approved runs; your app chooses which questions to route.Where else this fits
The same pattern works for short answers grounded in source passages your team controls. The warranty example is included; each idea below needs its own source and offline cases:Customer support
Bring the current return policy. Test a correct answer, a wrong return window, and a missing quote.
Research summaries
Bring approved source excerpts. Check one cited claim at a time; test a misquote and a contradiction.
Developer docs
Bring the reference for the right API version. Test a supported claim and a quote from an older release.
Start with the offline example
No account or key. Run the included cases before adapting the program or sending live traffic.
offline_cases.py reports
4 passed, 0 failed and check_locally.py reports 14 passed, 0 failed.
After an approved live run, its receipt names your saved agent, output check,
and verdict; the usage report separates your answers and Jev decisions from
Millwork’s accepted runs.
TypeSafe’s Jev returns a probability that a passage supports an answer.
DSPy’s BestOfN tries up to your set limit and stops when an answer reaches
your threshold. Your output check applies exact quote rules and a separate Jev
decision to the final answer.
Before you begin
For the offline example: Python 3.10 or newer. It needs no account, key or credit. For the endpoint test and the live run:- Node.js and the Millwork CLI release that
Set up with the CLI installs,
plus
curlandjq. Runmillwork --version --jsonfirst and confirm it reports that release; use it for every CLI command on this page. - An organization API key in your secret store, available as
SOLVERAPI_API_KEY, as described in Get your first model answer. - A TypeSafe account for Jev, and an account with the model provider your DSPy program already uses.
- Millwork credit and quota for one run, and an administrator who can register your agent endpoint’s key and give you a credential handle, a reference to that key. Without that handle you can complete only the offline steps.
1
Run the example offline
The example is a small policy-answering app.
Expected:
program.py
holds a DSPy module standing in for yours, wrapped in a bounded BestOfN.
check.py holds the output check, served through a Python output-check
adapter. Copy the downloader into dspy-example.py, review it, then run it.
It creates a dspy-jev-governed-run directory and checks every file against
the manifest before writing anything.Required: save as dspy-example.py
Required: save as dspy-example.py
4 passed, 0 failed and 14 passed, 0 failed.
offline_cases.py walks four questions through both endpoints: an answer
supported on the first attempt, one supported on the second, an abstention
after three unsupported attempts, and a Jev outage. check_locally.py checks
supported, contradicted, missing-source, low-confidence, misquoted and
abstained answers, and an unavailable check. It sends each answer again as
model text, which must get exactly the same result, and confirms that the
quality score never changes a verdict. Scripted answers stand in for your model and
for Jev, so neither command needs a key or makes a paid call. Both still run
DSPy’s Predict, BestOfN and the code that turns Jev’s answer into a
decision, as production does. Run every later command from this
dspy-jev-governed-run directory unless a step says otherwise.See one question, answer and verdict
See one question, answer and verdict
The same app serves both endpoints on your machine with the scripted answers.
The two keys below are local demo values that only this offline server
accepts; never use them for a deployed endpoint.Expected: the agent returns an answer with its
source_id and quote,
and the check returns "is_correct": true with four named results. The app
also prints one count record per endpoint; those lines hold numbers and
outcome codes only.2
Put your program and evidence in place
Change these files and keep the rest:
If the quote is not in the cited passage, the check rejects the answer without
asking Jev. It passes an answer only when the exact checks hold and Jev’s
probability reaches your threshold.Add your own offline cases before going live. The scripted model answers live
in
offline.py: give each question one scripted answer per attempt, and
include every output field your module declares, such as reasoning for
dspy.ChainOfThought. Add a JourneyCase in offline_cases.py for each new
question, and your check’s cases to check_cases.json and
heldout_cases.json. Include a pass, a contradiction, a missing source, a
low-confidence answer and an unavailable check. Then rerun both offline
commands. Offline runs use scripted answers, so they test your check and your
wiring, not your prompt; judge the prompt on the live path.3
Choose your threshold with Jev
Cost: from this step on, some commands cost money. TypeSafe bills each Jev
decision, and your model provider bills each answer your program generates,
including every BestOfN attempt. Millwork charges its platform fee only when
it accepts a live run, which happens in the last steps.
Model access and billing gives the current amount and the
full rule. Local
cases, endpoint tests, BestOfN attempts and Jev calls on their own are not
Millwork runs and carry no platform fee.The threshold is a decision rule you choose from labelled answers, not a
calibrated chance of being right. Its numbers are not a measurement: the scripted answers give every labelled
answer the same probability, 0.3, so every row rejects all supported answers.
For the real measurement, run Expected: a report marked
evaluate_quality.py compares the check
with the exact-quote check alone. It shows false acceptance and false
rejection at every threshold from 0.50 to 0.95, with the latency and TypeSafe
requests for the whole run. First see the report’s shape with scripted
answers:python evaluate_quality.py --live. It prints
how many TypeSafe requests it needs and stops.Approval: the TypeSafe account owner approves that many requests. Then
the same person loads the key in their own terminal, so it never appears in a
command, a file or a chat:"evidence": "live". Set SUPPORT_THRESHOLD
in support.py to the jev_check.by_threshold row whose false acceptance
and false rejection your organization accepts. If Jev times out, refuses or
returns a malformed answer, that case counts as a technical failure with no
probability. The report holds case IDs and numbers, never answer or passage
text.4
Deploy both endpoints over HTTPS
Deployment can incur hosting charges. The endpoint test at the end of this
step sends paid Jev requests.Deploy through the application or host your team already owns, following the
deployment step of
Build and connect an output check
for HTTPS, access and test approvals. Copy the Use a threaded worker: each agent call holds a thread for up to the 25-second
program deadline. Approval: the endpoint owner and the TypeSafe account owner approve the
printed count for each kit run you plan. Ask again before any run beyond that.
Then create the kit inside Expected:
dspy-jev-governed-run
directory to the host and install requirements.txt there. Configure these
values in the host’s secret settings:Neither endpoint key is a Millwork organization API key. Both are read on
each request, so you can rotate them by changing the settings.Run the app with a production WSGI server behind your HTTPS ingress, reachable
only from that ingress.
wsgi.py builds the app once per worker:python app.py starts a single-process server for the
offline walkthrough only. If your web app is ASGI, such as FastAPI, run this
as its own service and route /millwork/agent and /millwork/check to it
from your ingress. GET /healthz reports the example, adapter, DSPy and
Python versions only.Then test the check endpoint with the endpoint test kit: Node scripts, created
by the CLI, that send labelled requests to your check, including cases meant
to fail. check-cases.mjs gives the kit each case in check_cases.json once,
as model text, and checks its verdict and named results. The exact match
between the JSON and model-text forms of a case was already checked offline by
check_locally.py; against a live endpoint each form would be a separate Jev
request, and two requests need not return the same probability.Only cases whose quote is in their passage reach Jev. Count the requests one
kit run sends with your current cases:dspy-jev-governed-run, and have the endpoint owner
load the check key with
read -rs MILLWORK_KIT_ENDPOINT_KEY && export MILLWORK_KIT_ENDPOINT_KEY:passed is true and summary.failed is 0; the local-only
evaluator-unavailable case shows as not applicable. Passing means the
deployed endpoint answers Millwork’s contract with your access settings, and
live Jev gives each labelled case the verdict you expect. If a case gets a
different verdict, your labelled case and live Jev disagree: fix the case or
your threshold offline, then get approval before testing again. The kit is a
test tool on your workstation; the deployed app does not need Node.5
Save your agent
The API calls a saved agent an Approval: saving the agent sends one authenticated Expected:
arm. Ask your administrator
to register the agent endpoint’s key (MILLWORK_AGENT_KEYS) and give you its
credential handle. The key itself never appears in your request. Save this
body as dspy-agent.json:HEAD request, a
probe, to the agent endpoint, which answers it without running your program.
The endpoint owner approves that probe. Then save the agent with your
organization API key:status: "ready" and an arm_id, which the run request reads
from dspy-agent-response.json. A degraded status with
authentication_failed means the handle’s key and MILLWORK_AGENT_KEYS
differ.6
Connect your output check
The API calls an output check a verifier (The endpoint owner enters the Keep the Expected:
verifier_id). Connecting and
testing the output check have no Millwork platform fee, and neither calls Jev.
Connecting sends two test requests to your endpoint: a probe without the key,
which the adapter refuses, then a test that checks the key.
millwork verifier test sends one that checks the endpoint contract.
CUSTOMER_APP_ORIGIN is the Millwork app you sign in to, not your endpoint.Approval: the endpoint owner approves these three test requests. Then:MILLWORK_VERIFIER_KEYS key in the hidden
terminal prompt; the command then prints Verifier <id> connected. Without a
private terminal, it returns state: "action_required" with a verifier_id,
a private continue_url and an intent_id. Open continue_url in your
browser, signed in to the same organization, enter the key there, then finish
the connection:verifier_id once the command printed Verifier <id> connected. or
the continue output shows connection.status: "active". Then test it:headline: "ready", probe.contract.validated: true and the
same verifier_id. The reserved probe may return is_correct: false on
purpose; that does not make the endpoint unusable. To repair a connection, see
protected-connection recovery.7
Approve one run and read the receipt
Cost: Millwork’s platform fee, charged when it accepts the run; see
Model access and billing for the current amount.
With the default three attempts, one run makes at most three model calls
and sends at most four Jev decisions: one per attempt and one in the
check. The request’s Expected: a run ID and a queued status, not a finished run. If the
response is lost, send the same command again: the saved request key returns
the same run instead of starting a second one. Check the status until it is
Then read the receipt:Expected:
max_cost_usd limits only the model spend Millwork
records, and this example reports none, so it does not limit your provider or
TypeSafe bills; the program’s attempts and deadline limit those.Build the request. run_ref is a new random ID the usage report uses to match
the run to its counts, so it never holds customer data. The request key lets
Millwork recognize a retry, so save it for any retry:routing.required_arm_id runs the task on your saved agent, and verifier_id
selects your check. Without verifier_id, Millwork would apply only a basic
check that output is present.Approval: a person who can authorize paid runs reviews
run-request.json and approves this one run. Then submit it:completed, failed, cancelled or expired, as
Wait for a final status
describes:agent is your arm_id, check is your verifier_id, and
verdict is the check’s decision with its four named results. The receipt
shows the agent attempt at $0 because the example reports no cost to
Millwork; your provider and TypeSafe bills carry that spend.What other outcomes look like:Results and receipts explains the verdict,
quality score and named results.
8
Count what the run used
Copy the count file that Expected: an
MILLWORK_COUNTER_LOG names from
your host into this directory, or export the records from your log platform.
Save Millwork’s usage report for the month the run was accepted, read from the
run’s own record:executions entry for your run ID with the counts your app
recorded, and a millwork section with the runs Millwork accepted in the
whole period. The report skips log lines that are not count records.generator_calls counts every model call, including ones whose output DSPy
could not parse (generation_failures); a call beyond the run’s cap or after
the deadline is refused, not sent. typesafe_requests_sent includes requests that failed, which
typesafe_failures counts again on their own. Decisions answered from DSPy’s
cache are counted in typesafe_cache_hits and are not sent. Count records
hold numbers, outcome codes and your run_ref only: never a question, answer,
passage or key. Records from local cases and endpoint tests have no matching
run and appear under unattributed.To use this path for another eligible question, keep the saved agent and output
check. Build a new request with a fresh run_ref and request key, and get
approval for that paid run. Your app decides which questions to route;
finishing this example does not route future questions automatically.Time limits
Millwork waits at most 30 seconds for the agent call, or less when the saved agent’smax_runtime_s or the run’s remaining time is smaller. The
example’s program deadline, 25 seconds, usually ends first and answers
504 program_timeout, a technical failure with no verdict. A run that waited
long in the queue can reach Millwork’s limit first instead. A program that
needs longer is not suited to this recipe.
Each model answer has an 8-second limit and each Jev decision a 6-second
limit, with no automatic retries. After a 504, no new model answer or Jev
decision starts. A call already in flight finishes, and your app then writes a
late-work record with its counts, which the usage report adds to the run. The
check’s Jev decision runs inside Millwork’s 10-second wait for a check.
What the receipt does not show
The receipt records the check’s identity, verdict, quality score and named results. It does not keep the question, the answer, the passage or Jev’s response. The check sees only the answer. It checks whether the cited passage supports the answer, not whether the answer completed the customer’s task.Recover without starting work twice
For a Jev check without DSPy, start from
Check citations with Jev.