Wyrd docs v0.2.0 Reference GitHub

Status: Runnable

Verifier Card

A Verifier takes evidence from your app and decides whether it meets your requirements. Every object on this page rejects keys it does not define, so a typo fails at wyrd declare --check instead of being ignored. For a walkthrough, see Author a Verifier.

How a Verifier works

A Verifier is built from five parts:

PartWhat it isExample
InputA named piece of evidence your app sendsletter, a Markdown file
StepOptional work that prepares evidence before anything is judgedRead the text out of the letter
ActionWhat one step does: extract, agent, or codeextract the letter
RequirementOne claim that must be true, in words a reviewer reads“The letter follows the refund policy”
CheckHow one requirement is decided: assert, trace, agent, code, or driftAsk a judge Agent

When you call verify or submit, the server:

  1. Checks each input against its slot, such as a file’s format and size.
  2. Runs the steps. A step starts once the steps it reads from are done, and steps that don’t depend on each other may run at the same time. A step’s own requirements are checked right after it, before anything that depends on it starts.
  3. Checks the top-level requirements, once every step has finished.
  4. Combines every requirement’s outcome into one verdict with decide.

Steps read and compute; they are not the place for side effects. To act when a Verifier fails, such as paging someone, attach an Operator to the binding.

A Verifier with no steps is common: one input and one assert requirement covers many checks.

Spec fields

A Verifier spec has six fields.

yaml
apiVersion: wyrd/v1
kind: Verifier
metadata: {name: follows-policy, version: 1.0.0, space: support}
spec:
  description: A refund decision letter follows the refund policy.
  inputs: {letter: {type: media, formats: [markdown]}}
  steps: [{id: read-letter, action: {kind: extract, from: inputs.letter}}]
  requirements: [{id: follows-policy, statement: "...", check: {kind: agent, agent: ./policy-judge.yaml}}]
  decide: all
FieldTypeDefaultMeaning
descriptionstringnoneOne sentence shown with results
inputsmap of slot name to input slot{}The named evidence a caller sends
stepslist of steps[]Work that prepares evidence before requirements run
requirementslist of requirements[]What must be true; each has one check
decideall or {pass_rate: {threshold}}allHow requirement outcomes become the verdict
context_captureobjectnoneWhat run context to keep with the result

Step and requirement ids share one namespace across the whole Verifier.

Input slots

An input slot names one piece of evidence and the shape it must have. Your app fills it by name, as in verify("follows-policy", letter=...), and steps and checks read it as inputs.<slot>. Each slot has a type and required, which defaults to true.

typeExtra fieldsAccepts
jsonschema (JSON Schema, optional)Any JSON value, checked against schema when set
mediaformats (required), max_bytesA file in one of formats, no larger than max_bytes
tracenoneThe run’s trace spans
samplesfeatures (required)Feature values over a window, for drift checks

Media formats: png, jpeg, gif, webp, pdf, text, markdown, csv, json, html, docx, xlsx, pptx, doc, xls, ppt, tar_gz, zip.

An extract step can read pdf, text, markdown, csv, json, html, docx, xlsx, and pptx. See extract for what each extractor returns.

Steps

A step prepares evidence a requirement needs but the caller did not send in usable form: text pulled out of a PDF, a value computed in Python, or an Agent’s summary. Its result is steps.<id>.output. Skip steps when your requirements can read the inputs directly.

FieldTypeDefaultMeaning
idstringrequiredName used in references such as steps.<id>.output
actionactionrequiredWhat the step does
depends_onlist of step ids[]Steps that must finish first
when{path, operator, expected}noneRun the step only if this condition holds
requirementslist of requirements[]Requirements checked against this step’s output
timeout_secondsinteger above 0noneTime limit for one attempt
retries0 to 50Attempts after the first

Actions

The action is what a step does. Each step has exactly one.

kindUse it toOutput
extractTurn an uploaded file into JSON that checks and Agents can readThe file’s text and structure
agentAsk an Agent to read evidence and return structured dataThe Agent’s response
codeCompute a value in PythonThe function’s return value

extract

extract runs one of Wyrd’s built-in extractors on a media input and returns the file’s text and structure as JSON. Checks and Agent placeholders read JSON and text, not raw files, so any PDF, Office document, or data file you want to judge goes through extract first.

It takes one field, from, naming a media slot. The extractor is picked by the file’s format; you don’t choose one.

