A community-driven registry for Claude, Cursor, Windsurf, Cline & more. Not affiliated with Anthropic.
Are you the author? Sign in to claim
Pocket Flow: A minimalist LLM framework. Let Agents build Agents!
A Zig implementation of PocketFlow, a minimalist flow-based programming framework for building LLM-powered workflows.
PocketFlow-Zig is a port of the original Python PocketFlow framework, redesigned to leverage Zig's unique capabilities:
Node interface via vtables enables heterogeneous node typeszig fetch (Recommended)The easiest way to add PocketFlow-Zig to your project is using zig fetch --save:
# Fetch from a GitHub release tarball (recommended for stability)
zig fetch --save https://github.com/The-Pocket/PocketFlow-Zig/archive/refs/tags/v0.3.0.tar.gz
# Or fetch directly from a git repository
zig fetch --save git+https://github.com/The-Pocket/PocketFlow-Zig.git
# You can also specify a custom name for the dependency
zig fetch --save=pocketflow https://github.com/The-Pocket/PocketFlow-Zig/archive/refs/tags/v0.3.0.tar.gz
This automatically:
build.zig.zon fileAfter running zig fetch --save, your build.zig.zon will contain something like:
.dependencies = .{
.pocketflow = .{
.url = "https://github.com/The-Pocket/PocketFlow-Zig/archive/refs/tags/v0.3.0.tar.gz",
.hash = "1220...", // Auto-generated hash
},
},
Then add the import in your build.zig:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Fetch the pocketflow dependency
const pocketflow_dep = b.dependency("pocketflow", .{
.target = target,
.optimize = optimize,
});
// Get the module from the dependency
const pocketflow_mod = pocketflow_dep.module("pocketflow");
// Create your executable
const exe = b.addExecutable(.{
.name = "my_app",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
// Add the pocketflow import to your executable
exe.root_module.addImport("pocketflow", pocketflow_mod);
// Optional: also add the ollama module for LLM integration
const ollama_mod = pocketflow_dep.module("ollama");
exe.root_module.addImport("ollama", ollama_mod);
b.installArtifact(exe);
}
build.zig.zon ConfigurationIf you prefer to manually configure your dependencies, add the following to your build.zig.zon:
.{
.name = .my_project,
.version = "0.1.0",
.minimum_zig_version = "0.15.0",
.dependencies = .{
.pocketflow = .{
.url = "https://github.com/The-Pocket/PocketFlow-Zig/archive/refs/tags/v0.3.0.tar.gz",
// Get the hash by running: zig fetch https://github.com/The-Pocket/PocketFlow-Zig/archive/refs/tags/v0.3.0.tar.gz
.hash = "1220...",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}
To get the correct hash, run:
zig fetch https://github.com/The-Pocket/PocketFlow-Zig/archive/refs/tags/v0.3.0.tar.gz
This prints the hash without modifying any files.
For development or to track the latest changes:
.dependencies = .{
.pocketflow = .{
.url = "git+https://github.com/The-Pocket/PocketFlow-Zig.git",
.hash = "1220...",
},
},
Or with zig fetch:
zig fetch --save git+https://github.com/The-Pocket/PocketFlow-Zig.git
For local development or when vendoring:
.dependencies = .{
.pocketflow = .{
.path = "../PocketFlow-Zig",
},
},
# Clone the repository
git clone https://github.com/The-Pocket/PocketFlow-Zig.git
cd PocketFlow-Zig
# Build the library
zig build
# Run the example (requires Ollama running locally)
zig build run
# Run tests
zig build test
Each node implements prep, exec, and post phases:
const std = @import("std");
const pocketflow = @import("pocketflow");
const Node = pocketflow.Node;
const BaseNode = pocketflow.BaseNode;
const Context = pocketflow.Context;
const MyNode = struct {
base: BaseNode,
pub fn init(allocator: std.mem.Allocator) *MyNode {
const self = allocator.create(MyNode) catch @panic("oom");
self.* = .{ .base = BaseNode.init(allocator) };
return self;
}
pub fn deinit(self: *MyNode, allocator: std.mem.Allocator) void {
self.base.deinit();
allocator.destroy(self);
}
// Prepare: read from context, prepare data for execution
pub fn prep(_: *anyopaque, allocator: std.mem.Allocator, context: *Context) !*anyopaque {
const input = context.get([]const u8, "input") orelse "default";
const result = try allocator.create([]const u8);
result.* = input;
return @ptrCast(result);
}
// Execute: perform the main work (can be CPU-intensive)
pub fn exec(_: *anyopaque, allocator: std.mem.Allocator, prep_res: *anyopaque) !*anyopaque {
const input: *[]const u8 = @ptrCast(@alignCast(prep_res));
const output = try std.fmt.allocPrint(allocator, "Processed: {s}", .{input.*});
const result = try allocator.create([]const u8);
result.* = output;
return @ptrCast(result);
}
// Post: save results to context, return action for routing
pub fn post(_: *anyopaque, _: std.mem.Allocator, context: *Context, _: *anyopaque, exec_res: *anyopaque) ![]const u8 {
const output: *[]const u8 = @ptrCast(@alignCast(exec_res));
try context.set("output", output.*);
return "default"; // Action determines next node
}
pub fn cleanup_prep(_: *anyopaque, allocator: std.mem.Allocator, prep_res: *anyopaque) void {
const ptr: *[]const u8 = @ptrCast(@alignCast(prep_res));
allocator.destroy(ptr);
}
pub fn cleanup_exec(_: *anyopaque, allocator: std.mem.Allocator, exec_res: *anyopaque) void {
const ptr: *[]const u8 = @ptrCast(@alignCast(exec_res));
allocator.destroy(ptr);
}
pub const VTABLE = Node.VTable{
.prep = prep,
.exec = exec,
.post = post,
.cleanup_prep = cleanup_prep,
.cleanup_exec = cleanup_exec,
};
};
const pocketflow = @import("pocketflow");
const Flow = pocketflow.Flow;
const Context = pocketflow.Context;
const Node = pocketflow.Node;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Create nodes
const node1 = MyNode.init(allocator);
defer node1.deinit(allocator);
const node2 = MyNode.init(allocator);
defer node2.deinit(allocator);
// Wrap nodes with their vtables
const wrapper1 = Node{ .self = node1, .vtable = &MyNode.VTABLE };
const wrapper2 = Node{ .self = node2, .vtable = &MyNode.VTABLE };
// Connect nodes: node1 --"default"--> node2
try node1.base.next("default", wrapper2);
// Create and run flow
var flow = Flow.init(allocator, wrapper1);
var context = Context.init(allocator);
defer context.deinit();
try context.set("input", @as([]const u8, "Hello, PocketFlow!"));
try flow.run(&context);
if (context.get([]const u8, "output")) |output| {
std.debug.print("Result: {s}\n", .{output});
}
}
Nodes can return different actions to route to different successors:
pub fn post(_: *anyopaque, _: Allocator, context: *Context, _: *anyopaque, exec_res: *anyopaque) ![]const u8 {
const result: *i32 = @ptrCast(@alignCast(exec_res));
try context.set("result", result.*);
// Branch based on result
if (result.* > 100) {
return "high";
} else {
return "low";
}
}
// Later, when connecting nodes:
try node1.base.next("high", high_value_handler);
try node1.base.next("low", low_value_handler);
| Component | Description |
|---|---|
Node | Type-erased interface for workflow steps (via vtable) |
BaseNode | Provides successor management for routing |
Flow | Executes nodes in sequence, following action-based routing |
Context | Thread-safe key-value store for sharing data between nodes |
┌─────────┐ ┌─────────┐ ┌─────────┐
│ prep │ --> │ exec │ --> │ post │
└─────────┘ └─────────┘ └─────────┘
│ │ │
v v v
Read from Process Write to
Context Data Context
│
v
Return Action
(routes to next node)
See the examples/ directory:
Run the example:
# Requires Ollama running locally on port 11434
zig build run
// Initialize a new context
var ctx = Context.init(allocator);
defer ctx.deinit();
// Store a value
try ctx.set("key", value);
// Retrieve a value (returns null if not found)
if (ctx.get(MyType, "key")) |value| {
// use value
}
// Create a flow starting at a node
var flow = Flow.init(allocator, start_node);
// Run the flow with a context
try flow.run(&context);
// Add a successor for an action
try node.base.next("action_name", successor_node);
# Run all unit tests
zig build test
# Run integration tests (requires Ollama server)
zig build test-integration
PocketFlow-Zig/
├── src/
│ ├── pocketflow.zig # Main library exports
│ ├── node.zig # Node interface and BaseNode
│ ├── flow.zig # Flow execution engine
│ ├── context.zig # Thread-safe context storage
│ └── ollama.zig # Ollama LLM client (optional)
├── examples/
│ └── document_generator.zig
├── build.zig
├── build.zig.zon
├── README.md
└── LICENSE
Contributions are welcome! Areas of interest:
Please submit pull requests or open issues for discussion.
An AI-powered custom node for ComfyUI designed to enhance workflow automation and provide intelligent assistance
Deterministic multi-agent pipeline for end-to-end software development, orchestrating CLI-based AI tools (e.g. Gemini, C
💻 A curated list of papers and resources for multi-modal Graphical User Interface (GUI) agents.
npx CLI installing 100+ agents, commands, hooks, and integrations in one command