open-orbyt 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.so
4
+ *.egg
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .venv/
9
+ .env
10
+ .pytest_cache/
11
+ .mypy_cache/
12
+ .ruff_cache/
13
+ *.lock
14
+ .idea/
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: open_orbyt
3
+ Version: 0.1.0
4
+ Summary: A workflow framework for Python. Build AI agents, data pipelines, and orchestration flows with nodes, graphs, and a shared store.
5
+ Project-URL: Homepage, https://github.com/erickweyunga/orbyt
6
+ Project-URL: Repository, https://github.com/erickweyunga/orbyt
7
+ Project-URL: Documentation, https://github.com/erickweyunga/orbyt#readme
8
+ Project-URL: Issues, https://github.com/erickweyunga/orbyt/issues
9
+ Author-email: Maverick Weyunga <maverickweyunga@gmail.com>
10
+ License-Expression: MIT
11
+ Keywords: agent,ai,dag,flow,graph,orchestration,pipeline,workflow
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: pydantic>=2.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # orbyt
27
+
28
+ A workflow framework for Python. Build AI agents, data pipelines, and orchestration flows with nodes, graphs, and a shared store.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install orbyt
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```python
39
+ from orbyt import Flow, SharedStore, Result, new_node, run, DEFAULT_ACTION
40
+
41
+ # Create nodes
42
+ fetch = (
43
+ new_node()
44
+ .with_name("fetch")
45
+ .with_exec_func(lambda r: Result({"users": ["Alice", "Bob", "Charlie"]}))
46
+ .with_post_func(lambda shared, p, e: shared.set("data", e.value) or DEFAULT_ACTION)
47
+ )
48
+
49
+ transform = (
50
+ new_node()
51
+ .with_name("transform")
52
+ .with_prep_func(lambda shared: Result(shared.get_dict("data")))
53
+ .with_exec_func(lambda r: Result([name.upper() for name in r.must_dict()["users"]]))
54
+ .with_post_func(lambda shared, p, e: shared.set("result", e.value) or DEFAULT_ACTION)
55
+ )
56
+
57
+ # Connect into a flow
58
+ flow = Flow(fetch)
59
+ flow.connect(fetch, DEFAULT_ACTION, transform)
60
+
61
+ # Run
62
+ shared = SharedStore()
63
+ flow.run_flow(shared)
64
+ print(shared.get_list("result")) # ['ALICE', 'BOB', 'CHARLIE']
65
+ ```
66
+
67
+ ## Core Concepts
68
+
69
+ ### Nodes
70
+
71
+ Every node runs three phases: **prep** (read inputs) → **exec** (do the work, retryable) → **post** (write results, return an action).
72
+
73
+ ```python
74
+ node = (
75
+ new_node()
76
+ .with_max_retries(3)
77
+ .with_wait(1.0)
78
+ .with_prep_func(lambda shared: Result(shared.get_string("input")))
79
+ .with_exec_func(lambda r: Result(r.must_string().upper()))
80
+ .with_post_func(lambda shared, p, e: shared.set("output", e.value) or DEFAULT_ACTION)
81
+ )
82
+ ```
83
+
84
+ Or subclass for stateful nodes:
85
+
86
+ ```python
87
+ class MyNode(BaseNode):
88
+ def __init__(self):
89
+ super().__init__(max_retries=3, wait=0.5)
90
+
91
+ def prep(self, shared):
92
+ return shared.get_string("input")
93
+
94
+ def exec(self, prep_result):
95
+ return prep_result.upper()
96
+
97
+ def post(self, shared, prep_result, exec_result):
98
+ shared.set("output", exec_result)
99
+ return DEFAULT_ACTION
100
+ ```
101
+
102
+ ### Flows
103
+
104
+ Connect nodes with action-based routing to build directed graphs:
105
+
106
+ ```python
107
+ flow = Flow(start_node)
108
+ flow.connect(start_node, "success", next_node)
109
+ flow.connect(start_node, "error", error_node)
110
+ flow.connect(next_node, DEFAULT_ACTION, final_node)
111
+ flow.run_flow(shared)
112
+ ```
113
+
114
+ Flows implement the Node interface - nest them inside other flows.
115
+
116
+ ### SharedStore
117
+
118
+ Thread-safe key-value store with typed getters:
119
+
120
+ ```python
121
+ shared = SharedStore()
122
+ shared.set("count", 42)
123
+ shared.get_int("count") # 42
124
+ shared.get_int_or("missing", -1) # -1
125
+ shared.bind("user", UserModel) # pydantic or dataclass
126
+ ```
127
+
128
+ ### Batch Processing
129
+
130
+ Process lists of items with configurable concurrency:
131
+
132
+ ```python
133
+ from orbyt import new_batch_node
134
+
135
+ batch = (
136
+ new_batch_node()
137
+ .with_batch_concurrency(5)
138
+ .with_batch_error_handling(True)
139
+ .with_prep_func(lambda shared: [Result(url) for url in shared.get_list("urls")])
140
+ .with_exec_func(lambda r: Result(fetch(r.must_string())))
141
+ .with_post_func(lambda shared, items, results: ...)
142
+ )
143
+ ```
144
+
145
+ ### Retry + Fallback
146
+
147
+ ```python
148
+ node = (
149
+ new_node()
150
+ .with_max_retries(3)
151
+ .with_wait(1.0)
152
+ .with_exec_func(call_flaky_api)
153
+ .with_exec_fallback_func(lambda prep, err: {"status": "cached"})
154
+ )
155
+ ```
156
+
157
+ ### Async
158
+
159
+ Every primitive has an async counterpart. Phase functions may be sync or
160
+ async, and async flows can contain plain sync nodes — each phase is awaited
161
+ only when it returns a coroutine.
162
+
163
+ ```python
164
+ import asyncio
165
+ from orbyt import AsyncFlow, new_async_node, run_async, Result, SharedStore, DEFAULT_ACTION
166
+
167
+ fetch = (
168
+ new_async_node()
169
+ .with_name("fetch")
170
+ .with_exec_func(async_fetch_users) # a coroutine function
171
+ .with_post_func(lambda s, p, e: s.set("data", e.value) or DEFAULT_ACTION)
172
+ )
173
+
174
+ flow = AsyncFlow(fetch, max_steps=50) # max_steps guards against runaway cycles
175
+ flow.connect(fetch, DEFAULT_ACTION, transform)
176
+
177
+ await flow.run_flow(SharedStore())
178
+ ```
179
+
180
+ `run_async` mirrors `run` (retries sleep via `asyncio.sleep`, same fallback
181
+ behavior). `new_async_batch_node()` runs items concurrently with an
182
+ `asyncio.Semaphore` instead of a thread pool — the right model for I/O-bound
183
+ work. `AsyncFlow.max_steps` turns infinite loops into a loud `RuntimeError`.
184
+
185
+ ### Mermaid Graph Export
186
+
187
+ ```python
188
+ flow = Flow(node_a, name="My Flow")
189
+ flow.connect(node_a, DEFAULT_ACTION, node_b)
190
+ print(flow.to_mermaid())
191
+ ```
192
+
193
+ ## API Reference
194
+
195
+ ### Core Types
196
+
197
+ | Type | Description |
198
+ |---|---|
199
+ | `Node` | Abstract base class - implement `prep`, `exec`, `post` |
200
+ | `BaseNode` | Default implementation with retry/batch config |
201
+ | `CustomNode` | Node with function fields |
202
+ | `NodeBuilder` | Fluent builder from `new_node()` |
203
+ | `Flow` | Directed graph of nodes (also a Node) |
204
+ | `SharedStore` | Thread-safe key-value store |
205
+ | `Result` | Value wrapper with typed accessors |
206
+ | `BatchNode` | Node for processing lists |
207
+ | `WorkerPool` | Fixed-size thread pool |
208
+ | `AsyncNode` | Abstract async node - `async prep`/`exec`/`post` |
209
+ | `AsyncBaseNode` | Default async node with retry/batch config |
210
+ | `AsyncFlow` | Async directed graph (also an `AsyncNode`); `max_steps` loop guard |
211
+ | `AsyncBatchNode` | Async list processing, concurrency via `asyncio.Semaphore` |
212
+
213
+ ### Factory Functions
214
+
215
+ | Function | Description |
216
+ |---|---|
217
+ | `new_node()` | Create a `NodeBuilder` |
218
+ | `new_batch_node()` | Create a `BatchNodeBuilder` |
219
+ | `run(node, shared)` | Execute a node through prep→exec→post |
220
+ | `new_async_node()` | Create an `AsyncNodeBuilder` |
221
+ | `new_async_batch_node()` | Create an `AsyncBatchNodeBuilder` |
222
+ | `run_async(node, shared)` | Await a node through prep→exec→post (sync nodes too) |
223
+ | `to_slice(v)` | Convert any value to `list` |
224
+
225
+ ## License
226
+
227
+ MIT
@@ -0,0 +1,202 @@
1
+ # orbyt
2
+
3
+ A workflow framework for Python. Build AI agents, data pipelines, and orchestration flows with nodes, graphs, and a shared store.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install orbyt
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from orbyt import Flow, SharedStore, Result, new_node, run, DEFAULT_ACTION
15
+
16
+ # Create nodes
17
+ fetch = (
18
+ new_node()
19
+ .with_name("fetch")
20
+ .with_exec_func(lambda r: Result({"users": ["Alice", "Bob", "Charlie"]}))
21
+ .with_post_func(lambda shared, p, e: shared.set("data", e.value) or DEFAULT_ACTION)
22
+ )
23
+
24
+ transform = (
25
+ new_node()
26
+ .with_name("transform")
27
+ .with_prep_func(lambda shared: Result(shared.get_dict("data")))
28
+ .with_exec_func(lambda r: Result([name.upper() for name in r.must_dict()["users"]]))
29
+ .with_post_func(lambda shared, p, e: shared.set("result", e.value) or DEFAULT_ACTION)
30
+ )
31
+
32
+ # Connect into a flow
33
+ flow = Flow(fetch)
34
+ flow.connect(fetch, DEFAULT_ACTION, transform)
35
+
36
+ # Run
37
+ shared = SharedStore()
38
+ flow.run_flow(shared)
39
+ print(shared.get_list("result")) # ['ALICE', 'BOB', 'CHARLIE']
40
+ ```
41
+
42
+ ## Core Concepts
43
+
44
+ ### Nodes
45
+
46
+ Every node runs three phases: **prep** (read inputs) → **exec** (do the work, retryable) → **post** (write results, return an action).
47
+
48
+ ```python
49
+ node = (
50
+ new_node()
51
+ .with_max_retries(3)
52
+ .with_wait(1.0)
53
+ .with_prep_func(lambda shared: Result(shared.get_string("input")))
54
+ .with_exec_func(lambda r: Result(r.must_string().upper()))
55
+ .with_post_func(lambda shared, p, e: shared.set("output", e.value) or DEFAULT_ACTION)
56
+ )
57
+ ```
58
+
59
+ Or subclass for stateful nodes:
60
+
61
+ ```python
62
+ class MyNode(BaseNode):
63
+ def __init__(self):
64
+ super().__init__(max_retries=3, wait=0.5)
65
+
66
+ def prep(self, shared):
67
+ return shared.get_string("input")
68
+
69
+ def exec(self, prep_result):
70
+ return prep_result.upper()
71
+
72
+ def post(self, shared, prep_result, exec_result):
73
+ shared.set("output", exec_result)
74
+ return DEFAULT_ACTION
75
+ ```
76
+
77
+ ### Flows
78
+
79
+ Connect nodes with action-based routing to build directed graphs:
80
+
81
+ ```python
82
+ flow = Flow(start_node)
83
+ flow.connect(start_node, "success", next_node)
84
+ flow.connect(start_node, "error", error_node)
85
+ flow.connect(next_node, DEFAULT_ACTION, final_node)
86
+ flow.run_flow(shared)
87
+ ```
88
+
89
+ Flows implement the Node interface - nest them inside other flows.
90
+
91
+ ### SharedStore
92
+
93
+ Thread-safe key-value store with typed getters:
94
+
95
+ ```python
96
+ shared = SharedStore()
97
+ shared.set("count", 42)
98
+ shared.get_int("count") # 42
99
+ shared.get_int_or("missing", -1) # -1
100
+ shared.bind("user", UserModel) # pydantic or dataclass
101
+ ```
102
+
103
+ ### Batch Processing
104
+
105
+ Process lists of items with configurable concurrency:
106
+
107
+ ```python
108
+ from orbyt import new_batch_node
109
+
110
+ batch = (
111
+ new_batch_node()
112
+ .with_batch_concurrency(5)
113
+ .with_batch_error_handling(True)
114
+ .with_prep_func(lambda shared: [Result(url) for url in shared.get_list("urls")])
115
+ .with_exec_func(lambda r: Result(fetch(r.must_string())))
116
+ .with_post_func(lambda shared, items, results: ...)
117
+ )
118
+ ```
119
+
120
+ ### Retry + Fallback
121
+
122
+ ```python
123
+ node = (
124
+ new_node()
125
+ .with_max_retries(3)
126
+ .with_wait(1.0)
127
+ .with_exec_func(call_flaky_api)
128
+ .with_exec_fallback_func(lambda prep, err: {"status": "cached"})
129
+ )
130
+ ```
131
+
132
+ ### Async
133
+
134
+ Every primitive has an async counterpart. Phase functions may be sync or
135
+ async, and async flows can contain plain sync nodes — each phase is awaited
136
+ only when it returns a coroutine.
137
+
138
+ ```python
139
+ import asyncio
140
+ from orbyt import AsyncFlow, new_async_node, run_async, Result, SharedStore, DEFAULT_ACTION
141
+
142
+ fetch = (
143
+ new_async_node()
144
+ .with_name("fetch")
145
+ .with_exec_func(async_fetch_users) # a coroutine function
146
+ .with_post_func(lambda s, p, e: s.set("data", e.value) or DEFAULT_ACTION)
147
+ )
148
+
149
+ flow = AsyncFlow(fetch, max_steps=50) # max_steps guards against runaway cycles
150
+ flow.connect(fetch, DEFAULT_ACTION, transform)
151
+
152
+ await flow.run_flow(SharedStore())
153
+ ```
154
+
155
+ `run_async` mirrors `run` (retries sleep via `asyncio.sleep`, same fallback
156
+ behavior). `new_async_batch_node()` runs items concurrently with an
157
+ `asyncio.Semaphore` instead of a thread pool — the right model for I/O-bound
158
+ work. `AsyncFlow.max_steps` turns infinite loops into a loud `RuntimeError`.
159
+
160
+ ### Mermaid Graph Export
161
+
162
+ ```python
163
+ flow = Flow(node_a, name="My Flow")
164
+ flow.connect(node_a, DEFAULT_ACTION, node_b)
165
+ print(flow.to_mermaid())
166
+ ```
167
+
168
+ ## API Reference
169
+
170
+ ### Core Types
171
+
172
+ | Type | Description |
173
+ |---|---|
174
+ | `Node` | Abstract base class - implement `prep`, `exec`, `post` |
175
+ | `BaseNode` | Default implementation with retry/batch config |
176
+ | `CustomNode` | Node with function fields |
177
+ | `NodeBuilder` | Fluent builder from `new_node()` |
178
+ | `Flow` | Directed graph of nodes (also a Node) |
179
+ | `SharedStore` | Thread-safe key-value store |
180
+ | `Result` | Value wrapper with typed accessors |
181
+ | `BatchNode` | Node for processing lists |
182
+ | `WorkerPool` | Fixed-size thread pool |
183
+ | `AsyncNode` | Abstract async node - `async prep`/`exec`/`post` |
184
+ | `AsyncBaseNode` | Default async node with retry/batch config |
185
+ | `AsyncFlow` | Async directed graph (also an `AsyncNode`); `max_steps` loop guard |
186
+ | `AsyncBatchNode` | Async list processing, concurrency via `asyncio.Semaphore` |
187
+
188
+ ### Factory Functions
189
+
190
+ | Function | Description |
191
+ |---|---|
192
+ | `new_node()` | Create a `NodeBuilder` |
193
+ | `new_batch_node()` | Create a `BatchNodeBuilder` |
194
+ | `run(node, shared)` | Execute a node through prep→exec→post |
195
+ | `new_async_node()` | Create an `AsyncNodeBuilder` |
196
+ | `new_async_batch_node()` | Create an `AsyncBatchNodeBuilder` |
197
+ | `run_async(node, shared)` | Await a node through prep→exec→post (sync nodes too) |
198
+ | `to_slice(v)` | Convert any value to `list` |
199
+
200
+ ## License
201
+
202
+ MIT
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "open_orbyt"
7
+ version = "0.1.0"
8
+ description = "A workflow framework for Python. Build AI agents, data pipelines, and orchestration flows with nodes, graphs, and a shared store."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.11"
12
+ authors = [
13
+ { name = "Maverick Weyunga", email = "maverickweyunga@gmail.com" },
14
+ ]
15
+ keywords = ["workflow", "pipeline", "dag", "flow", "orchestration", "agent", "ai", "graph"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ "pydantic>=2.0",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/erickweyunga/orbyt"
34
+ Repository = "https://github.com/erickweyunga/orbyt"
35
+ Documentation = "https://github.com/erickweyunga/orbyt#readme"
36
+ Issues = "https://github.com/erickweyunga/orbyt/issues"
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["src/orbyt"]
40
+
41
+ [tool.pytest.ini_options]
42
+ testpaths = ["tests"]
@@ -0,0 +1,76 @@
1
+ """orbyt - A workflow framework for Python.
2
+
3
+ Build AI agents, data pipelines, and orchestration flows
4
+ with nodes, graphs, and a shared store.
5
+ """
6
+
7
+ from ._async_batch import (
8
+ AsyncBatchNode,
9
+ AsyncBatchNodeBuilder,
10
+ new_async_batch_node,
11
+ run_batch_async,
12
+ )
13
+ from ._async_flow import AsyncFlow
14
+ from ._async_node import (
15
+ AsyncBaseNode,
16
+ AsyncCustomNode,
17
+ AsyncNode,
18
+ AsyncNodeBuilder,
19
+ new_async_node,
20
+ run_async,
21
+ )
22
+ from ._batch import BatchError, BatchNode, BatchNodeBuilder, new_batch_node
23
+ from ._flow import Flow
24
+ from ._node import (
25
+ DEFAULT_ACTION,
26
+ Action,
27
+ BaseNode,
28
+ CustomNode,
29
+ FallbackNode,
30
+ Node,
31
+ NodeBuilder,
32
+ NodeConfig,
33
+ RetryableNode,
34
+ new_node,
35
+ run,
36
+ )
37
+ from ._pool import WorkerPool
38
+ from ._result import Result
39
+ from ._store import SharedStore
40
+ from ._utils import to_slice
41
+
42
+ __all__ = [
43
+ "Action",
44
+ "AsyncBaseNode",
45
+ "AsyncBatchNode",
46
+ "AsyncBatchNodeBuilder",
47
+ "AsyncCustomNode",
48
+ "AsyncFlow",
49
+ "AsyncNode",
50
+ "AsyncNodeBuilder",
51
+ "BaseNode",
52
+ "BatchError",
53
+ "BatchNode",
54
+ "BatchNodeBuilder",
55
+ "CustomNode",
56
+ "DEFAULT_ACTION",
57
+ "FallbackNode",
58
+ "Flow",
59
+ "Node",
60
+ "NodeBuilder",
61
+ "NodeConfig",
62
+ "Result",
63
+ "RetryableNode",
64
+ "SharedStore",
65
+ "WorkerPool",
66
+ "new_async_batch_node",
67
+ "new_async_node",
68
+ "new_batch_node",
69
+ "new_node",
70
+ "run",
71
+ "run_async",
72
+ "run_batch_async",
73
+ "to_slice",
74
+ ]
75
+
76
+ __version__ = "0.1.0"