yaml
spec:
  inputs:
    policy: {type: media, formats: [pdf]}
  steps:
    - id: policy-text
      action: {kind: extract, from: inputs.policy}
  requirements:
    - id: has-text
      statement: Every page of the policy has text
      check: {kind: assert, each: "steps.policy-text.output.pages[*].text", operator: not_equals, expected: ""}
    - id: names-window
      statement: The first page states the refund window
      check: {kind: assert, path: "steps.policy-text.output.pages[0].text", operator: contains, expected: "30 days"}
python
from wyrd import verify

run.observe.verify("policy-check", policy=verify.File("policy.pdf"))
Built-in extractors

Every output has a format field naming the extractor that produced it, and every other field is always present: an empty list is [], empty text is "", and null appears only where the type allows it. There is no page_count or slide_count; use the list’s length.

FormatReadsOutput
pdfThe text layer of each pagepages
docxParagraphs with their style, and tables, in document orderblocks
pptxEach slide’s title, body paragraphs, tables, and speaker notesslides
xlsxEvery non-empty cell of every sheet: value, formula, and errorsheets
csvEvery row, including the header, as stringsrows
jsonThe parsed documentvalue
text, markdown, htmlThe source text, unchangedtext
PDF
FieldTypeMeaning
format"pdf"Always pdf
pageslistOne entry per page, in document order
pages[].numberintegerPage number, starting at 1
pages[].textstringAll text on the page. "" when the page has no text layer, such as a scan
json
{"format": "pdf", "pages": [{"number": 1, "text": "Refund policy\nRefunds are issued within 30 days."}, {"number": 2, "text": ""}]}

An encrypted PDF fails the step.

DOCX
FieldTypeMeaning
format"docx"Always docx
blockslistThe document body, one entry per paragraph or table, in document order
blocks[].kind"paragraph" or "table"Which of the two shapes below this block has
blocks[].textstringParagraph only: the paragraph’s text
blocks[].stylestring or nullParagraph only: Word’s style ID, such as Heading1 or ListParagraph. null for an unstyled paragraph
blocks[].rowslist of lists of stringsTable only: each row’s cell text, left to right
json
{"format": "docx", "blocks": [{"kind": "paragraph", "text": "Refund policy", "style": "Heading1"}, {"kind": "table", "rows": [["Window", "Days"], ["Standard", "30"]]}]}

Only the document body is read: text inside content controls is included; headers, footers, footnotes, and comments are not.

PPTX
FieldTypeMeaning
format"pptx"Always pptx
slideslistOne entry per slide, in presentation order
slides[].numberintegerSlide number, starting at 1
slides[].titlestring or nullText of the slide’s title placeholder, with its lines joined by \n. null when the slide has none
slides[].paragraphslist of stringsEvery non-empty paragraph in the slide’s other text boxes and placeholders, in slide order. Table text is not repeated here
slides[].tableslistTables on the slide
slides[].tables[].rowslist of lists of stringsEach row’s cell text, left to right
slides[].notesstringSpeaker notes, with paragraphs joined by \n. "" when there are none
json
{"format": "pptx", "slides": [{"number": 1, "title": "Q3 refunds", "paragraphs": ["Refund rate fell to 2%"], "tables": [{"rows": [["Region", "Rate"], ["US", "2%"]]}], "notes": ""}]}
XLSX
FieldTypeMeaning
format"xlsx"Always xlsx
sheetslistOne entry per worksheet, in workbook order
sheets[].namestringThe sheet’s tab name
sheets[].cellslistEvery non-empty cell, row by row, left to right. Empty cells are left out
sheets[].cells[].addressstringThe cell’s address, such as B2
sheets[].cells[].valuestring, number, boolean, or nullThe cell’s value. For a formula, the result Excel last saved; Wyrd never recalculates. Dates are Excel serial numbers. null for an error cell
sheets[].cells[].formulastring or nullThe formula, such as =SUM(B2:B9). null for a typed value
sheets[].cells[].errorstring or nullThe error the cell shows, such as #DIV/0!. null when there is none
json
{"format": "xlsx", "sheets": [{"name": "Refunds", "cells": [{"address": "A1", "value": "Days", "formula": null, "error": null}, {"address": "B2", "value": 30, "formula": "=15*2", "error": null}]}]}

Because empty cells are left out, cells[1] above is B2, not A2. Look a cell up by address.

