abstractflow 0.3.0__py3-none-any.whl → 0.3.1__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,290 @@
1
+ """WorkflowBundle (.flow) tooling (authoring-side).
2
+
3
+ This module remains for backwards compatibility, but bundling logic lives in
4
+ `abstractruntime.workflow_bundle` so hosts and clients share the same semantics.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import zipfile
11
+ from dataclasses import dataclass
12
+ from datetime import datetime, timezone
13
+ from pathlib import Path
14
+ from typing import Any, Dict, Iterable, List, Optional, Tuple
15
+
16
+ from abstractruntime.workflow_bundle import (
17
+ WORKFLOW_BUNDLE_FORMAT_VERSION_V1,
18
+ WorkflowBundleEntrypoint,
19
+ WorkflowBundleManifest,
20
+ WorkflowBundleError,
21
+ open_workflow_bundle,
22
+ workflow_bundle_manifest_to_dict,
23
+ )
24
+
25
+ from .visual.models import NodeType, VisualFlow
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class PackedWorkflowBundle:
30
+ path: Path
31
+ manifest: WorkflowBundleManifest
32
+
33
+
34
+ def _now_iso() -> str:
35
+ return datetime.now(timezone.utc).isoformat()
36
+
37
+
38
+ def _read_json_bytes(path: Path) -> bytes:
39
+ return path.read_bytes()
40
+
41
+
42
+ def _load_visualflow_from_bytes(raw: bytes) -> VisualFlow:
43
+ data = json.loads(raw.decode("utf-8"))
44
+ return VisualFlow.model_validate(data)
45
+
46
+
47
+ def _node_type_str(node: Any) -> str:
48
+ t = getattr(node, "type", None)
49
+ return t.value if hasattr(t, "value") else str(t or "")
50
+
51
+
52
+ def _reachable_exec_node_ids(flow: VisualFlow) -> set[str]:
53
+ """Return exec-reachable node ids (Blueprint-style; ignores disconnected exec nodes)."""
54
+ exec_ids: set[str] = set()
55
+ for n in flow.nodes:
56
+ data = dict(n.data) if isinstance(n.data, dict) else {}
57
+ pins = data.get("inputs") if isinstance(data.get("inputs"), list) else []
58
+ pins2 = data.get("outputs") if isinstance(data.get("outputs"), list) else []
59
+ for p in list(pins) + list(pins2):
60
+ if isinstance(p, dict) and p.get("type") == "execution":
61
+ exec_ids.add(str(n.id))
62
+ break
63
+
64
+ if not exec_ids:
65
+ return set()
66
+
67
+ incoming_exec = {e.target for e in flow.edges if getattr(e, "targetHandle", None) == "exec-in"}
68
+
69
+ roots: list[str] = []
70
+ if isinstance(flow.entryNode, str) and flow.entryNode in exec_ids:
71
+ roots.append(flow.entryNode)
72
+ for n in flow.nodes:
73
+ if _node_type_str(n) == str(NodeType.ON_EVENT.value) and n.id in exec_ids:
74
+ roots.append(n.id)
75
+ if not roots:
76
+ for n in flow.nodes:
77
+ if n.id in exec_ids and n.id not in incoming_exec:
78
+ roots.append(n.id)
79
+ break
80
+ if not roots:
81
+ roots.append(next(iter(exec_ids)))
82
+
83
+ adj: Dict[str, list[str]] = {}
84
+ for e in flow.edges:
85
+ if getattr(e, "targetHandle", None) != "exec-in":
86
+ continue
87
+ if e.source not in exec_ids or e.target not in exec_ids:
88
+ continue
89
+ adj.setdefault(e.source, []).append(e.target)
90
+
91
+ reachable: set[str] = set()
92
+ stack = list(dict.fromkeys([r for r in roots if isinstance(r, str) and r]))
93
+ while stack:
94
+ cur = stack.pop()
95
+ if cur in reachable:
96
+ continue
97
+ reachable.add(cur)
98
+ for nxt in adj.get(cur, []):
99
+ if nxt not in reachable:
100
+ stack.append(nxt)
101
+ return reachable
102
+
103
+
104
+ def _collect_reachable_flows(
105
+ *, root_flow: VisualFlow, root_bytes: bytes, flows_dir: Path
106
+ ) -> Tuple[List[Tuple[str, VisualFlow, bytes]], List[str]]:
107
+ """Return [(flow_id, flow, raw_bytes)] in discovery order + list of missing subflow ids."""
108
+ ordered: list[Tuple[str, VisualFlow, bytes]] = []
109
+ visited: set[str] = set()
110
+ missing: list[str] = []
111
+
112
+ # Memoize loaded files by id for reuse.
113
+ cache: Dict[str, Tuple[VisualFlow, bytes]] = {str(root_flow.id): (root_flow, root_bytes)}
114
+
115
+ def _load_by_id(flow_id: str) -> Optional[Tuple[VisualFlow, bytes]]:
116
+ fid = str(flow_id or "").strip()
117
+ if not fid:
118
+ return None
119
+ if fid in cache:
120
+ return cache[fid]
121
+ p = (flows_dir / f"{fid}.json").resolve()
122
+ if not p.exists():
123
+ return None
124
+ raw = _read_json_bytes(p)
125
+ vf = _load_visualflow_from_bytes(raw)
126
+ cache[fid] = (vf, raw)
127
+ return cache[fid]
128
+
129
+ def _dfs(vf: VisualFlow, raw: bytes) -> None:
130
+ fid = str(vf.id)
131
+ if fid in visited:
132
+ return
133
+ visited.add(fid)
134
+ ordered.append((fid, vf, raw))
135
+
136
+ reachable = _reachable_exec_node_ids(vf)
137
+ for n in vf.nodes:
138
+ if _node_type_str(n) != str(NodeType.SUBFLOW.value):
139
+ continue
140
+ if reachable and n.id not in reachable:
141
+ continue
142
+ data = n.data if isinstance(n.data, dict) else {}
143
+ sub_id = data.get("subflowId") or data.get("flowId")
144
+ if not isinstance(sub_id, str) or not sub_id.strip():
145
+ # Match VisualFlow executor behavior: fail if a reachable subflow is malformed.
146
+ missing.append(f"<missing-subflow-id:{fid}:{n.id}>")
147
+ continue
148
+ sub_id = sub_id.strip()
149
+ child = _load_by_id(sub_id)
150
+ if child is None:
151
+ # Self-recursion is valid even if the file isn't duplicated on disk.
152
+ if sub_id == fid:
153
+ _dfs(vf, raw)
154
+ continue
155
+ missing.append(sub_id)
156
+ continue
157
+ _dfs(child[0], child[1])
158
+
159
+ _dfs(root_flow, root_bytes)
160
+ return ordered, missing
161
+
162
+
163
+ def pack_workflow_bundle(
164
+ *,
165
+ root_flow_json: str | Path,
166
+ out_path: str | Path,
167
+ bundle_id: Optional[str] = None,
168
+ bundle_version: str = "0.0.0",
169
+ flows_dir: Optional[str | Path] = None,
170
+ entrypoints: Optional[List[str]] = None,
171
+ default_entrypoint: Optional[str] = None,
172
+ metadata: Optional[Dict[str, Any]] = None,
173
+ ) -> PackedWorkflowBundle:
174
+ """Pack a `.flow` bundle from a root VisualFlow JSON file."""
175
+ root_path = Path(root_flow_json).expanduser().resolve()
176
+ if not root_path.exists():
177
+ raise FileNotFoundError(f"root flow not found: {root_path}")
178
+ root_bytes = _read_json_bytes(root_path)
179
+ root_flow = _load_visualflow_from_bytes(root_bytes)
180
+
181
+ flows_base = Path(flows_dir).expanduser().resolve() if flows_dir is not None else root_path.parent
182
+ if not flows_base.exists() or not flows_base.is_dir():
183
+ raise FileNotFoundError(f"flows_dir does not exist: {flows_base}")
184
+
185
+ ordered, missing = _collect_reachable_flows(root_flow=root_flow, root_bytes=root_bytes, flows_dir=flows_base)
186
+ if missing:
187
+ uniq = sorted(set(missing))
188
+ raise WorkflowBundleError(f"Missing referenced subflows in flows_dir: {uniq}")
189
+
190
+ # Entry points: default to root flow id.
191
+ entry_ids = list(entrypoints) if isinstance(entrypoints, list) and entrypoints else [str(root_flow.id)]
192
+ entry_ids = [str(x).strip() for x in entry_ids if isinstance(x, str) and str(x).strip()]
193
+ if not entry_ids:
194
+ raise WorkflowBundleError("No valid entrypoints specified")
195
+
196
+ de_param = str(default_entrypoint).strip() if isinstance(default_entrypoint, str) and str(default_entrypoint).strip() else ""
197
+ if de_param and de_param not in entry_ids:
198
+ raise WorkflowBundleError(f"default_entrypoint '{de_param}' must be one of: {entry_ids}")
199
+ default_ep = de_param or (str(root_flow.id).strip() if str(root_flow.id).strip() in entry_ids else entry_ids[0])
200
+
201
+ # Compile artifacts for all included flows.
202
+ flows_json: Dict[str, bytes] = {}
203
+ interfaces_by_flow: Dict[str, list[str]] = {}
204
+ name_by_flow: Dict[str, str] = {}
205
+ desc_by_flow: Dict[str, str] = {}
206
+
207
+ for fid, vf, raw in ordered:
208
+ flows_json[fid] = raw
209
+ name_by_flow[fid] = str(getattr(vf, "name", "") or "")
210
+ desc_by_flow[fid] = str(getattr(vf, "description", "") or "")
211
+ interfaces_by_flow[fid] = list(getattr(vf, "interfaces", []) or [])
212
+
213
+ bid = str(bundle_id or "").strip() or str(root_flow.id)
214
+ created_at = _now_iso()
215
+
216
+ eps: list[WorkflowBundleEntrypoint] = []
217
+ for fid in entry_ids:
218
+ fid2 = str(fid or "").strip()
219
+ if not fid2:
220
+ continue
221
+ eps.append(
222
+ WorkflowBundleEntrypoint(
223
+ flow_id=fid2,
224
+ name=name_by_flow.get(fid2) or fid2,
225
+ description=desc_by_flow.get(fid2, ""),
226
+ interfaces=list(interfaces_by_flow.get(fid2, [])),
227
+ )
228
+ )
229
+ if not eps:
230
+ raise WorkflowBundleError("No valid entrypoints specified")
231
+
232
+ manifest = WorkflowBundleManifest(
233
+ bundle_format_version=WORKFLOW_BUNDLE_FORMAT_VERSION_V1,
234
+ bundle_id=bid,
235
+ bundle_version=str(bundle_version or "0.0.0"),
236
+ created_at=created_at,
237
+ entrypoints=eps,
238
+ default_entrypoint=default_ep,
239
+ flows={fid: f"flows/{fid}.json" for fid in sorted(flows_json.keys())},
240
+ artifacts={},
241
+ assets={},
242
+ metadata=dict(metadata) if isinstance(metadata, dict) else {},
243
+ )
244
+ manifest.validate()
245
+
246
+ out = Path(out_path).expanduser().resolve()
247
+ out.parent.mkdir(parents=True, exist_ok=True)
248
+
249
+ # Deterministic write order for reproducibility.
250
+ with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED) as zf:
251
+ zf.writestr("manifest.json", json.dumps(workflow_bundle_manifest_to_dict(manifest), indent=2, ensure_ascii=False))
252
+ for fid in sorted(flows_json.keys()):
253
+ zf.writestr(f"flows/{fid}.json", flows_json[fid])
254
+
255
+ return PackedWorkflowBundle(path=out, manifest=manifest)
256
+
257
+
258
+ def inspect_workflow_bundle(*, bundle_path: str | Path) -> WorkflowBundleManifest:
259
+ b = open_workflow_bundle(bundle_path)
260
+ return b.manifest
261
+
262
+
263
+ def unpack_workflow_bundle(*, bundle_path: str | Path, out_dir: str | Path) -> Path:
264
+ src = Path(bundle_path).expanduser().resolve()
265
+ out = Path(out_dir).expanduser().resolve()
266
+ out.mkdir(parents=True, exist_ok=True)
267
+
268
+ if src.is_dir():
269
+ # Directory bundle: copy files (best-effort).
270
+ for p in src.rglob("*"):
271
+ if p.is_dir():
272
+ continue
273
+ rel = p.relative_to(src)
274
+ dst = out / rel
275
+ dst.parent.mkdir(parents=True, exist_ok=True)
276
+ dst.write_bytes(p.read_bytes())
277
+ return out
278
+
279
+ with zipfile.ZipFile(src, "r") as zf:
280
+ zf.extractall(out)
281
+ return out
282
+
283
+
284
+ # Backwards-compat: prefer the shared stdlib-only implementation in AbstractRuntime.
285
+ from abstractruntime.workflow_bundle import ( # noqa: E402
286
+ PackedWorkflowBundle as PackedWorkflowBundle,
287
+ inspect_workflow_bundle as inspect_workflow_bundle,
288
+ pack_workflow_bundle as pack_workflow_bundle,
289
+ unpack_workflow_bundle as unpack_workflow_bundle,
290
+ )
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: abstractflow
3
+ Version: 0.3.1
4
+ Summary: Diagram-based AI workflow generation built on AbstractCore
5
+ Author-email: AbstractFlow Team <contact@abstractflow.ai>
6
+ Maintainer-email: AbstractFlow Team <contact@abstractflow.ai>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/lpalbou/AbstractFlow
9
+ Project-URL: Documentation, https://abstractflow.readthedocs.io
10
+ Project-URL: Repository, https://github.com/lpalbou/AbstractFlow
11
+ Project-URL: Bug Tracker, https://github.com/lpalbou/AbstractFlow/issues
12
+ Project-URL: Changelog, https://github.com/lpalbou/AbstractFlow/blob/main/CHANGELOG.md
13
+ Keywords: ai,workflow,diagram,llm,automation,visual-programming,abstractcore,machine-learning
14
+ Classifier: Development Status :: 2 - Pre-Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Distributed Computing
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: AbstractRuntime>=0.4.0
29
+ Requires-Dist: abstractcore[tools]>=2.6.8
30
+ Requires-Dist: pydantic>=2.0.0
31
+ Requires-Dist: typing-extensions>=4.0.0
32
+ Provides-Extra: agent
33
+ Requires-Dist: abstractagent>=0.2.0; extra == "agent"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
36
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
37
+ Requires-Dist: black>=23.0.0; extra == "dev"
38
+ Requires-Dist: isort>=5.12.0; extra == "dev"
39
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
40
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
41
+ Requires-Dist: pre-commit>=3.0.0; extra == "dev"
42
+ Provides-Extra: server
43
+ Requires-Dist: fastapi>=0.100.0; extra == "server"
44
+ Requires-Dist: uvicorn[standard]>=0.23.0; extra == "server"
45
+ Requires-Dist: websockets>=11.0.0; extra == "server"
46
+ Provides-Extra: ui
47
+ Requires-Dist: streamlit>=1.28.0; extra == "ui"
48
+ Requires-Dist: plotly>=5.15.0; extra == "ui"
49
+ Requires-Dist: networkx>=3.1.0; extra == "ui"
50
+ Provides-Extra: all
51
+ Requires-Dist: abstractflow[agent,dev,server,ui]; extra == "all"
52
+ Dynamic: license-file
53
+
54
+ # AbstractFlow
55
+
56
+ Diagram-based, **durable** AI workflows for Python.
57
+
58
+ AbstractFlow provides:
59
+ - A portable workflow format (`VisualFlow` JSON) and helpers to execute it from any host (`abstractflow.visual`).
60
+ - A simple programmatic API (`Flow`, `FlowRunner`) backed by **AbstractRuntime**.
61
+ - A reference visual editor app in `web/` (FastAPI backend + React frontend).
62
+
63
+ Project status: **Pre-alpha** (`pyproject.toml`: `Development Status :: 2 - Pre-Alpha`). Expect breaking changes.
64
+
65
+ ## Capabilities (implemented)
66
+
67
+ - Execute programmatic flows (`Flow` → `FlowRunner`) with a default in-memory runtime.
68
+ - Execute portable `VisualFlow` JSON from any host process (`abstractflow.visual`).
69
+ - Durable waits and resumption via AbstractRuntime (e.g. user/event/schedule waits).
70
+ - Package a flow tree as a WorkflowBundle (`.flow`) via the CLI.
71
+ - Author/run VisualFlows in the reference web editor (`web/`).
72
+
73
+ Evidence (code): `abstractflow/runner.py`, `abstractflow/visual/executor.py`, `abstractflow/cli.py`, `web/backend/routes/ws.py`.
74
+
75
+ ## Docs
76
+
77
+ - Start here: `docs/getting-started.md`
78
+ - API reference: `docs/api.md`
79
+ - VisualFlow format: `docs/visualflow.md`
80
+ - Visual editor: `docs/web-editor.md`
81
+ - CLI: `docs/cli.md`
82
+ - FAQ: `docs/faq.md`
83
+ - Architecture: `docs/architecture.md`
84
+ - Docs index: `docs/README.md`
85
+
86
+ ## Installation
87
+
88
+ ```bash
89
+ pip install abstractflow
90
+ ```
91
+
92
+ Requirements: Python **3.10+** (`pyproject.toml`: `requires-python`).
93
+
94
+ Optional extras:
95
+ - Agent nodes (ReAct workflows): `pip install "abstractflow[agent]"`
96
+ - Dev tools (tests/formatting): `pip install "abstractflow[dev]"`
97
+
98
+ Notes:
99
+ - `abstractflow` depends on `AbstractRuntime` and `abstractcore[tools]` (see `pyproject.toml`).
100
+ - Some VisualFlow node types require additional packages (e.g. `memory_kg_*` nodes need `abstractmemory`).
101
+
102
+ ## Quickstart (programmatic)
103
+
104
+ ```python
105
+ from abstractflow import Flow, FlowRunner
106
+
107
+ flow = Flow("linear")
108
+ flow.add_node("double", lambda x: x * 2, input_key="value", output_key="doubled")
109
+ flow.add_node("add_ten", lambda x: x + 10, input_key="doubled", output_key="final")
110
+ flow.add_edge("double", "add_ten")
111
+ flow.set_entry("double")
112
+
113
+ result = FlowRunner(flow).run({"value": 5})
114
+ print(result) # {"success": True, "result": 20}
115
+ ```
116
+
117
+ ## Quickstart (execute a VisualFlow JSON)
118
+
119
+ ```python
120
+ import json
121
+ from abstractflow.visual import VisualFlow, execute_visual_flow
122
+
123
+ with open("my-flow.json", "r", encoding="utf-8") as f:
124
+ vf = VisualFlow.model_validate(json.load(f))
125
+ result = execute_visual_flow(vf, {"prompt": "Hello"}, flows={vf.id: vf})
126
+ print(result) # {"success": True, "waiting": False, "result": ...}
127
+ ```
128
+
129
+ If your flow uses subflows, load all referenced `*.json` into the `flows={...}` mapping (see `docs/getting-started.md`).
130
+
131
+ ## Visual editor (from source)
132
+
133
+ The visual editor is a dev/reference app in `web/` (not shipped as a Python package on PyPI).
134
+
135
+ ```bash
136
+ git clone https://github.com/lpalbou/AbstractFlow.git
137
+ cd AbstractFlow
138
+
139
+ python -m venv .venv
140
+ source .venv/bin/activate
141
+ pip install -e ".[server,agent]"
142
+
143
+ # Terminal 1: Backend (FastAPI)
144
+ cd web && python -m backend --reload --port 8080
145
+
146
+ # Terminal 2: Frontend (Vite)
147
+ cd web/frontend && npm install && npm run dev
148
+ ```
149
+
150
+ Open the frontend at http://localhost:3003 (default Vite port). See `docs/web-editor.md`.
151
+
152
+ ## CLI (WorkflowBundle `.flow`)
153
+
154
+ ```bash
155
+ abstractflow bundle pack web/flows/ac-echo.json --out /tmp/ac-echo.flow
156
+ abstractflow bundle inspect /tmp/ac-echo.flow
157
+ abstractflow bundle unpack /tmp/ac-echo.flow --dir /tmp/ac-echo
158
+ ```
159
+
160
+ See `docs/cli.md` and `abstractflow/cli.py`.
161
+
162
+ ## Related projects
163
+
164
+ - AbstractRuntime (durable execution kernel): https://github.com/lpalbou/AbstractRuntime
165
+ - AbstractCore (providers/models/tools): https://github.com/lpalbou/AbstractCore
166
+ - AbstractAgent (ReAct/CodeAct): https://github.com/lpalbou/AbstractAgent
167
+
168
+ ## Changelog
169
+
170
+ See `CHANGELOG.md`.
171
+
172
+ ## Contributing
173
+
174
+ See `CONTRIBUTING.md`.
175
+
176
+ ## Security
177
+
178
+ See `SECURITY.md`.
179
+
180
+ ## Acknowledgments
181
+
182
+ See `ACKNOWLEDMENTS.md`.
183
+
184
+ ## License
185
+
186
+ MIT. See `LICENSE`.
@@ -0,0 +1,33 @@
1
+ abstractflow/__init__.py,sha256=0c08cMHJC5c-tAjh9up90y7cG-SUa2rTf3Nhf2w718o,2586
2
+ abstractflow/__main__.py,sha256=nl-6C1KVPCcPfrIM67zYnyotsKI8XaNpIsNJ1EZ6S5w,175
3
+ abstractflow/cli.py,sha256=oU-XeqXuYkJxTBVkm-sHksYeYYy658owE9WwRamGAoI,3240
4
+ abstractflow/compiler.py,sha256=E71lsuqZWYlpRh4iPRGs2ahZVyjvwNHlOepgHaTKTxA,634
5
+ abstractflow/py.typed,sha256=QjzTvd0siCmvIPhJL7u61TKvbkV9Ba_WXrDMoyIZ3Fc,76
6
+ abstractflow/runner.py,sha256=97Aepi3NUtygp8NbjLrv-6HZPCRciAytKEix5ZK4QtI,16173
7
+ abstractflow/workflow_bundle.py,sha256=Adv3yT-odPn6bLN4OovJ6OuT8De1JBbxqxVsAt_7uCI,10596
8
+ abstractflow/adapters/__init__.py,sha256=1My7MaQqrg9DRGNR3fq0PSGfs06ZpzuwHJnj7LRqX9A,360
9
+ abstractflow/adapters/agent_adapter.py,sha256=3MsCKK22RuIDEjko5w-FyhdsnA3OuzRf5curyszXUbY,191
10
+ abstractflow/adapters/control_adapter.py,sha256=CZdW1incL-y7Ef6DKlorHbNyucGMpP_0KqpkUeCboOY,193
11
+ abstractflow/adapters/effect_adapter.py,sha256=X0cc5WREn1aU0MLbdSPQHodfEQDvV_3t4KJXC0wfeKE,192
12
+ abstractflow/adapters/event_adapter.py,sha256=NzupBnN_jNQ1moGbQ4NEBw_8L1-a834gjBbMJ2NnHfI,191
13
+ abstractflow/adapters/function_adapter.py,sha256=7EfM22zr0MlVVugaUPmJcoOpixgB_Xm36_J5enH-X4s,194
14
+ abstractflow/adapters/subflow_adapter.py,sha256=R6YI3ZUd1OX3ttzjjh36QS47rUySC1MSA74Wrp3H3WY,193
15
+ abstractflow/adapters/variable_adapter.py,sha256=wa0zPYN2M45iKbVE_W1iW5MtXY2iHCdSqLPhqOq7Rpw,194
16
+ abstractflow/core/__init__.py,sha256=1Qo2qFHgi49nTwGYQaLv9ywwSR6kwRNQQoZFjqXzPmU,120
17
+ abstractflow/core/flow.py,sha256=nQW-KDN7iNbKpAY4ONmB_ZPbMK3gWIVgxJDGwcTf7Ds,335
18
+ abstractflow/visual/__init__.py,sha256=R8pahi0TZ5-mpxR8PXgni_YUiJKQdJZYcNpE9QjnP9I,965
19
+ abstractflow/visual/agent_ids.py,sha256=0L4ngxBxuLaLufA35q9wZTYVgC5FDO1skZPN5a3hvmc,187
20
+ abstractflow/visual/builtins.py,sha256=QVfIRPid4p45bDvj9RBPjnBkynhgcY3547jR7wNUkd8,185
21
+ abstractflow/visual/code_executor.py,sha256=gsYsPGTta5jnKiEourXygjMkRJWmvHYiquNwEFZFSvI,195
22
+ abstractflow/visual/event_ids.py,sha256=NzSry5Omjbxnn163CP5PJ3CQJ2dXo-Flf5sBROLjeYQ,858
23
+ abstractflow/visual/executor.py,sha256=Vt-Luy1z5SvCsq6WZfTFJVeLOg0YujxNdt4HfagG10I,42754
24
+ abstractflow/visual/interfaces.py,sha256=DcGwBKY7VxGH5-uC6MmXJD5CNpdYaqVal6ufjADqa6w,16051
25
+ abstractflow/visual/models.py,sha256=8ze_g4olmP7XFAAugmBcVvoW6jxtzMPLHDvguFRIhkI,8402
26
+ abstractflow/visual/session_runner.py,sha256=egdBXYHCg5esRaOzqIco30AzvoiVAaDvgZQDDNuVSmg,7603
27
+ abstractflow/visual/workspace_scoped_tools.py,sha256=epSnsUvuEtuxg1pep8gnjDSX5-phq3rkZsJlGJCOQ8g,1029
28
+ abstractflow-0.3.1.dist-info/licenses/LICENSE,sha256=_zpmvJ804El0O63VB5CWtADw0TccHWYvCRLm9c05blM,1076
29
+ abstractflow-0.3.1.dist-info/METADATA,sha256=B2jw5IEMR9gqPGsx91CTncZNK1j4FwhsHIO5XzrPTP8,6406
30
+ abstractflow-0.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
31
+ abstractflow-0.3.1.dist-info/entry_points.txt,sha256=Gc916Xwp7HMEOUlxFYHn7lMRrOT3Ah0Q_3tP9S8LHP0,55
32
+ abstractflow-0.3.1.dist-info/top_level.txt,sha256=bimZZ-20W8CxqozcCSWc_NlDus4gBMlKsMZC7xQxzww,13
33
+ abstractflow-0.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5