Wyrd docs v0.2.0 Reference GitHub

Status: Runnable

Verify a request in real time

Call run.observe.verify inside a request, get the verdict back before you reply, and decide what to send.

Prerequisites

my-check.yaml yaml
apiVersion: wyrd/v1
kind: Verifier
metadata: {name: my-check, version: 1.0.0}
spec:
  inputs:
    answer: {type: json}
  requirements:
    - id: no-refund-promise
      statement: The answer never promises a guaranteed refund
      check: {kind: assert, path: inputs.answer, operator: not_contains, expected: guaranteed refund}
  • verifier:run on it. The workload role a registered Service gets includes it.

1. Call the Verifier

Pass one input per slot the Verifier declares:

python
with state.run() as run:
    result = run.observe.verify("my-check", answer=answer)

The name is the Verifier’s Card name, looked up among the Verifiers bound to the run’s Card. For a Verifier bound to a component, call it on run.for_card("<alias>"). A Verifier with one slot also takes one positional value.

Python blocks until the result arrives; Rust and TypeScript await it. The server allows 60 seconds.

2. Act on the verdict

A failed verdict is a normal return value, not an exception:

python
if result.verdict != "passed":
    answer = "Let me pass this to our billing team."

inconclusive means no judgment was possible, such as a missing input. Treat it as not passed.

Files

File reads nothing when you create it. During the call the SDK hashes it, asks the server which digests it is missing, uploads only those directly to your tenant’s object storage, and sends the digest in the request. The same file uploads once and is reused by every later call. Format comes from the file extension, from the slot when it accepts one format, or from what you declare:

LanguageFrom a pathFrom bytes
Pythonverify.File("report.pdf")verify.File(data, format="pdf", filename="report.pdf")
Rustwyrd_ai::verify::File::path("report.pdf")wyrd_ai::verify::File::bytes(data).with_format(MediaFormat::Pdf)
TypeScriptverify.File.path("report.pdf")verify.File.bytes(data, { format: "pdf" })

To use a file already declared as an Artifact, pass an ArtifactRef instead: wyrd.verify.ArtifactRef(card_ref, path="report.pdf"), verify::ArtifactRef::new(card_ref, Some(path)), or new verify.ArtifactRef(cardRef, "report.pdf"). It is read with the Artifact’s own authorization.

3. Read the breakdown

FieldHolds
execution_idThe server-issued ID of this execution
run_idYour run
verifier, subjectThe exact Verifier and Card versions
outcome.verdictpassed, failed, or inconclusive. Python also exposes it as result.verdict
outcome.requirementsOne entry per requirement: id, step, statement, outcome, reason, actual, citations, detail, and the check’s provenance
outcome.stepsOne entry per step: id, status, output, attempts, duration_ms, reason, tool_calls
outcome.evidenceThe pinned input declarations, by slot, with file digests
python
for requirement in result.requirements:
    print(requirement["id"], requirement["outcome"], requirement["reason"])
# no-refund-promise passed ...

Each citation names an original input slot and a location in it, such as { "slot": "answer", "locator": { "json_pointer": "" } }, or { "slot": "report", "locator": { "page": 2 } } from a judge reading a PDF. A judge that passes without a valid citation is reported as inconclusive.

4. Confirm the result was stored

After it answers, the server stores the pinned inputs and the result in vala.verification.results, tied to your run, with binding_id null. A verify call never starts queued work and never runs Operators.

python
from wyrd.bifrost import Bifrost

rows = Bifrost().sql(
    "SELECT result_id, verdict, verifier_version FROM vala.verification.results "
    "WHERE run_id = $1 AND binding_id IS NULL",
    [run.run_id],
)
print(rows.to_pandas())

The row appears shortly after the call returns. The server stores it in the background, after it has already answered you, so the result your code received stands even in the rare case the row is lost, such as an abrupt server restart. An empty query seconds after a call is not proof the row is missing; retry before concluding that.

Checks that do not belong on the request path

A Verifier with samples slots and drift checks judges a scheduled window, so verify refuses it; record its samples with submit as in Monitor drift. A slow check, such as a model judge, belongs in submit instead of holding the request; see Submit for later.

What can go wrong

Error codeCauseFix
WYRD_SDK_404_UNKNOWN_VERIFIERNo Verifier with that name is bound to this component in the loaded bundleCheck the name and the component alias; reload the bundle after changing verified_by
WYRD_VERIFICATION_400_INPUT_INVALIDAn unknown slot name, a value of the wrong kind for its slot, an invalid trace ID, an unreadable file, or a format the slot does not acceptMatch the Verifier’s inputs. Nothing ran
WYRD_VERIFICATION_413_INPUT_TOO_LARGEA file is larger than its slot’s max_bytes or the deployment limitSend a smaller file, or raise max_bytes up to the deployment limit
WYRD_VERIFICATION_422_INPUT_UNSUPPORTEDThe Verifier judges a scheduled windowUse submit
WYRD_PERMISSION_403_DENIED_RBACThe credential lacks verifier:run on this Verifier, or read access to an Artifact or trace it namesGrant a role that includes it
WYRD_VERIFICATION_429_STAGING_FULLThe server is staging more verification work than it holdsNothing was staged. submit retries it for you; retry verify after details.retry_after_seconds
WYRD_VERIFICATION_504_EXECUTION_TIMED_OUTThe execution took longer than 60 seconds, usually a slow Agent stepDecide your fallback in code, or use submit. The SDK does not retry verify

A missing required input is not an error: the requirements that need it come back inconclusive. For when to check in real time versus in the background, see How verification works.

Last updated