CSV
FieldTypeMeaning
format"csv"Always csv
rowslist of lists of stringsEvery row in file order, including the header row. Every cell is a string, so 0.02 is "0.02". Rows may have different lengths
json
{"format": "csv", "rows": [["region", "rate"], ["US", "0.02"]]}
JSON
FieldTypeMeaning
format"json"Always json
valueany JSONThe parsed document, unchanged
json
{"format": "json", "value": {"refund_days": 30, "regions": ["US", "EU"]}}
Text, Markdown, HTML
FieldTypeMeaning
format"text", "markdown", or "html"The slot format the file was sent as
textstringThe whole file, unchanged. Markdown and HTML are not parsed, so headings and tags are still in the text
json
{"format": "markdown", "text": "# Refund policy\n\nRefunds are issued within 30 days.\n"}

The file must be UTF-8.

Tables and merged cells

In DOCX and PPTX tables, a merged cell’s text is in its top-left position and every position it covers is "", so every row of one table has the same length.

Use the output

Read fields with a path, as in the example: steps.<id>.output.pages[0].text. Indexes are zero-based, even though PDF number and PPTX number start at 1. [*] works only in an assert check’s each.

To give the whole file to an Agent, pass the output to a placeholder: inputs: {policy: steps.policy-text.output}. A citation still points at the original file by slot, using page for PDF, slide for PPTX, lines for text, Markdown, HTML, and CSV, and json_pointer into this output for DOCX, XLSX, and JSON. Extraction output has a citation example for each.

What extract does not do
  • Read images. There is no OCR and no rendering. png, jpeg, gif, and webp can’t be extracted; give them to an Agent as a media input instead.
  • Read legacy Office files. doc, xls, and ppt are separate formats; convert them to docx, xlsx, or pptx.
  • Open archives. zip and tar_gz go to a step Agent’s workspace instead.

A slot that accepts a format extract can’t read fails wyrd declare --check with rule extract_format. At run time the step fails, and the requirements that need it are inconclusive, when the file doesn’t parse as its format, a PDF is encrypted, a text file isn’t UTF-8, or the file exceeds WYRD_VERIFICATION_EXTRACT_MAX_PAGES or WYRD_VERIFICATION_EXTRACT_TEXT_MAX_MIB (limits). Each result records the extractor version, wyrd-extract/1, so a verdict shows which extractor read the file.

agent

agent runs an Agent Card and returns its response, shaped by the Agent’s Prompt response_format. Use it when a later requirement needs a model to pull something out of the evidence, such as the refund limit a policy states:

yaml
steps:
  - id: refund-rules
    action:
      kind: agent
      agent: ./refund-reader.yaml
      inputs: {policy: steps.policy-text.output, answer: inputs.answer.text}
requirements:
  - id: limit-found
    statement: The reader found a positive refund limit
    check: {kind: assert, path: steps.refund-rules.output.max_refund, operator: greater_than, expected: 0}

agent is a path such as ./refund-reader.yaml or a CardRef. inputs maps each Prompt placeholder to a reference. A placeholder takes text, so a media slot goes through extract first. workspace maps file paths under /inputs/, such as /inputs/policy.pdf, to references, for Agents that read files with the fs tools.

A step Agent can use these tools: cards.list, cards.get, bifrost.list_tables, bifrost.describe_table, bifrost.query, fs.list, fs.read, fs.search, and code.run.

code

code calls a Python function and returns what it returns, as JSON. Use it for logic one operator cannot express:

yaml
steps:
  - id: answer-facts
    action:
      kind: code
      entrypoint: facts
      inputs: {answer: inputs.answer, rules: steps.refund-rules.output}
      source:
        inline: |
          def facts(answer, rules):
              return {"within_limit": answer["refund"] <= rules["max_refund"]}
requirements:
  - id: refund-within-limit
    statement: The refund offered is within the policy limit
    check: {kind: assert, path: steps.answer-facts.output.within_limit, operator: equals, expected: true}

entrypoint names the function; it receives inputs as keyword arguments. source is {inline: "<python>"} or {artifact: {card, path?}}, where card names an Artifact Card. A step returns data for later checks. To return a verdict directly, use a code check instead.

Requirements

A requirement is one claim the evidence must support, plus the check that decides it. Each requirement ends passed, failed, inconclusive, or not_applicable, and those outcomes become the verdict. Put a requirement on a step when it judges that step’s output, so later steps can branch on it; otherwise list it at the top level.

