abstractgateway 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,178 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.pyc
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ pip-wheel-metadata/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ .python-version
87
+
88
+ # pipenv
89
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
90
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
91
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
92
+ # install all needed dependencies.
93
+ #Pipfile.lock
94
+
95
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
96
+ __pypackages__/
97
+
98
+ # Celery stuff
99
+ celerybeat-schedule
100
+ celerybeat.pid
101
+
102
+ # SageMath parsed files
103
+ *.sage.py
104
+
105
+ # Environments
106
+ .env
107
+ .venv
108
+ env/
109
+ venv/
110
+ ENV/
111
+ env.bak/
112
+ venv.bak/
113
+
114
+ # Spyder project settings
115
+ .spyderproject
116
+ .spyproject
117
+
118
+ # Rope project settings
119
+ .ropeproject
120
+
121
+ # mkdocs documentation
122
+ /site
123
+
124
+ # mypy
125
+ .mypy_cache/
126
+ .dmypy.json
127
+ dmypy.json
128
+
129
+ # Pyre type checker
130
+ .pyre/
131
+
132
+ # IDE
133
+ .vscode/
134
+ .idea/
135
+ *.swp
136
+ *.swo
137
+ *~
138
+
139
+ # macOS
140
+ .DS_Store
141
+
142
+ # Windows
143
+ Thumbs.db
144
+ ehthumbs.db
145
+ Desktop.ini
146
+
147
+ # Node.js / Frontend
148
+ node_modules/
149
+ web/frontend/node_modules/
150
+ web/frontend/dist/
151
+ web/frontend/.vite/
152
+ *.local
153
+
154
+ # Package manager locks (optional - uncomment if you want to ignore)
155
+ # package-lock.json
156
+ # yarn.lock
157
+ # pnpm-lock.yaml
158
+
159
+ # TypeScript
160
+ *.tsbuildinfo
161
+
162
+ # ESLint
163
+ .eslintcache
164
+
165
+ # Vite
166
+ *.local
167
+
168
+ # SQLite databases (for flow storage)
169
+ *.db
170
+ *.sqlite
171
+ *.sqlite3
172
+ flows.db
173
+
174
+ # Monaco Editor cache
175
+ .monaco/
176
+
177
+
178
+ web/runtime/
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: abstractgateway
3
+ Version: 0.1.0
4
+ Summary: AbstractGateway: deployable Run Gateway host for AbstractRuntime (commands + ledger).
5
+ Project-URL: GitHub, https://github.com/lpalbou/abstractgateway
6
+ Author: Laurent-Philippe Albou
7
+ License: MIT
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: abstractruntime>=0.4.0
10
+ Requires-Dist: fastapi>=0.100.0
11
+ Requires-Dist: uvicorn[standard]>=0.23.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
14
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
15
+ Provides-Extra: visualflow
16
+ Requires-Dist: abstractflow>=0.3.0; extra == 'visualflow'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # AbstractGateway
20
+
21
+ AbstractGateway is the **deployable Run Gateway host** for AbstractRuntime runs:
22
+ - durable command inbox
23
+ - ledger replay/stream
24
+ - security baseline (token + origin + limits)
25
+
26
+ This decouples the gateway service from any specific UI (AbstractFlow, AbstractCode, web/PWA thin clients).
27
+
28
+ ## What it does (contract)
29
+ - Clients **act** by submitting durable commands: `start`, `resume`, `pause`, `cancel`, `emit_event`
30
+ - Clients **render** by replaying/streaming the durable ledger (cursor-based, replay-first)
31
+
32
+ Endpoints:
33
+ - `POST /api/gateway/runs/start`
34
+ - `GET /api/gateway/runs/{run_id}`
35
+ - `GET /api/gateway/runs/{run_id}/ledger`
36
+ - `GET /api/gateway/runs/{run_id}/ledger/stream` (SSE)
37
+ - `POST /api/gateway/commands`
38
+
39
+ ## Install
40
+
41
+ ### Default (bundle mode)
42
+
43
+ ```bash
44
+ pip install abstractgateway
45
+ ```
46
+
47
+ Bundle mode executes **WorkflowBundles** (`.flow`) via **WorkflowArtifacts** without importing `abstractflow`.
48
+
49
+ ### Optional (compatibility): VisualFlow JSON
50
+
51
+ ```bash
52
+ pip install "abstractgateway[visualflow]"
53
+ ```
54
+
55
+ This mode depends on the **AbstractFlow compiler library** (`abstractflow`) to interpret VisualFlow JSON (it does **not** require the AbstractFlow web UI/app).
56
+
57
+ ## Run
58
+
59
+ ```bash
60
+ export ABSTRACTGATEWAY_DATA_DIR="./runtime"
61
+ export ABSTRACTGATEWAY_FLOWS_DIR="/path/to/bundles-or-flow"
62
+
63
+ # Security (recommended)
64
+ export ABSTRACTGATEWAY_AUTH_TOKEN="your-token"
65
+ export ABSTRACTGATEWAY_ALLOWED_ORIGINS="*"
66
+
67
+ abstractgateway serve --host 127.0.0.1 --port 8080
68
+ ```
69
+
70
+ Notes:
71
+ - `ABSTRACTGATEWAY_WORKFLOW_SOURCE` defaults to `bundle`. Valid values:
72
+ - `bundle` (default): `ABSTRACTGATEWAY_FLOWS_DIR` points to a directory containing `*.flow` bundles (or a single `.flow` file)
73
+ - `visualflow` (compat): `ABSTRACTGATEWAY_FLOWS_DIR` points to a directory containing `*.json` VisualFlow files
74
+ - For production, run behind HTTPS (reverse proxy) and set exact allowed origins.
75
+
76
+ ## Creating a `.flow` bundle (authoring)
77
+
78
+ Use AbstractFlow to pack a bundle:
79
+
80
+ ```bash
81
+ abstractflow bundle pack /path/to/root.json --out /path/to/bundles/my.flow --flows-dir /path/to/flows
82
+ ```
83
+
84
+ ## Starting a run (bundle mode)
85
+
86
+ The stable way is to pass `bundle_id` + `flow_id`:
87
+
88
+ ```bash
89
+ curl -sS -X POST "http://localhost:8080/api/gateway/runs/start" \
90
+ -H "Content-Type: application/json" \
91
+ -H "Authorization: Bearer your-token" \
92
+ -d '{"bundle_id":"my-bundle","flow_id":"ac-echo","input_data":{}}'
93
+ ```
94
+
95
+ For backwards-compatible clients, you can also pass a namespaced id as `flow_id` (`"my-bundle:ac-echo"`).
96
+
97
+ ## Docs
98
+ - Architecture: `docs/architecture.md` (framework) and `abstractgateway/docs/architecture.md` (this package)
99
+ - Deployment: `docs/guide/deployment.md`
100
+
101
+
@@ -0,0 +1,83 @@
1
+ # AbstractGateway
2
+
3
+ AbstractGateway is the **deployable Run Gateway host** for AbstractRuntime runs:
4
+ - durable command inbox
5
+ - ledger replay/stream
6
+ - security baseline (token + origin + limits)
7
+
8
+ This decouples the gateway service from any specific UI (AbstractFlow, AbstractCode, web/PWA thin clients).
9
+
10
+ ## What it does (contract)
11
+ - Clients **act** by submitting durable commands: `start`, `resume`, `pause`, `cancel`, `emit_event`
12
+ - Clients **render** by replaying/streaming the durable ledger (cursor-based, replay-first)
13
+
14
+ Endpoints:
15
+ - `POST /api/gateway/runs/start`
16
+ - `GET /api/gateway/runs/{run_id}`
17
+ - `GET /api/gateway/runs/{run_id}/ledger`
18
+ - `GET /api/gateway/runs/{run_id}/ledger/stream` (SSE)
19
+ - `POST /api/gateway/commands`
20
+
21
+ ## Install
22
+
23
+ ### Default (bundle mode)
24
+
25
+ ```bash
26
+ pip install abstractgateway
27
+ ```
28
+
29
+ Bundle mode executes **WorkflowBundles** (`.flow`) via **WorkflowArtifacts** without importing `abstractflow`.
30
+
31
+ ### Optional (compatibility): VisualFlow JSON
32
+
33
+ ```bash
34
+ pip install "abstractgateway[visualflow]"
35
+ ```
36
+
37
+ This mode depends on the **AbstractFlow compiler library** (`abstractflow`) to interpret VisualFlow JSON (it does **not** require the AbstractFlow web UI/app).
38
+
39
+ ## Run
40
+
41
+ ```bash
42
+ export ABSTRACTGATEWAY_DATA_DIR="./runtime"
43
+ export ABSTRACTGATEWAY_FLOWS_DIR="/path/to/bundles-or-flow"
44
+
45
+ # Security (recommended)
46
+ export ABSTRACTGATEWAY_AUTH_TOKEN="your-token"
47
+ export ABSTRACTGATEWAY_ALLOWED_ORIGINS="*"
48
+
49
+ abstractgateway serve --host 127.0.0.1 --port 8080
50
+ ```
51
+
52
+ Notes:
53
+ - `ABSTRACTGATEWAY_WORKFLOW_SOURCE` defaults to `bundle`. Valid values:
54
+ - `bundle` (default): `ABSTRACTGATEWAY_FLOWS_DIR` points to a directory containing `*.flow` bundles (or a single `.flow` file)
55
+ - `visualflow` (compat): `ABSTRACTGATEWAY_FLOWS_DIR` points to a directory containing `*.json` VisualFlow files
56
+ - For production, run behind HTTPS (reverse proxy) and set exact allowed origins.
57
+
58
+ ## Creating a `.flow` bundle (authoring)
59
+
60
+ Use AbstractFlow to pack a bundle:
61
+
62
+ ```bash
63
+ abstractflow bundle pack /path/to/root.json --out /path/to/bundles/my.flow --flows-dir /path/to/flows
64
+ ```
65
+
66
+ ## Starting a run (bundle mode)
67
+
68
+ The stable way is to pass `bundle_id` + `flow_id`:
69
+
70
+ ```bash
71
+ curl -sS -X POST "http://localhost:8080/api/gateway/runs/start" \
72
+ -H "Content-Type: application/json" \
73
+ -H "Authorization: Bearer your-token" \
74
+ -d '{"bundle_id":"my-bundle","flow_id":"ac-echo","input_data":{}}'
75
+ ```
76
+
77
+ For backwards-compatible clients, you can also pass a namespaced id as `flow_id` (`"my-bundle:ac-echo"`).
78
+
79
+ ## Docs
80
+ - Architecture: `docs/architecture.md` (framework) and `abstractgateway/docs/architecture.md` (this package)
81
+ - Deployment: `docs/guide/deployment.md`
82
+
83
+
@@ -0,0 +1,71 @@
1
+ # AbstractGateway — Architecture (Living)
2
+
3
+ > Updated: 2026-01-08
4
+ > Status: implemented (see completed backlog 318)
5
+
6
+ AbstractGateway is the **deployable control-plane host** for AbstractRuntime runs:
7
+ - clients submit **durable commands** (start/resume/pause/cancel/emit_event)
8
+ - clients render by replaying/streaming the **append-only ledger** (cursor-based)
9
+ - the gateway host owns the durable stores and is the single authority for a run (ADR‑0020)
10
+
11
+ ## Diagram (v0)
12
+
13
+ ```mermaid
14
+ flowchart LR
15
+ subgraph Clients["Clients (stateless thin UIs)"]
16
+ ACode["AbstractCode (TUI)"]
17
+ Web["Web/PWA Thin Client"]
18
+ FlowUI["AbstractFlow UI"]
19
+ Third["3rd-party apps"]
20
+ end
21
+
22
+ subgraph GW["AbstractGateway (service)"]
23
+ API["HTTP API + SSE\n/api/gateway/*"]
24
+ Inbox["Durable Command Inbox\n(CommandStore)"]
25
+ Runner["GatewayRunner\npoll + tick"]
26
+ Stores["RunStore / LedgerStore / ArtifactStore"]
27
+ end
28
+
29
+ subgraph RT["AbstractRuntime (kernel)"]
30
+ Runtime["Runtime.tick / resume"]
31
+ end
32
+
33
+ Clients -->|commands| API
34
+ API --> Inbox
35
+ API -->|ledger replay/stream| Stores
36
+ Inbox --> Runner
37
+ Runner --> Runtime
38
+ Runtime --> Stores
39
+ ```
40
+
41
+ ## Scope and packaging
42
+ AbstractGateway should be deployable without installing authoring tools (AbstractFlow).
43
+ Workflow loading must therefore be pluggable:
44
+ - core `abstractgateway` depends on `abstractruntime`
45
+ - default workflow source: **WorkflowBundles (.flow)** containing **VisualFlow JSON** (`manifest.flows`), compiled via `abstractruntime.visualflow_compiler` (no `abstractflow` import)
46
+ - optional extras can add additional workflow sources (e.g. a “directory of VisualFlow JSON files” host wired with authoring-side helpers)
47
+
48
+ Bundle-mode execution wiring:
49
+ - VisualFlow is compiled via `abstractruntime.visualflow_compiler` (single semantics engine).
50
+ - LLM/tool workflows are supported by wiring `abstractruntime.integrations.abstractcore`:
51
+ - `LLM_CALL` handler (AbstractCore-backed)
52
+ - `TOOL_CALLS` handler (host-configured tool executor)
53
+ - Visual Agent nodes are supported by registering deterministic per-node ReAct workflows (requires `abstractagent`).
54
+ - Visual “On Event” nodes are supported by compiling derived listener workflows and starting them as child runs in the same session.
55
+
56
+ Runtime configuration (env):
57
+ - `ABSTRACTGATEWAY_PROVIDER` / `ABSTRACTGATEWAY_MODEL`: default provider/model for bundle-mode runs that contain LLM nodes.
58
+ - `ABSTRACTGATEWAY_TOOL_MODE`:
59
+ - `passthrough` (default): tool calls enter a durable wait (safest for untrusted hosts)
60
+ - `local`: tool calls execute in the gateway process (dev only)
61
+
62
+ Run start identifiers (bundle mode):
63
+ - The **bundle** is the portable distribution unit. Clients should primarily identify “what to run” via `bundle_id`.
64
+ - The **flow id** selects *which entrypoint/subflow* within the bundle to start:
65
+ - If a bundle has a single `manifest.entrypoints[]` item **or** declares `manifest.default_entrypoint`, the gateway can start it with `{bundle_id, input_data}` (no `flow_id`).
66
+ - If the bundle has multiple entrypoints and no `default_entrypoint`, clients must specify `flow_id` (or pass a fully-qualified workflow id like `bundle:flow`).
67
+
68
+ ## Related
69
+ - Backlog 318: `docs/backlog/completed/318-framework-abstractgateway-extract-run-gateway-host.md`
70
+ - ADR‑0018: `docs/adr/0018-durable-run-gateway-and-remote-host-control-plane.md`
71
+ - ADR‑0020: `docs/adr/0020-agent-host-pool-and-orchestrator-placement.md`
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "abstractgateway"
7
+ version = "0.1.0"
8
+ description = "AbstractGateway: deployable Run Gateway host for AbstractRuntime (commands + ledger)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Laurent-Philippe Albou" }]
13
+
14
+ dependencies = [
15
+ "abstractruntime>=0.4.0",
16
+ "fastapi>=0.100.0",
17
+ "uvicorn[standard]>=0.23.0",
18
+ ]
19
+
20
+ [project.optional-dependencies]
21
+ # Optional support for starting runs from VisualFlow JSON (compiled via AbstractFlow).
22
+ visualflow = [
23
+ "abstractflow>=0.3.0",
24
+ ]
25
+ dev = [
26
+ "pytest>=7.0.0",
27
+ "httpx>=0.27.0",
28
+ ]
29
+
30
+ [project.scripts]
31
+ abstractgateway = "abstractgateway.cli:main"
32
+
33
+ [project.urls]
34
+ "GitHub" = "https://github.com/lpalbou/abstractgateway"
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/abstractgateway"]
38
+
39
+
@@ -0,0 +1,11 @@
1
+ """AbstractGateway.
2
+
3
+ AbstractGateway is a deployable Run Gateway host for AbstractRuntime:
4
+ - durable command inbox (start/resume/pause/cancel/emit_event)
5
+ - ledger replay + SSE streaming (replay-first)
6
+ - security middleware for network-safe deployments
7
+ """
8
+
9
+ __version__ = "0.1.0"
10
+
11
+
@@ -0,0 +1,55 @@
1
+ """AbstractGateway FastAPI application."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from contextlib import asynccontextmanager
6
+
7
+ from fastapi import FastAPI
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+
10
+ from .routes import gateway_router
11
+ from .security import GatewaySecurityMiddleware, load_gateway_auth_policy_from_env
12
+
13
+
14
+ @asynccontextmanager
15
+ async def _lifespan(_app: FastAPI):
16
+ # Start the background worker that polls the durable command inbox and ticks runs.
17
+ from .service import start_gateway_runner, stop_gateway_runner
18
+
19
+ start_gateway_runner()
20
+ try:
21
+ yield
22
+ finally:
23
+ stop_gateway_runner()
24
+
25
+
26
+ app = FastAPI(
27
+ title="AbstractGateway",
28
+ description="Durable Run Gateway for AbstractRuntime (commands + ledger replay/stream).",
29
+ version="0.1.0",
30
+ lifespan=_lifespan,
31
+ )
32
+
33
+ # Gateway security (backlog 309).
34
+ app.add_middleware(GatewaySecurityMiddleware, policy=load_gateway_auth_policy_from_env())
35
+
36
+ # CORS for browser clients. In production, prefer configuring exact origins and terminating TLS at a reverse proxy.
37
+ #
38
+ # IMPORTANT: add after GatewaySecurityMiddleware so CORS headers are present even on early security rejections
39
+ # (otherwise browsers surface a generic "NetworkError").
40
+ app.add_middleware(
41
+ CORSMiddleware,
42
+ allow_origins=["*"],
43
+ allow_credentials=True,
44
+ allow_methods=["*"],
45
+ allow_headers=["*"],
46
+ )
47
+
48
+ app.include_router(gateway_router, prefix="/api")
49
+
50
+
51
+ @app.get("/api/health")
52
+ async def health_check():
53
+ return {"status": "healthy", "service": "abstractgateway"}
54
+
55
+
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+
6
+ def main(argv: list[str] | None = None) -> None:
7
+ parser = argparse.ArgumentParser(prog="abstractgateway", description="AbstractGateway (Run Gateway host)")
8
+ sub = parser.add_subparsers(dest="cmd", required=True)
9
+
10
+ serve = sub.add_parser("serve", help="Run the AbstractGateway HTTP/SSE server")
11
+ serve.add_argument("--host", default="127.0.0.1", help="Bind host (default: 127.0.0.1)")
12
+ serve.add_argument("--port", type=int, default=8080, help="Bind port (default: 8080)")
13
+ serve.add_argument("--reload", action="store_true", help="Enable auto-reload (dev only)")
14
+
15
+ args = parser.parse_args(argv)
16
+
17
+ if args.cmd == "serve":
18
+ import uvicorn
19
+
20
+ uvicorn.run(
21
+ "abstractgateway.app:app",
22
+ host=str(args.host),
23
+ port=int(args.port),
24
+ reload=bool(args.reload),
25
+ )
26
+ return
27
+
28
+ raise SystemExit(2)
29
+
30
+
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Any, Optional
7
+
8
+
9
+ def _as_bool(raw: Any, default: bool) -> bool:
10
+ if raw is None:
11
+ return default
12
+ if isinstance(raw, bool):
13
+ return raw
14
+ s = str(raw).strip().lower()
15
+ if not s:
16
+ return default
17
+ if s in {"1", "true", "yes", "on"}:
18
+ return True
19
+ if s in {"0", "false", "no", "off"}:
20
+ return False
21
+ return default
22
+
23
+
24
+ def _as_int(raw: Optional[str], default: int) -> int:
25
+ if raw is None or not str(raw).strip():
26
+ return default
27
+ try:
28
+ return int(str(raw).strip())
29
+ except Exception:
30
+ return default
31
+
32
+
33
+ def _as_float(raw: Optional[str], default: float) -> float:
34
+ if raw is None or not str(raw).strip():
35
+ return default
36
+ try:
37
+ return float(str(raw).strip())
38
+ except Exception:
39
+ return default
40
+
41
+
42
+ def _env(name: str, fallback: Optional[str] = None) -> Optional[str]:
43
+ v = os.getenv(name)
44
+ if v is not None and str(v).strip():
45
+ return v
46
+ if fallback:
47
+ v2 = os.getenv(fallback)
48
+ if v2 is not None and str(v2).strip():
49
+ return v2
50
+ return None
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class GatewayHostConfig:
55
+ """Process-level configuration for the AbstractGateway host."""
56
+
57
+ data_dir: Path
58
+ flows_dir: Path
59
+
60
+ runner_enabled: bool = True
61
+ poll_interval_s: float = 0.25
62
+ command_batch_limit: int = 200
63
+ tick_max_steps: int = 100
64
+ tick_workers: int = 2
65
+ run_scan_limit: int = 200
66
+
67
+ @staticmethod
68
+ def from_env() -> "GatewayHostConfig":
69
+ # NOTE: We intentionally use ABSTRACTGATEWAY_* as the canonical namespace.
70
+ # For a transition period, we accept legacy ABSTRACTFLOW_* names as fallbacks.
71
+ data_dir_raw = _env("ABSTRACTGATEWAY_DATA_DIR", "ABSTRACTFLOW_RUNTIME_DIR") or "./runtime"
72
+ flows_dir_raw = _env("ABSTRACTGATEWAY_FLOWS_DIR", "ABSTRACTFLOW_FLOWS_DIR") or "./flows"
73
+
74
+ enabled_raw = _env("ABSTRACTGATEWAY_RUNNER", "ABSTRACTFLOW_GATEWAY_RUNNER") or "1"
75
+ runner_enabled = _as_bool(enabled_raw, True)
76
+
77
+ poll_s = _as_float(_env("ABSTRACTGATEWAY_POLL_S", "ABSTRACTFLOW_GATEWAY_POLL_S"), 0.25)
78
+ tick_workers = _as_int(_env("ABSTRACTGATEWAY_TICK_WORKERS", "ABSTRACTFLOW_GATEWAY_TICK_WORKERS"), 2)
79
+ tick_steps = _as_int(_env("ABSTRACTGATEWAY_TICK_MAX_STEPS", "ABSTRACTFLOW_GATEWAY_TICK_MAX_STEPS"), 100)
80
+ batch = _as_int(_env("ABSTRACTGATEWAY_COMMAND_BATCH_LIMIT", "ABSTRACTFLOW_GATEWAY_COMMAND_BATCH_LIMIT"), 200)
81
+ scan = _as_int(_env("ABSTRACTGATEWAY_RUN_SCAN_LIMIT", "ABSTRACTFLOW_GATEWAY_RUN_SCAN_LIMIT"), 200)
82
+
83
+ return GatewayHostConfig(
84
+ data_dir=Path(data_dir_raw).expanduser().resolve(),
85
+ flows_dir=Path(flows_dir_raw).expanduser().resolve(),
86
+ runner_enabled=bool(runner_enabled),
87
+ poll_interval_s=float(poll_s),
88
+ command_batch_limit=max(1, int(batch)),
89
+ tick_max_steps=max(1, int(tick_steps)),
90
+ tick_workers=max(1, int(tick_workers)),
91
+ run_scan_limit=max(1, int(scan)),
92
+ )
93
+
94
+
@@ -0,0 +1,6 @@
1
+ from .visualflow_host import VisualFlowGatewayHost
2
+ from .bundle_host import WorkflowBundleGatewayHost
3
+
4
+ __all__ = ["VisualFlowGatewayHost", "WorkflowBundleGatewayHost"]
5
+
6
+