Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ddf8e89
allow for attribute access to local memory
zvictor May 16, 2025
078fda3
fix #20
zvictor May 16, 2025
6508030
minor implementation simplification
zvictor May 16, 2025
c2379aa
add contributions call
zvictor May 16, 2025
385f7fe
propagate orphan actions triggered inside a flow
zvictor May 16, 2025
b968c13
allow for deletion of data in the memory
zvictor May 17, 2025
2b8ec76
fix generic types
zvictor May 17, 2025
5b6226a
add the `ExecutionTree` type
zvictor May 17, 2025
147c7d0
redefine the structure of the execution log
zvictor May 17, 2025
439a8ca
rewrite the migration guide from pocketflow
zvictor May 22, 2025
fbfed36
simplify ActionLinker implementation
zvictor May 22, 2025
a2bac59
avoid error rethrowing
zvictor May 22, 2025
8e2dd3c
increase default `max_visits` to 15
zvictor May 22, 2025
edbe0c6
Prepare Python package to use Changesets
zvictor May 22, 2025
3885bc4
Prepare Python package to use Changesets (2)
zvictor May 22, 2025
dfe0621
Prepare Python package to use Changesets
zvictor May 22, 2025
c9c55a5
workaround for github.com/changesets/action/issues/501
zvictor May 22, 2025
75c0c80
fix `LocalProxy` equality check
zvictor May 22, 2025
8e08091
minor improvements
zvictor May 22, 2025
10289fa
add changeset
zvictor May 22, 2025
30a3341
new memory object, using factory+proxy
zvictor May 23, 2025
4e4635f
remove `Memory.create`
zvictor May 23, 2025
e1fac68
fix styling format
zvictor May 23, 2025
b143228
update dependencies
zvictor May 23, 2025
9ca73e3
Merge remote-tracking branch 'origin/main' into revision-may
zvictor May 24, 2025
d422856
permeate terminal actions
zvictor May 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@

strict_env
layout node
dotenv_if_exists

use nix
18 changes: 14 additions & 4 deletions .github/workflows/changeset-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,24 @@ jobs:
typescript:
- 'typescript/**'

- name: Prepare Python package to use Changesets
if: steps.filter.outputs.python == 'true'
working-directory: python
run: |
echo "Initializing pnpm project..."
touch pnpm-workspace.yaml
pnpm init
echo "Checking .changeset:"
ls -la .changeset

- name: Check for Python changesets
# Only run this step if python files were changed
if: steps.filter.outputs.python == 'true'
id: python-check
uses: changesets/action@v1
uses: changesets/action@v1.4.9
continue-on-error: true
with:
cwd: ./python
cwd: '${{ github.workspace }}/python'
version: false
publish: false
setupGitUser: true
Expand All @@ -70,10 +80,10 @@ jobs:
# Only run this step if typescript files were changed
if: steps.filter.outputs.typescript == 'true'
id: typescript-check
uses: changesets/action@v1
uses: changesets/action@v1.4.9
continue-on-error: true
with:
cwd: ./typescript
cwd: '${{ github.workspace }}/typescript'
version: false
publish: false
setupGitUser: true
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
python/README.md

# OS generated files
.DS_Store
.DS_Store?
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ Check out [Agentic Coding Guidance](https://brainy.gitbook.io/flow/guides/agenti

We would like to extend our deepest gratitude to the creators and contributors of the PocketFlow framework, from which brainyFlow originated as a fork.

## Contributors Wanted!

We're looking for contributors for all aspects of the project. Whether you're interested in documentation, testing, or implementing features, we'd love your help!

Get involved by joining our [Discord server](https://discord.gg/N9mVvxRXyH).

## Liability Disclaimer

BrainyFlow is provided "as is" without any warranties or guarantees.
Expand Down
10 changes: 5 additions & 5 deletions cookbook/typescript-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
"license": "ISC",
"description": "",
"devDependencies": {
"@types/node": "^22.14.0",
"tsx": "^4.19.3",
"@types/node": "^22.15.21",
"tsx": "^4.19.4",
"typescript": "^5.8.3"
},
"dependencies": {
"@phukon/duckduckgo-search": "^1.1.0",
"brainyflow": "^0.0.5",
"openai": "^4.91.1",
"yaml": "^2.7.1"
"brainyflow": "^1.0.0",
"openai": "^4.103.0",
"yaml": "^2.8.0"
}
}
8 changes: 4 additions & 4 deletions cookbook/typescript-chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
"license": "ISC",
"description": "",
"devDependencies": {
"@types/node": "^22.13.17",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
"@types/node": "^22.15.21",
"tsx": "^4.19.4",
"typescript": "^5.8.3"
},
"dependencies": {
"openai": "^4.91.1"
"openai": "^4.103.0"
}
}
4 changes: 2 additions & 2 deletions docs/core_abstraction/flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ flowchart LR

