Memory
BrainyFlow provides a streamlined approach for components to communicate with each other. This chapter explains how data is stored, accessed, and isolated.
Memory Scopes: Global vs. Local
Each Memory
instance encapsulates two distinct scopes:
Global Store (
memory
): A single object shared across all nodes within a singleflow.run()
execution. Changes made here persist throughout the flow. Think of it as the main shared state.Local Store (
memory.local
): An object specific to a particular execution path within the flow. It's created when a nodetrigger
s a successor. Changes here are isolated to that specific branch and its descendants.
This dual-scope system allows for both shared application state (global) and controlled, path-specific data propagation (local).
Real-World Analogies:
Think of the memory system like a river delta:
Global Store: The main river water that flows everywhere
Local Store: Specific channels that might carry unique properties that only affect downstream areas fed by that channel
Alternatively, think of it like nested scopes in programming:
Global Store: Like variables declared in the outermost scope of a program, accessible everywhere.
Local Store: Like variables declared inside a function or block. They are only accessible within that block and any nested blocks (downstream nodes in the flow). If a local variable has the same name as a global one, the local variable "shadows" the global one within its scope.
This model gives you the flexibility to share data across your entire flow (global) or isolate context to specific execution paths (local).
Accessing Memory (Reading)
Nodes access data stored in either scope through the memory
proxy instance passed to their prep
and post
methods. When you read a property (e.g., memory.someValue
), the proxy automatically performs a lookup:
It checks the local store (
memory.local
) first.If the property is not found locally, it checks the global store (
memory
).
When accessing memory, you should always use memory.someValue
and let the Memory
manager figure out where to fetch the value for you.
You could also directly access the entire local store object using memory.local
- or a value at memory.local.someValue
- but that's an anti-pattern that should be avoided.
Writing to Memory
Writing to Global Store: Assigning a value directly to a property on the
memory
object (e.g.,memory.someValue = 'new data'
) writes to the global store. The proxy automatically removes the property from the local store first if it exists there.Writing to Local Store: While possible via
memory.local.someValue = 'new data'
, the primary and recommended way to populate the local store for downstream nodes is using theforkingData
argument inthis.trigger()
.
Note that you can set types to the memory, like in TypeScript! That is optional, but helps you keep your code organized.
Best Practices
Read in
prep()
: Gather necessary input data frommemory
at the beginning of a node's execution.Write Global State in
post()
: Update the shared global store by assigning tomemory
properties (e.g.,memory.results = ...
) in thepost()
phase after processing is complete.Set Local State via
forkingData
: Pass branch-specific context to successors by providing theforkingData
argument inthis.trigger()
within the parent'spost()
method. This populates thelocal
store for the next node(s).Read Transparently: Always read data via the
memory
proxy (e.g.,memory.someValue
). It handles the local-then-global lookup automatically. Avoid reading directly frommemory.local
or other internal properties unless strictly needed.
When to Use The Memory
Ideal for: Sharing data results, large content, or information needed by multiple components
Benefits: Separates data from computation logic (separation of concerns)
Global Memory: Use for application-wide state, configuration, and final results
Local Memory: Use for passing contextual data down a specific execution path
Technical Concepts
The memory system in BrainyFlow implements several established computer science patterns:
Lexical Scoping: Local memory "shadows" global memory, similar to how local variables in functions can shadow global variables
Context Propagation: Local memory propagates down the execution tree, similar to how context flows in React or middleware systems
Transparent Resolution: The system automatically resolves properties from the appropriate memory scope
Remember
Reading: Always read via the
memory
proxy (e.g.,memory.value
). It checks local then global.Writing: Direct assignment
memory.property = value
writes to the global store.Local State Creation: Use
trigger(action, forkingData)
inpost()
to populate thelocal
store for the next node(s) in a specific branch.Lifecycle: Read from
memory
inprep
, compute inexec
(no memory access), write global state tomemory
and trigger successors (potentially withforkingData
for local state) inpost
.
Last updated