283 lines
8.9 KiB
Plaintext

# Use Variables
## Business Background
In Workflow orchestration, information needs to be passed between nodes. To achieve this, we use **variables** to store and manage this information.
:::warning A variable consists of three main parts:
1. **Unique Identifier**: The name of the variable, used to distinguish different variables so that they can be accurately referenced and used in the program. For example: `userName` or `totalAmount`.
2. **Value**: The data stored by the variable. The value can be of various types, such as numbers (e.g., `42`), strings (e.g., `"Hello!"`), boolean values (e.g., `true`), etc.
3. **Type**: The kind of data that the variable can store. The type determines what kind of values the variable can accept. For example, a variable can be an integer, float, string, or boolean, etc.
:::
Here is an example of a workflow orchestration: The WebSearch node retrieves knowledge and passes it to the LLM node for analysis through the `natural_language_desc`.
<div style={{display: 'flex', gap: '20px'}}>
<img style={{width: "50%"}} loading="lazy" src="/variable/variable-biz-context-websearch-llm.png" />
<div>
In this example:
<p style={{marginTop: 10}}>1. The WebSearch node stores the information (value) in a variable with the unique identifier `natural_language_desc`.</p>
<p style={{marginTop: 5}}>2. The LLM node retrieves the information (value) from the knowledge base using the unique identifier `natural_language_desc` and passes it to the LLM node for analysis.</p>
<p style={{marginTop: 5}}>3. The type of the `natural_language_desc` variable is a string, representing the content of the information retrieved from the web, such as "DeepSeek has released a new model today."</p>
</div>
</div>
## What is the Variable Engine?
The Variable Engine is an optional built-in feature provided by Flowgram that helps to efficiently implement **variable information orchestration** during workflow design. It can achieve the following functions:
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "25px" }}>
<div style={{ gridColumn: "span 2" }}>
<b>Scope Constraint Control</b>
<p className="rs-tip">With the Variable Engine, you can control the scope of variables, ensuring that variables are available within the appropriate range and avoiding unnecessary conflicts.</p>
<div style={{display: "flex", gap: "25px"}}>
<div>
<img loading="lazy" src="/variable/variable-scope-feature-1.png" />
<p style={{marginTop: '10px'}}>In the image, the query variable of the Start node can be accessed by the subsequent LLM node and End node.</p>
</div>
<div>
<img loading="lazy" src="/variable/variable-scope-feature-2.png" />
<p style={{marginTop: '10px'}}>In the image, the LLM node is within the Condition branch, while the End node is outside the Condition branch; therefore, the variable selector of the End node cannot select the result variable from the LLM node.</p>
</div>
</div>
</div>
<div>
<b>Maintenance of Variable Information Tree</b>
<p className="rs-tip">The Variable Engine can help you build a clear variable information tree, making it easier to view and manage the status and relationships of all variables.</p>
<img loading="lazy" src="/variable/variable-tree-management.gif" />
<p style={{marginTop: '10px'}}>The image shows multiple nodes + global configuration output variables; some variables contain multiple sub-variables, forming a tree structure.</p>
</div>
<div>
<b>Automatic Type Inference of Variables</b>
<p className="rs-tip">The Variable Engine can automatically infer the type of variables based on context, reducing the workload of manually specifying types and improving development efficiency.</p>
<img loading="lazy" src="/variable/variable-batch-auto-infer.gif" />
<p style={{marginTop: '10px'}}>In the image, the Batch node processes the arr variable from the Start node; when the type of the arr variable changes, the type of the item variable output by the Batch node also changes accordingly.</p>
</div>
</div>
## Enable Variable Engine
[> API Detail](https://flowgram.ai/auto-docs/editor/interfaces/VariablePluginOptions.html)
```tsx pure title="use-editor-props.ts" {3}
// EditorProps
{
variableEngine: {
/**
* Need to enable the variable engine to use
*/
enable: true
}
}
```
## Node Output Variables
[> Demo Detail](https://github.com/bytedance/flowgram.ai/blob/main/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts#L25)
### Set Output Variables through FlowNodeVariableData
```tsx pure title="sync-variable-plugin.tsx"
import {
FlowNodeVariableData,
ASTFactory,
} from '@flowgram.ai/fixed-layout-editor';
// ....
flowDocument.onNodeCreate(({ node }) => {
const variableData = node.getData<FlowNodeVariableData>(FlowNodeVariableData);
// ....
// 1. Clear VariableData if No value
variableData.clearVar()
// 2. Set a String Variable as output
variableData.setVar(
ASTFactory.createVariableDeclaration({
meta: {
title: `Your Output Variable Title`,
},
key: `your_variable_global_unique_key_${node.id}`,
type: ASTFactory.createString(),
})
)
// 3. Set a Complicated Variable Data as output
variableData.setVar(
ASTFactory.createVariableDeclaration({
meta: {
title: `Your Output Variable Title`,
},
key: `your_variable_global_unique_key_${node.id}`,
type: ASTFactory.createArray({
items: ASTFactory.createObject({
properties: [
ASTFactory.createProperty({
key: 'stringType',
type: ASTFactory.createString(),
}),
ASTFactory.createProperty({
key: 'booleanType',
type: ASTFactory.createBoolean(),
}),
ASTFactory.createProperty({
key: 'numberType',
type: ASTFactory.createNumber(),
}),
ASTFactory.createProperty({
key: 'integerType',
type: ASTFactory.createInteger(),
}),
],
}),
}),
})
);
// ....
})
// ....
```
More usage, see: [Class: FlowNodeVariableData](https://flowgram.ai/auto-docs/editor/classes/FlowNodeVariableData.html)
### Set Output Variables through Form Effect
```tsx pure title="node-registries.ts"
import {
FlowNodeRegistry,
createEffectFromVariableProvider,
ASTFactory,
type ASTNodeJSON
} from '@flowgram.ai/fixed-layout-editor';
export function createTypeFromValue(value: string): ASTNodeJSON | undefined {
switch (value) {
case 'string':
return ASTFactory.createString();
case 'number':
return ASTFactory.createNumber();
case 'boolean':
return ASTFactory.createBoolean();
case 'integer':
return ASTFactory.createInteger();
default:
return;
}
}
export const nodeRegistries: FlowNodeRegistry[] = [
{
type: 'start',
formMeta: {
effect: {
'path.to.value': createEffectFromVariableProvider({
// parse form value to variable
parse(v: string) {
return {
meta: {
title: `Your Output Variable Title`,
},
key: `your_variable_global_unique_key_${node.id}`,
type: createTypeFromValue(v)
}
}
}),
},
render: () => (
// ...
)
},
}
]
```
## Node Consumes Variables
### Get Variable List
```tsx pure title="use-variable-tree.tsx"
import {
type BaseVariableField,
useScopeAvailable,
} from '@flowgram.ai/fixed-layout-editor';
// .... Inside react hooks or component
const available = useScopeAvailable()
const renderVariable = (variable: BaseVariableField) => {
// ....
}
return available.variables.map(renderVariable)
// ....
```
### Get Drilldown of Object Type Variable
```tsx pure title="use-variable-tree.tsx"
import {
type BaseVariableField,
ASTMatch,
} from '@flowgram.ai/fixed-layout-editor';
// ....
const renderVariable = (variable: BaseVariableField) => ({
title: variable.meta?.title,
key: variable.key,
// Only Object Type can drilldown
children: ASTMatch.isObject(type) ? type.properties.map(renderVariable) : [],
});
// ....
```
### Get Drilldown of Array Type Variable
```tsx pure title="use-variable-tree.tsx"
import {
type BaseVariableField,
type BaseType,
ASTMatch,
} from '@flowgram.ai/fixed-layout-editor';
// ....
const getTypeChildren = (type?: BaseType): BaseVariableField[] => {
if (!type) return [];
// get properties of Object
if (ASTMatch.isObject(type)) return type.properties;
// get items type of Array
if (ASTMatch.isArray(type)) return getTypeChildren(type.items);
};
const renderVariable = (variable: BaseVariableField) => ({
title: variable.meta?.title,
key: variable.key,
// Only Object Type can drilldown
children: getTypeChildren(variable.type).map(renderVariable),
});
// ....
```