loopgraph 0.2.0__py3-none-any.whl

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,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: loopgraph
3
+ Version: 0.2.0
4
+ Summary: Event-driven graph workflow engine with native loop support.
5
+ Author: LoopGraph Team
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Provides-Extra: test
10
+ Requires-Dist: pytest>=7.4.0; extra == "test"
11
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
12
+ Provides-Extra: lint
13
+ Requires-Dist: ruff>=0.6.4; extra == "lint"
14
+ Requires-Dist: mypy>=1.11.0; extra == "lint"
15
+
16
+ # LoopGraph
17
+
18
+ **The agent workflow engine that treats loops as a first-class citizen.**
19
+
20
+ Build graph-based AI agent workflows where cycles, re-entry, and iterative reasoning are native — not hacks. More intuitive and lightweight than LangGraph, with loops as a first-class primitive.
21
+
22
+ - **Zero dependencies** — pure Python 3.10+, nothing to install beyond your own agents
23
+ - **Native loop support** — cycles in your graph are validated, tracked, and safe by design
24
+ - **Event-driven** — every state transition emits events; hook in logging, metrics, or triggers anywhere
25
+ - **Async-first** — built on `asyncio`, handles concurrent nodes without blocking
26
+ - **Recoverable** — snapshot and replay any run from any point
27
+
28
+ ```bash
29
+ pip install loopgraph
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Quickstart
35
+
36
+ ```python
37
+ import asyncio
38
+ from loopgraph.core.graph import Graph, Node, Edge, NodeKind
39
+ from loopgraph.bus.eventbus import EventBus
40
+ from loopgraph.registry.function_registry import FunctionRegistry
41
+ from loopgraph.scheduler.scheduler import Scheduler
42
+
43
+ async def my_agent(payload):
44
+ # your agent logic here
45
+ return {"result": "done", "loop_again": False}
46
+
47
+ async def router(payload):
48
+ # return the next node id
49
+ return "end" if not payload.get("loop_again") else "agent"
50
+
51
+ graph = Graph(
52
+ nodes=[
53
+ Node(id="agent", kind=NodeKind.TASK),
54
+ Node(id="router", kind=NodeKind.SWITCH),
55
+ Node(id="end", kind=NodeKind.TASK),
56
+ ],
57
+ edges=[
58
+ Edge(source="agent", target="router"),
59
+ Edge(source="router", target="agent"), # the loop back-edge
60
+ Edge(source="router", target="end"),
61
+ ],
62
+ entry="agent",
63
+ )
64
+
65
+ registry = FunctionRegistry()
66
+ registry.register("agent", my_agent)
67
+ registry.register("router", router)
68
+ registry.register("end", lambda p: p)
69
+
70
+ bus = EventBus()
71
+ scheduler = Scheduler(graph=graph, registry=registry, bus=bus)
72
+
73
+ asyncio.run(scheduler.run(payload={"input": "hello"}))
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Why LoopGraph?
79
+
80
+ Most workflow engines assume a DAG — a graph with no cycles. That works for linear pipelines, but agent workflows are inherently iterative: an agent reasons, reflects, decides to try again, and loops back. Forcing that into a DAG requires awkward workarounds.
81
+
82
+ LoopGraph makes loops explicit and safe:
83
+
84
+ - **Back-edges are first-class** — declare a cycle in your graph and the engine handles reset, visit tracking, and state management automatically
85
+ - **Loop safety** — the engine validates your graph at construction time; overlapping loops that share nodes are rejected before anything runs
86
+ - **Full observability** — every loop iteration emits events (`NODE_SCHEDULED`, `NODE_COMPLETED`, `NODE_FAILED`) so you always know where you are
87
+
88
+ ---
89
+
90
+ ## Event Hooks
91
+
92
+ Subscribe to any workflow event to add logging, metrics, or side effects:
93
+
94
+ ```python
95
+ from loopgraph.core.types import EventType
96
+
97
+ async def on_completed(event):
98
+ print(f"{event.node_id} finished → {event.payload}")
99
+
100
+ bus.subscribe(EventType.NODE_COMPLETED, on_completed)
101
+ ```
102
+
103
+ Available events: `NODE_SCHEDULED`, `NODE_STARTED`, `NODE_COMPLETED`, `NODE_FAILED`.
104
+
105
+ ---
106
+
107
+ ## Custom Events from Handlers
108
+
109
+ Wrap your handler in a closure to emit custom events mid-execution:
110
+
111
+ ```python
112
+ def make_handler(bus, base_handler):
113
+ async def wrapper(payload):
114
+ await bus.emit(Event(id="pre", graph_id="g", node_id="n",
115
+ type=EventType.NODE_SCHEDULED, payload={"stage": "pre"}))
116
+ result = await base_handler(payload)
117
+ await bus.emit(Event(id="post", graph_id="g", node_id="n",
118
+ type=EventType.NODE_COMPLETED, payload={"stage": "post"}))
119
+ return result
120
+ return wrapper
121
+
122
+ registry.register("my_node", make_handler(bus, my_agent))
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Loop Re-entry Rules
128
+
129
+ - Re-entry is triggered by a `SWITCH` node selecting a back-edge
130
+ - Only `COMPLETED` nodes can be reset for re-entry
131
+ - Reset clears upstream-completion tracking and preserves cumulative `visit_count`
132
+ - Overlapping loops sharing any node are rejected at graph construction time
133
+
134
+ ---
135
+
136
+ ## Installation
137
+
138
+ ```bash
139
+ pip install loopgraph
140
+ ```
141
+
142
+ Requires Python 3.10+. No runtime dependencies.
143
+
144
+ ---
145
+
146
+ ## Development
147
+
148
+ ```bash
149
+ git clone https://github.com/your-org/loopgraph
150
+ cd loopgraph
151
+ python3 -m venv .venv && source .venv/bin/activate
152
+ pip install -e ".[test,lint]"
153
+ pytest
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Design Principles
159
+
160
+ - **Keep the core compact.** Nodes stay stateless and the scheduler stays simple, with minimum opinionated design and maximum freedom for users to compose their own workflow patterns. Handlers capture their own context (event bus, metrics, side effects) so the framework never grows special cases for custom behaviour.
161
+ - **Push heavy lifting to the edge.** Long-running work should run via remote APIs, threads, or separate nodes/clusters. We avoid building a distributed fan-out scheduler; users orchestrate their own parallelism while the engine focuses on deterministic single-node execution.
162
+ - **Flexible aggregation semantics.** Aggregator nodes may proceed when only a subset of upstream nodes finish — as long as those nodes reach a terminal state. Fail-fast and error-tolerance are user-level workflow patterns, and the engine stays policy-light so users can implement either.
163
+ - **Retries live with handlers.** The framework doesn't implement automatic retries. Each handler decides whether to retry, abort, or compensate, keeping recovery logic close to the business code.
164
+ - **Pluggable concurrency.** A shared ConcurrencyManager (semaphore or priority-aware) controls global slots. Multiple schedulers can share one manager, but there's no hidden magic — users choose the policy, preserving clarity and control.
165
+ - **Recovery through snapshots.** The engine snapshots execution state and event logs so users can resume or replay runs without re-executing nodes. Payloads flow naturally between nodes, satisfying replay needs without extra APIs.
@@ -0,0 +1,24 @@
1
+ loopgraph/__init__.py,sha256=hfsbTvpGMIagxsEkuPrhIjEN_LQ1MSWSmemZ7lVVP-I,1007
2
+ loopgraph/_debug.py,sha256=916xk9zxdqQo9UElyz1YbyYxqdWSW8HK6Ch8oBqUiQI,1280
3
+ loopgraph/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ loopgraph/bus/__init__.py,sha256=SGmZyIdrjgBOXjzo8thuM8wbcYn2GZFaavoAPMesIco,167
5
+ loopgraph/bus/eventbus.py,sha256=stKB0codg14F8dOALFdtoDg2Xi_vHOvLUcmbVAM_Kac,6576
6
+ loopgraph/concurrency/__init__.py,sha256=mv8MZ5RpVMpMvhOBKuVnp9RsbmsbuYKHzg2Ogjn8hFg,202
7
+ loopgraph/concurrency/policies.py,sha256=VtOoCyWnFYrTEBrfYxkrAd53sZo8NHF7bktz9HfbzmU,6710
8
+ loopgraph/core/__init__.py,sha256=lZh69PG91I7HbZXboz6lfpIGLGal4j5R1OuimQZXh70,392
9
+ loopgraph/core/graph.py,sha256=__dbQDw4zPILlmaEjB9VKd_F9854WZw3Zg1jUUC682I,18003
10
+ loopgraph/core/state.py,sha256=xwiFdQe25MAGp_XqDSv69ohm20I8r2zCPij0GY7x4Pc,17687
11
+ loopgraph/core/types.py,sha256=qQMdIPVqvyQcQA5RpYQ98b4wjd8qQ2iD9rU5ymH9XU8,2005
12
+ loopgraph/diagnostics/__init__.py,sha256=E3lCV2g0pNzoMga52bmZYVzwmxwUdPp9Sm9k-ssadKg,148
13
+ loopgraph/diagnostics/inspect.py,sha256=b726Mqk4xi3rPLQoEsaD_Ss1mvCaLfSOfzS4LMNB1j0,2532
14
+ loopgraph/persistence/__init__.py,sha256=oKUSFK-yWJ9S3x4QpFT100LGfwVOzfNX8lgvq6_oPbI,255
15
+ loopgraph/persistence/event_log.py,sha256=MWAvZMvqcP_cNo6mtvZRQtlyhYMEzibhdf0Cc1-DuBE,1983
16
+ loopgraph/persistence/snapshot.py,sha256=wK10SjyEpia3j1WYFf0mfXHHFo5epaVN0cRAVgQo1iY,1802
17
+ loopgraph/registry/__init__.py,sha256=Ou_Ji9iYBkg6CAK6KFU7NLDgKi6BNJT7UztwUZ00kEc,36
18
+ loopgraph/registry/function_registry.py,sha256=WSBpk3SJG7QmeoudKI7NSR1Htc3oCqCTQzjrbHJ-4aw,3760
19
+ loopgraph/scheduler/__init__.py,sha256=Y7DD-fSnWqGyjAojoNQqoT1CWEeV6DkVlSEvG5NVZOU,87
20
+ loopgraph/scheduler/scheduler.py,sha256=EyBHQopWyvzGGbMccigDXtX3Q5WH9iak3coMwhOOViI,24801
21
+ loopgraph-0.2.0.dist-info/METADATA,sha256=3lQSXTizoQ8BOrBG8KJXFSjuvN8to3ub8vgRHrqVe5g,6355
22
+ loopgraph-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
23
+ loopgraph-0.2.0.dist-info/top_level.txt,sha256=bTwyu1IHllBA8-cY8U9uCwndzhBMtR1HgxJgfruAmG4,10
24
+ loopgraph-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ loopgraph