### Cycle Detection

Loops are created by connecting a node back to a previously executed node. To prevent infinite loops, `Flow` includes cycle detection controlled by the `maxVisits` option in its constructor (default is 5). If a node is visited more times than `maxVisits` during a single run, an error is thrown.
Loops are created by connecting a node back to a previously executed node. To prevent infinite loops, `Flow` includes cycle detection controlled by the `maxVisits` option in its constructor (default is 15). If a node is visited more times than `maxVisits` during a single run, an error is thrown.

{% hint style="success" %}
This ensures that the flow does not get stuck in an infinite loop.
Expand All @@ -313,7 +313,7 @@ This ensures that the flow does not get stuck in an infinite loop.
const flow = new Flow(startNode, { maxVisits: 10 })
```

- The default value for `maxVisits` is `5`.
- The default value for `maxVisits` is `15`.
- Set `maxVisits` to `Infinity` or a very large number for effectively no limit (use with caution!).

## Flow Parallelism
Expand Down
54 changes: 37 additions & 17 deletions docs/guides/migrating_from_pocketflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ from brainyflow import Node, Flow, SequentialBatchNode # ... etc
### Step 2: Add `async` / `await`:

- Add `async` before `def` for your `prep`, `exec`, `post`, and `exec_fallback` methods in Nodes and Flows.
- Remove any `_async` suffix from the method names.
- Add `await` before any calls to these methods, `run()` methods, `asyncio.sleep()`, or other async library functions.

#### Node Example (Before):
Expand Down Expand Up @@ -106,32 +107,49 @@ class MyNode(Node):

_(Flow methods follow the same pattern)_

### Step 3: Update Batch Processing Implementation (`*BatchNode` / `*BatchFlow` Removal)
### Step 3: Use `.trigger()` for next actions

Check all `.post()` methods and replace any `return action` with a call to `self.trigger(action)`. _`return "default"` can be either replaced or removed._

### Step 4: Replace memory access methods:

Replace `shared.get(` by `getattr(shared, ` if `shared` is a `Memory` instance.

### Step 5: Update Batch Processing Implementation (`*BatchNode` / `*BatchFlow` Removal)

PocketFlow had dedicated classes like `BatchNode`, `ParallelBatchNode`, `BatchFlow`, and `ParallelBatchFlow`. BrainyFlow v0.3+ **removes these specialized classes**.

The functionality is now achieved using standard `Node`s and `Flow`s combined with a specific pattern:
The batch functionality is now achieved using standard `Node`s and `Flow`s combined with a specific pattern:

1. **Rename Classes**:
1. **Adopt the Fan-Out Trigger Pattern**:

- Replace `BatchNode`, `AsyncBatchNode`, `ParallelBatchNode`, `AsyncParallelBatchNode` with the standard `brainyflow.Node`.
- Replace `BatchFlow`, `AsyncBatchFlow` with `brainyflow.Flow`.
- Replace `AsyncParallelBatchFlow` with `brainyflow.ParallelFlow`.
- Remember to make `prep`, `exec`, `post` methods `async` as per Step 2.
All `BatchNode` need to be split into two, a **Trigger Node** and a **Processor Node**.

2. **Adopt the Fan-Out Trigger Pattern**:
The **Trigger Node**:
- Use the `prep` method to fetch the list of items to process, as usual.
- Use the `post` method to iterate through these items. For **each item**, calls `self.trigger(action, forkingData={"item": current_item, "index": i, ...})`. The `forkingData` dictionary passes item-specific data into the **local memory** of the triggered successor. (the `action` name can be any of your choice as long as you connect the nodes in the flow; e.g. `process_one`, `default`)

- The node that previously acted as the `BatchNode` (or the starting node of a `BatchFlow`) needs to be refactored into a **Trigger Node**.
- Its `prep` method usually fetches the list of items to process.
- Its `post` method iterates through these items. For **each item**, it calls `self.trigger("process_one", forkingData={"item": current_item, "index": i, ...})`. The `forkingData` dictionary passes item-specific data into the **local memory** of the triggered successor.
- The logic previously in the `exec_one` method of the `BatchNode` must be moved into the `exec` method of a new **Processor Node**.
- This `ProcessorNode` is connected to the `TriggerNode` via the `"process_one"` action (e.g., `trigger_node.on("process_one", processor_node)`).
- The `ProcessorNode`'s `prep` method reads the specific item data (e.g., `memory.item`, `memory.index`) from its **local memory**, which was populated by the `forkingData`.
The **Processor Node**:
- The `ProcessorNode`'s `prep` method reads the specific item data (e.g., `memory.item`, `memory.index`) from its **local memory**, which was populated by the `forkingData` in the trigger node.
- The logic previously in the `exec_one` method of the `BatchNode` must be renamed to `exec`.
- Its `post` method typically writes the result back to the **global memory**, often using the index to place it correctly in a shared list or dictionary.

3. **Choose the Right Flow**:
Similarly, `BatchFlow` need to be split into a `Node` and a regular `Flow`:
- Replace the return value of the `prep` method with a `post` method containing trigger calls.
- Instead of `self.params["property"]`, use the usual `memory.property`.

2. **Choose the Right Flow**:
- Wrap the `TriggerNode` and `ProcessorNode` in a standard `brainyflow.Flow` if you need items processed **sequentially**.
- Wrap them in a `brainyflow.ParallelFlow` if items can be processed **concurrently**.
- Connect the nodes: `trigger_node >> processor_node` or `trigger_node - action >> processor_node`

3. **Rename All Classes**:

- Replace `AsyncParallelBatchFlow` with `brainyflow.ParallelFlow`.
- Replace `AsyncParallelBatchNode`, `ParallelBatchNode`, `AsyncBatchNode`, `BatchNode` with the standard `brainyflow.Node`.
- Replace `AsyncBatchFlow`, `BatchFlow` with `brainyflow.Flow`.
- Remember to make `prep`, `exec`, `post` methods `async` as per Step 2.


#### Example: Translating Text into Multiple Languages

Expand Down Expand Up @@ -330,7 +348,7 @@ triggerNode.next(processorNode)

_(See the [MapReduce design pattern](../design_pattern/mapreduce.md) for more detailed examples of fan-out/aggregate patterns)._

### Step 4: Run with `asyncio`:
### Step 6: Run with `asyncio`:

BrainyFlow code must be run within an async event loop. The standard way is using `asyncio.run()`:

Expand All @@ -353,9 +371,11 @@ if __name__ == "__main__":
Migrating from PocketFlow to BrainyFlow primarily involves:

1. Updating imports to `brainyflow` and adding `import asyncio`.
2. Adding `async` to your Node/Flow method definitions (`prep`, `exec`, `post`, `exec_fallback`).
2. Adding `async` to your Node/Flow method definitions (`prep`, `exec`, `post`, `exec_fallback`) and removing any `_async` suffix from the method names.
3. Replacing any `return action` in `post()` with a call to `self.trigger(action)`.
3. Using `await` when calling `run()` methods and any other asynchronous operations within your methods.
4. Replacing `BatchNode`/`BatchFlow` with the appropriate `Sequential*` or `Parallel*` BrainyFlow classes.
5. Running your main execution logic within an `async def main()` function called by `asyncio.run()`.
6. Replacing all `return action` by `self.trigger(action)` in your Node methods.

This transition enables you to leverage the performance and concurrency benefits of asynchronous programming in your workflows.
4 changes: 2 additions & 2 deletions docs/guides/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ async def mock_llm_logic(prompt: str) -> str:
async def test_node_with_mocked_llm():
# Assume MyLlmNode calls utils.call_llm internally
# node = MyLlmNode()
# memory = Memory.create({"input": "some text to summarize"})
# memory = Memory({"input": "some text to summarize"})

# Use patch to replace the actual call_llm
with patch('utils.call_llm', new=AsyncMock(side_effect=mock_llm_logic)) as mock_call:
Expand Down Expand Up @@ -394,7 +394,7 @@ async def test_retry_logic():
global call_count_retry
call_count_retry = 0 # Reset counter for test
# node = NodeWithRetry()
# memory = Memory.create({})
# memory = Memory({})

# Patch the external call made within node.exec
# Also patch asyncio.sleep to avoid actual waiting
Expand Down
4 changes: 4 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
machine-display: false
---

# Installation

BrainyFlow is currently available for both Python and TypeScript.
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"@ianvs/prettier-plugin-sort-imports": "4.4.1",
"@total-typescript/ts-reset": "0.6.1",
"@tsconfig/node-lts": "22.0.1",
"@types/node": "22.13.14",
"@types/node": "22.15.21",
"prettier": "3.5.3",
"tsx": "4.19.3",
"typescript": "^5.8.2"
"tsx": "4.19.4",
"typescript": "5.8.3"
},
"packageManager": "pnpm@10.8.1"
"packageManager": "pnpm@10.11.0"
}
9 changes: 5 additions & 4 deletions prettier.config.cjs → prettier.config.mjs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
/**
* @see https://prettier.io/docs/en/configuration.html
* @see https://prettier.io/docs/configuration
* @type {import("prettier").Config}
*/

module.exports = {
const config = {
editorconfig: true,
semi: false,
useTabs: false,
singleQuote: true,
arrowParens: 'always',
tabWidth: 2,
printWidth: 100,
printWidth: 130,
trailingComma: 'all',
plugins: ['@ianvs/prettier-plugin-sort-imports'],
importOrderTypeScriptVersion: '5.4.5',
Expand Down Expand Up @@ -40,3 +39,5 @@ module.exports = {
'^./types$',
],
}

export default config
60 changes: 60 additions & 0 deletions python/.changeset/may.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
"brainyflow": major
---

# Major refactor and enhancement release

## Breaking Changes

* **Memory creation**: If you were creating a new Memory object using the `Memory.create(data)` static class method, you will need to replace it by simply `Memory(data)`.
* **Flow.run()**: If you were traversing the nodes representation given by the `.run()` method, you will need to update your code to use the new `ExecutionTree` class.

### Core Library Changes
- **Memory class**: Considerable refactor with new deletion methods (`__delattr__`, `__delitem__`) and improved proxy behavior for local memory access
- **Flow class**: Default `maxVisits` increased from 5 to 15 for cycle detection
- **NodeError**: Changed from Exception class to Protocol interface
- **ExecutionTree**: Updated structure for better result aggregation and tracking
- **Type annotations**: Improved throughout with better Generic constraints and Protocol usage, fixing all type errors and inconsistencies

### API Changes
- Memory deletion operations now support both attribute and item-style deletion
- Error message format updated for cycle detection: now shows "Maximum cycle count (N) reached for ClassName#nodeId"
- Node execution warnings removed for nodes with successors

## New Features

### Memory Management
- Added comprehensive deletion support for Memory objects
- New local proxy with isolated deletion operations
- Better memory cloning with forking data support
- Enhanced store management with helper functions

### Developer Experience
- Improved migration documentation with detailed examples
- Added "Contributors Wanted" section to encourage community participation
- Better test isolation and predictable node ID management
- Enhanced error messages and debugging information

## Infrastructure Improvements

### CI/CD Pipeline
- Updated changesets action to v1.4.9 (workaround for github.com/changesets/action/issues/501)

### Testing
- Comprehensive test suite updates for new Memory functionality
- Added deletion operation tests
- Improved test reliability with BaseNode ID resets
- Better assertion patterns matching new error formats

### Documentation
- Updated migration guide from PocketFlow with clearer examples
- Enhanced core abstraction documentation
- Improved installation and setup instructions

## Bug Fixes
- Fixed memory proxy behavior edge cases
- Improved error handling in node execution
- Better cycle detection error reporting
- Enhanced type safety throughout the codebase

This release represents a significant improvement in memory management, developer experience, and overall library robustness while maintaining the core workflow orchestration capabilities.
Loading
Loading