route: generating complete configuration and capturing common checks
The route function group generates ordinary WorkflowRoute configuration; hand-written objects and generated configuration follow the same contract.
The first argument of every to* generator is the target node. route(target, id?) is an alias of route.to, and only its second argument and that of to is directly an optional route ID. Condition generators accept id, conditionId, and description in options; these can usually be omitted.
| Generator | Check and result |
|---|---|
| to(target, id?) | No when; jumps directly |
| toIf(target, options?) | Reads the same-named state field as a Boolean: Boolean(state[target]) |
| toIfDefined(target, field, options?) | The field is not undefined; null, false and 0 also count as defined |
| toIfError(target, options?) | By default checks whether error is defined; errorKey selects another field |
| toIfNotEmpty(target, key, options?) | The length of a known array is greater than 0; it is not a general empty-value check for arbitrary values |
| toIfEqual(target, path, value, options?) | The value read from path is strictly equal to the fixed value |
| toIfEqualWith(target, leftPath, rightPath, options?) | The values read from two state paths are strictly equal |
| deliver(field, options?) | Ends and reads the required field; undefined is rejected |
| deliverIfReady(field, options?) | Ends only when the field is defined, otherwise continues matching the next route |
| deliverWhenComplete(field, options?) | Ends only when StepOutcome.type is complete and takes its value |
The two Equal generators support dot paths such as profile.items.0.id; the current parameters of toIfDefined and toIfNotEmpty are field keys, and they must not be assumed to support paths. Strict equality does not deep-compare objects; when two missing paths both yield undefined they compare equal, so a preceding check should be configured when the business does not accept that.
Existing to* generators generally generate to-target from the target; toIfEqual generates an ID from the path, value, and target combination it can express stably, and toIfEqualWith generates one from the left and right paths and the target; symbol paths or non-scalar equal values cannot produce the same stable default ID. The deliver family conventionally uses complete. These are conventions of the specialized functions, not a uniform naming constraint imposed by Workflow.
Generators commonly use conditionId: {nid}.{rid}. Plain hand-written objects support the same two placeholders; Workflow only expands node values and the final local route ID, appends suffixes such as ~2 for duplicate route IDs, and does not guess IDs or specialized-function rules that were not provided.
Progressive examples for the same business case
Minimal jump and end
Beginner · Node.js
import { route } from '@jangts/agent-core/engine';
const jump = route('reply');
const sameJump = route.to('reply');
const finish = route.deliver('answer');
console.log(jump, sameJump, finish);import { route } from '@jangts/agent-core/engine';
const jump = route('reply');
const sameJump = route.to('reply');
const finish = route.deliver('answer');
console.log(jump, sameJump, finish);Common branches for the same business
Configuration · Node.js
import { route } from '@jangts/agent-core/engine';
const reviewRoutes = [
route.toIfError('failure'),
route.toIf('recovered'),
route.toIfNotEmpty('candidate', 'candidates'),
route.toIfDefined('reply', 'answer'),
route.toIfEqual('status', 'intent.id', 'status'),
route.toIfEqualWith('reuse', 'cache.0.id', 'request.id'),
route.to('fallback'),
];
const completionRoutes = [route.deliverIfReady('answer'), route.to('retry')];
const outcomeRoutes = [route.deliverWhenComplete('outcome'), route.to('retry')];
console.log(reviewRoutes, completionRoutes, outcomeRoutes);import { route } from '@jangts/agent-core/engine';
const reviewRoutes = [
route.toIfError('failure'),
route.toIf('recovered'),
route.toIfNotEmpty('candidate', 'candidates'),
route.toIfDefined('reply', 'answer'),
route.toIfEqual('status', 'intent.id', 'status'),
route.toIfEqualWith('reuse', 'cache.0.id', 'request.id'),
route.to('fallback'),
];
const completionRoutes = [route.deliverIfReady('answer'), route.to('retry')];
const outcomeRoutes = [route.deliverWhenComplete('outcome'), route.to('retry')];
console.log(reviewRoutes, completionRoutes, outcomeRoutes);Override identity as the business requires; hand-written objects also support placeholders
Extension · Node.js
conditionId carries no authorization. It helps logs explain which condition matched.
import { defineWorkflow, route } from '@jangts/agent-core/engine';
type State = { answer?: string; reviewError?: string };
const workflow = defineWorkflow<State, string>({ id: 'review', nodes: [
{ id: 'check', description: '检查既有结果', startsRound: true, routes: [
route.toIfError('failure', { errorKey: 'reviewError', id: 'review-error' }),
{ id: 'ready', conditionId: '{nid}.{rid}', when: state => state.answer !== undefined,
complete: state => state.answer! },
route.to('failure'),
] },
{ id: 'failure', description: '给出当前缺失信息',
run: () => ({ answer: '当前没有可交付结果' }), routes: [route.deliver('answer')] },
] });
console.log(workflow.describe());import { defineWorkflow, route } from '@jangts/agent-core/engine';
const workflow = defineWorkflow({ id: 'review', nodes: [
{ id: 'check', description: '检查既有结果', startsRound: true, routes: [
route.toIfError('failure', { errorKey: 'reviewError', id: 'review-error' }),
{ id: 'ready', conditionId: '{nid}.{rid}', when: state => state.answer !== undefined,
complete: state => state.answer },
route.to('failure'),
] },
{ id: 'failure', description: '给出当前缺失信息',
run: () => ({ answer: '当前没有可交付结果' }), routes: [route.deliver('answer')] },
] });
console.log(workflow.describe());