Documentsragflow
Component Variable Propagation
Component Variable Propagation
Type
Topic
Status
Published
Created
Jul 8, 2026
Updated
Jul 8, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Component Variable Propagation#

Variable propagation is the mechanism by which a canvas component's output values are stored in a shared state bag (CanvasState) and made available to downstream components via a {{cpn_id@variable}} reference syntax. It is the primary data-flow channel across an agent pipeline's DAG.


Reference Syntax#

Variable references take one of four forms :

FormResolves to
{{cpn_id@param}}Outputs[cpn_id][param]
{{cpn_id@param.path}}Dot-path traversal into Outputs[cpn_id][param]
{{sys.x}}Sys["x"] (query, user_id, conversation_turns, files)
{{env.x}}Env["x"] (deployment-time constants)

cpn_id is a DSL-assigned identifier such as begin_0, llm_0, or cpn_0. The iteration aliases {{item}} and {{index}} are also recognized and map to Globals["__item__"] / Globals["__index__"] .

The regex is defined in both the Go runtime and the Python base class — they must stay byte-for-byte identical:


CanvasState — the central store#

CanvasState (in internal/agent/runtime/state.go) is the per-run shared bag:

  • Outputs map[string]map[string]any — the primary variable store: Outputs[cpn_id][param_name] = value
  • Sys, Env, Globals — system variables, env constants, and loop/global aliases
  • History, Retrieval — conversation history and aggregated retrieval chunks
  • CancelFlag, RunID, TaskID — run lifecycle metadata

All maps are guarded by a single sync.RWMutex . CanvasState is registered with eino's type registry so it round-trips through checkpoint/resume paths .

Key accessors:

MethodPurpose
SetVar(cpnID, param, v)Write Outputs[cpnID][param] (supports nested dot-paths)
GetVar(ref)Resolve any cpn_id@param, sys.x, env.x, item, or index ref
ReadVars(refs)Batch-resolve a list of refs in a single locked pass
RecordOutput(cpnID, bucket, payload)Store a node result for downstream lookup
Snapshot()Return a shallow copy of all Outputs for statePre injection

How outputs are written#

Python side: each component calls set_output(key, value), which stores {value, type} in self._param.outputs[key]. For example, UserFillUp calls self.set_output(k, resolved) for each form field it processes . After invocation, the canvas persists each component's output() dict back into the shared graph state.

Go side: the eino statePost handler in scheduler.go flattens every key in a node's output map directly into Outputs[cpnID][key] , mirroring the Python convention so {{cpnID@key}} references work identically. Metadata keys (__cpn_id__, state, __legacy_noop__) are skipped.


How references are resolved#

Template resolution (Go): ResolveTemplate(s, state) replaces every {{...}} in a string with its current CanvasState value. Unresolvable refs are loud errors to surface misconfigured canvases early. ResolveTemplateForDisplay is the soft-fail variant (unresolved → ""), mirroring canvas.py's display path.

Python side: get_variable_value(exp) splits cpn_id@var_nm, fetches the component's output(root_key), then calls get_variable_param_value for any dot-path traversal. Traversal handles dict, list/tuple (integer index), JSON strings (auto-parsed once), and object attributes. get_value_with_variable handles mixed strings containing multiple references.

For parameter binding, get_input() on ComponentBase detects whether an input value is a bare reference (is_reff) or a templated string, resolves accordingly, and stores the result via set_input_value.


Lifecycle in the scheduler#

The eino scheduler wires statePre and statePost onto every node as graph options :

  1. statePre — before a node runs, injects a snapshot of all current Outputs into the input map under "state", and syncs eino's local state into the context-attached CanvasState .
  2. statePost — after a node runs, writes each output key into both the eino-local state and the context CanvasState via SetVar .

This double-write ensures that components reading state via runtime.GetStateFromContext (e.g. Begin, Message, LLM) always see upstream outputs regardless of which state object they hold.


Key files#

FileRole
internal/agent/runtime/state.goCanvasState struct, GetVar/SetVar/RecordOutput, dotTraverse
internal/agent/runtime/template.goVarRefPattern, ResolveTemplate, ResolveTemplateForDisplay
internal/agent/canvas/scheduler.gostatePre/statePost handlers, BuildWorkflow
agent/component/base.pyset_output, get_input, variable_ref_patt, get_input_elements_from_text
agent/canvas.pyget_variable_value, get_variable_param_value, get_value_with_variable, set_variable_value
agent/component/fillup.pyUserFillUp — example of a component writing multiple named outputs