FieldTypeMeaning
idstringName in results and in steps.<id>.requirements.<rid>
statementstringThe claim, as a reader sees it in results
checkcheckHow the claim is decided

Checks

A check is the method that decides one requirement. Use the cheapest one that can decide it: assert and trace compare values with no model, code runs your Python, and agent asks a model.

kindFieldsDecides by
assertpath or each, operator, expectedComparing a value; each with [*] requires every item to pass
traceon, span_selector, operator, expectedComparing values selected from trace spans
agentagent, inputsA tool-less Agent whose response_format is a closed verdict
coderuntime, source, entrypoint, inputsA Python function that returns a verdict
drifton, method, signal, condition, profileComparing a window of samples with a baseline; see Monitor drift

assert and trace return in milliseconds. A step whose when condition is false is skipped, and its requirements are not_applicable.

Comparison operators

assert, trace, and when compare a value with an operator. Write an operator as its name, such as operator: not_contains, or, when it takes parameters, as {kind: <name>, ...}.

GroupOperators
Comparisonequals, not_equals, greater_than, greater_than_or_equals, less_than, less_than_or_equals, is_positive, is_negative, is_zero
Stringcontains, not_contains, contains_ignore_case, starts_with, ends_with, is_email, is_url, is_uuid, is_ipv4, is_ipv6, is_json
Collectionin, not_in, is_subset, is_superset, is_disjoint, all_of, any_of, none_of, is_empty, is_non_empty, unique_values
Typeis_truthy, is_falsy, is_null, is_not_null, is_string, is_number, is_boolean, is_object

Operators with parameters:

kindParameters
in_range, not_in_rangemin, max, inclusive
approximately_equals, within_abs_tolerancetolerance
within_pct_tolerancepct
within_std_devsigma, mean, std_dev
between_percentileslower_pct, upper_pct
divergence_less_thanmetric (kl, js, wasserstein), threshold
cosine_similarity_at_leastthreshold
matches_regex, not_matches_regexpattern
has_min_lengthmin
has_max_lengthmax
lengthexpected
length_greater_thanmin
length_less_thanmax
is_typeexpected (null, bool, number, string, array, object)
yaml
check: {kind: assert, path: inputs.refund.amount, operator: {kind: in_range, min: 0, max: 100, inclusive: true}}

References

Every path, from, on, and inputs value is a reference.

ReferenceReads
inputs.<slot>An input slot’s value
steps.<id>.outputA step’s output
steps.<id>.statusA step’s final status
steps.<id>.requirements.<rid>.outcomeThe outcome of a step’s requirement
steps.<id>.requirements.<rid>.reasonThe reason of a step’s requirement
requirement.id, requirement.statementThe requirement being checked

Inputs and step outputs take child selectors: .field, [0], and [*]. [*] is allowed only in an assert check’s each.

Verdicts and citations

An agent or code check returns:

json
{
  "outcome": "passed",
  "reason": "The 40 dollar refund is approved within 30 days.",
  "citations": [{"slot": "letter", "locator": {"lines": {"start": 3, "end": 4}}}]
}

outcome is passed, failed, inconclusive, or not_applicable. Each citation names an input slot and one locator:

LocatorFor
{page: n}PDF pages, counted from 1
{slide: n}PPTX slides
{lines: {start, end}}Text, Markdown, HTML, CSV
{json_pointer: "/path"}JSON inputs and DOCX or XLSX extraction output
{region: {page, x, y, width, height}}A rectangle on a page, in page coordinates

The server checks every citation against the evidence. A passed verdict without a valid citation is recorded as inconclusive.

Decide

With decide: all, the Verifier passes when every requirement that applies passes. With decide: {pass_rate: {threshold: 0.8}}, it passes when at least that share of applicable requirements pass. threshold is above 0 and at most 1.

Rejections

CodeRaised when
WYRD_REGISTRY_400_INVALID_CARD_SPECA field is unknown, missing, or has the wrong type
WYRD_SPEC_400_VALIDATIONA rule fails; details holds rule and path

Common rule values:

ruleCause
unknown_referenceA reference names an input, step, or requirement that does not exist
duplicate_idTwo steps or requirements share an id
graphdepends_on forms a cycle
media_as_textA media slot is bound to an Agent text placeholder
extract_formatAn extract step reads a slot with a format it cannot extract
operatorAn operator’s parameters are invalid, such as a bad regex
step_budgettimeout_seconds is 0 or retries is above 5
pass_ratethreshold is not above 0 and at most 1

Last updated