Settings history of a workflow (experimental)
A workflow’s settings change over time. Someone raises the gate, adds a property, or moves a budget. When the results change, you want to know which settings made them, and you want to go back to the settings that worked.
Each change of a workflow’s settings writes a revision. A revision holds
the settings a person set: the template and its version, the trigger, the
parameters, the gate, the owned outputs, the budget and, for a workflow of
your worker, its batch and routing. Its id is a hash of those settings, such
as wrev_5d0c9e2a41b7f3368e10c4d2a9b8f701, so the same settings always get
the same id. The workflow runs on one revision at a time, its active
revision. Each run records the revision it ran on.
A revision leaves out what comes from the environment: the units of the ontology’s properties, the models, and the system prompt. The run’s fingerprint covers those. Two runs with the same revision and different fingerprints had the same settings and another environment, for example a new model.
See the history
Section titled “See the history”In the console, open a workflow and choose the Settings tab. Under the form, History lists each change, newest first: when, who, and what changed. The active revision is marked.
With the TypeScript SDK:
import { LbbClient } from "@littlebigbrain/client";
const client = new LbbClient({ baseUrl: process.env.LBB_URL!, apiKey: process.env.LBB_API_KEY, graph: "tender" });
const page = await client.workflows.named.revisions("sinks");for (const revision of page.data) { console.log(revision.activation, revision.action, revision.activated_at, revision.activated_by, revision.bound);}With the Python SDK:
import os
from lbb import LbbClient
client = LbbClient(base_url=os.environ["LBB_URL"], api_key=os.environ["LBB_API_KEY"], graph="tender")
page = client.workflows.named.revisions("sinks")for revision in page.data: print(revision.activation, revision.action, revision.activated_at, revision.activated_by, revision.bound)Each item is a revision with the facts of one activation: activation (its
number), action (create, change, activate, or existing for the
settings a workflow had before its history began), activated_at,
activated_by (the key or the account that made the change) and bound
(this is the active one). A revision that was active twice is listed twice.
The history keeps the newest 100 activations.
Compare two revisions
Section titled “Compare two revisions”A revision’s settings is a small JSON object. Read two revisions and
compare their settings:
const before = await client.workflows.named.getRevision("sinks", "wrev_e3a41f0b9c2d7e8a5b6c1d0f9e8a7b6c");const after = await client.workflows.named.getRevision("sinks", "wrev_5d0c9e2a41b7f3368e10c4d2a9b8f701");console.log(before.settings.gate, after.settings.gate);The console shows what changed on each row of the history, and in the confirmation before you go back.
See whether a revision was tested
Section titled “See whether a revision was tested”Before you go back to earlier settings, check whether a test ever read
inputs with them. A revision of a values.extract workflow carries
last_tested:
const revision = await client.workflows.named.getRevision("sinks", "wrev_e3a41f0b9c2d7e8a5b6c1d0f9e8a7b6c");if (revision.last_tested) { const { test, finished, counts } = revision.last_tested; console.log(test, finished, counts.inputs_done, counts.passed, counts.review, counts.failed);} else if (revision.last_tested === null) { console.log("No test is recorded for these settings.");}revision = client.workflows.named.get_revision("sinks", "wrev_e3a41f0b9c2d7e8a5b6c1d0f9e8a7b6c")if revision.last_tested: print(revision.last_tested.test, revision.last_tested.finished, revision.last_tested.counts.passed)elif "last_tested" in revision.model_fields_set: print("No test is recorded for these settings.")last_tested names the newest test that read inputs with exactly this
revision, and the counts it ended with: inputs read, values that passed the
checks, values for review, properties with no value, and failures. It shows
what happened and does not judge it. A test counts when it ended done and
at least one input was read by the model. An experiment’s arm is a test too:
its last_tested has experiment (id and arm), and an experiment that
ended by its cost limit or the monthly budget has cancelled arms, which do
not count. Only a run of the revision as stored counts: in a model
experiment only arm A (the models in use) records a fact, and in a revision
experiment both arms record. A change of any setting, the monthly budget included, makes a new
revision with no test.
last_tested has three states. An object means a test read inputs with
these settings. null means the workflow has tests and no test is recorded
for these settings. A test that ran on an older node, or before this field
existed, is not recorded, so null does not prove that none ran. A response
without the key means the workflow has no tests, or the server cannot say;
that is unknown.
In the console, each row of the history shows Tested with the time, or No test recorded. When you choose Use these settings on a row with no recorded test, the confirmation warns and its button reads Use without a test. Activation is never refused for a missing test.
Go back to earlier settings
Section titled “Go back to earlier settings”In the console, choose Use these settings on a row of the history, read what changes, and confirm.
With the SDK, activate the revision. Pass the revision you read as active, so the call fails if someone else changed the workflow since:
const workflow = await client.workflows.named.activateRevision("sinks", "wrev_e3a41f0b9c2d7e8a5b6c1d0f9e8a7b6c", { expectedRevision: "wrev_5d0c9e2a41b7f3368e10c4d2a9b8f701",});console.log(workflow.active_revision);workflow = client.workflows.named.activate_revision( "sinks", "wrev_e3a41f0b9c2d7e8a5b6c1d0f9e8a7b6c", expected_revision="wrev_5d0c9e2a41b7f3368e10c4d2a9b8f701",)An activation makes the same checks as a change of the settings. It fails
with 409 workflow_revision_conflict when another revision is active than
the one you passed, and with 409 workflow_revision_stale when the graph
now reads those settings differently, for example after a field was
renamed. Then change the workflow with the settings you want. Activating the
active revision again changes nothing, so a retry is safe.
A change of the watched fields, the parameters or the gate makes the workflow read its class again, as a change of the settings does. Work that already waits or runs keeps the revision it started with; new work uses the new one.
Test earlier settings
Section titled “Test earlier settings”A test runs a workflow on chosen inputs and writes nothing to the graph.
Pass revision with name to run it on a revision of the history instead
of the settings in use; nothing is activated. Run one test per revision on
the same inputs and compare them:
const test = await client.workflows.tests.create({ name: "sinks", revision: "wrev_e3a41f0b9c2d7e8a5b6c1d0f9e8a7b6c", sample: 6,});See Test a workflow before it writes.
Runs name their revision
Section titled “Runs name their revision”Each run summary has a revision. A run keeps it after the history drops
that revision; reading the revision then answers 404 workflow_revision_not_found.
const runs = await client.workflows.named.runs("sinks");const byRevision = Map.groupBy(runs.data, (run) => run.revision);Agents
Section titled “Agents”The MCP server reads the history with lbb_workflows (actions revisions
and revision) and activates a revision with lbb_workflows_manage
(action activate). An activation changes what the workflow does from then
on and deletes no data, so it needs no extra approval. Show the user what
changes before you activate, and tell them when last_tested is null.
| Method and path | Does |
|---|---|
GET /v1/workflows/named/revisions?name=&cursor=&limit= |
The history, newest activation first |
GET /v1/workflows/named/revisions/get?name=&id= |
One revision |
POST /v1/workflows/named/revisions/activate |
{"name", "id", "expected_revision"?}: make a revision active |
The errors page lists workflow_revision_conflict,
workflow_revision_stale and workflow_revision_not_found.