Skip to content

Test a workflow before it writes (experimental)

View Markdown

A workflow that fills values from text writes facts to your graph. Before it does, you want to see what it reads on a few real records: the value, the words of the text that state it, and what the workflow does with it.

A test runs a values.extract workflow on chosen inputs and writes nothing to the graph. You read each input’s values with their quotes, answer each value, and save the answers as the test’s dataset. The dataset has the shape of a template’s answer key, so you can compare versions of the workflow on the same examples later.

In the console, open a values workflow and choose the Test tab. Pick a sample of the class, or paste the IRIs of chosen instances (at most 20), and choose Start test. To test a workflow before you create it, choose Test on examples in the Use template dialog.

Name the workflow in name. Send iris (at most 20) to choose the inputs, or a sample of the first instances of the watched class (1 to 20, default 6).

Terminal window
curl -X POST "$LBB_URL/v1/workflows/named/tests?graph=tender" \
-H "Authorization: Bearer $LBB_API_KEY" -H 'Content-Type: application/json' \
-H 'Idempotency-Key: sinks-test-1' --data '{"name": "sinks", "sample": 6}'
curl "$LBB_URL/v1/workflows/named/tests/get?graph=tender&id=wtest_1791967648726a1b2c3d4e5f6" \
-H "Authorization: Bearer $LBB_API_KEY"

To test a workflow that does not exist yet, send install in place of name, with the body of its create (PUT /v1/workflows/triggered).

To test earlier settings of a workflow, send revision with name: the id of a revision from its settings history. The test runs on those settings and the workflow keeps the ones it uses, so you can compare two versions on the same inputs. The test’s revision names the settings it ran on.

To test other models, send models with the model of each step you want to change: {"extract": {"provider": "anthropic", "model": "claude-haiku-5-5", "effort": "low"}}. The test runs on those models and the workflow keeps its own. The test’s models names the models it ran on. Choose a model for the workflow afterwards with model choices.

A test goes from queued to running, then ends done, failed or cancelled. wait reads the test until it ends. When the test is still open after 10 minutes, wait raises a TimeoutError (TypeScript: timeoutMs, Python: timeout in seconds). If the server stops during a test, the test goes on where it stopped: an input that has a result is not read again. cancel stops a test, and the inputs it read keep their results.

list returns one page of the graph’s tests, newest first, and name keeps the tests of one workflow. all reads every page:

Terminal window
curl "$LBB_URL/v1/workflows/named/tests?graph=tender&name=sinks&limit=10" \
-H "Authorization: Bearer $LBB_API_KEY"

For each input the test keeps the text the workflow read, and for each property:

  • the value, in the property’s unit;
  • the quote: the words of the text that state the value;
  • the support: how well the quote supports the value, from 0 to 1;
  • what the live workflow does with it: Checks passed (it writes the value), Needs review (it gives the value to a person), or the check that dropped it;
  • the conversion of a number the text states in another unit: 600 mm → 60 cm.

The summary counts the values: Checks passed, Needs review, No value and Failed.

Answer a value with Accept, Correct (and the right value) or Reject. For a property without a value, Accept says the text states none, and Correct gives the value it states. With the API, the answer cant_tell keeps the property out of the dataset, and answer: null removes an answer.

Terminal window
curl -X POST "$LBB_URL/v1/workflows/named/tests/answer?graph=tender" \
-H "Authorization: Bearer $LBB_API_KEY" -H 'Content-Type: application/json' \
-H 'Idempotency-Key: sinks-answer-1' --data '{
"id": "wtest_1791967648726a1b2c3d4e5f6",
"iri": "https://example.com/sink/s-4-1",
"property": "width_cm",
"answer": { "decision": "correct", "value": 45 }
}'

An answer writes nothing to the graph.

Save as dataset keeps the answers as an Evals dataset. Each answered input becomes an example: its text, the values a correct run writes, and those it leaves out. Only the properties you answered are in it.

A test has one dataset. The first save creates it, and the test names it in dataset. A later save brings that same dataset up to date with the answers, and returns it with the same id. In the console the button then reads Update dataset. Download saves the answers the same way and downloads the dataset’s two files: records (each input’s name and text) and expected (the answer key).

Terminal window
curl -X POST "$LBB_URL/v1/evals/datasets?graph=tender" \
-H "Authorization: Bearer $LBB_API_KEY" -H 'Content-Type: application/json' \
-H 'Idempotency-Key: sinks-save-1' --data '{
"name": "sinks answers",
"kind": "values",
"source": { "kind": "test", "id": "wtest_1791967648726a1b2c3d4e5f6" }
}'

Answer more values and save again: the dataset keeps its id.

const saved = await client.workflows.tests.get(test.id);
const again = await client.evals.datasets.create({
name: "sinks answers",
kind: "values",
source: { kind: "test", id: test.id },
});
console.log(saved.dataset === again.id);

When you delete the dataset, the next save of the test creates a new one. See Datasets of examples to list, read, export, add to and delete datasets.

A test calls the same models as the workflow. The calls count in your environment’s monthly workflow budget, and they show in the call log with the route tests/<name>. A test that may cost more than the budget has left is refused with 429 workflow_budget_exceeded. The calls of a test do not count as the workflow’s own work: model checks, calibration and model trials leave them out.

A template your worker runs has npm run test:examples. It runs the workflow on instances of your graph with the real models, checks each write with the commit’s dry run, and prints what the workflow would write. It writes nothing.

Terminal window
LBB_URL=... LBB_API_KEY=... LBB_GRAPH=tender npm run test:examples

In your own code, captureActivities(lbbActivities(client)) gives the same activities: the models and the reads run, and the writes and review items are kept instead of written. Its model calls name the test, so the call log shows them as a test’s and model checks leave them out. This needs a server with workflow tests.

In Python, capture_activities(lbb_activities(client)) gives the same activities. See Run a worker in Python.