AbstractRuntime 0.0.0__tar.gz → 0.0.1__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.
- abstractruntime-0.0.1/.gitignore +88 -0
- {abstractruntime-0.0.0 → abstractruntime-0.0.1}/LICENSE +3 -1
- abstractruntime-0.0.1/PKG-INFO +163 -0
- abstractruntime-0.0.1/README.md +141 -0
- abstractruntime-0.0.1/ROADMAP.md +234 -0
- abstractruntime-0.0.1/docs/adr/0001_layered_coupling_with_abstractcore.md +27 -0
- abstractruntime-0.0.1/docs/adr/0002_execution_modes_local_remote_hybrid.md +32 -0
- abstractruntime-0.0.1/docs/adr/0003_provenance_tamper_evident_hash_chain.md +30 -0
- abstractruntime-0.0.1/docs/adr/README.md +37 -0
- abstractruntime-0.0.1/docs/backlog/README.md +42 -0
- abstractruntime-0.0.1/docs/backlog/completed/001_runtime_kernel.md +23 -0
- abstractruntime-0.0.1/docs/backlog/completed/002_persistence_and_ledger.md +23 -0
- abstractruntime-0.0.1/docs/backlog/completed/003_wait_primitives.md +18 -0
- abstractruntime-0.0.1/docs/backlog/completed/004_scheduler_driver.md +116 -0
- abstractruntime-0.0.1/docs/backlog/completed/005_abstractcore_integration.md +23 -0
- abstractruntime-0.0.1/docs/backlog/completed/006_snapshots_bookmarks.md +17 -0
- abstractruntime-0.0.1/docs/backlog/completed/007_provenance_hash_chain.md +22 -0
- abstractruntime-0.0.1/docs/backlog/completed/009_artifact_store.md +136 -0
- abstractruntime-0.0.1/docs/backlog/completed/011_subworkflow_support.md +454 -0
- abstractruntime-0.0.1/docs/backlog/completed/012_run_store_query_and_scheduler_support.md +78 -0
- abstractruntime-0.0.1/docs/backlog/completed/013_effect_retries_and_idempotency.md +190 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/001_integrations_abstractcore.md +257 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/001_runtime_kernel.md +37 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/002_persistence_and_ledger.md +30 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/002_snapshots_bookmarks.md +186 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/003_provenance_ledger_chain.md +209 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/003_wait_resume_and_scheduler.md +28 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/004_effect_handlers_and_integrations.md +32 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/004_tests.md +134 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/005_docs_updates.md +113 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/005_examples_and_composition.md +20 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/006_ai_fingerprint_and_provenance.md +68 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/DEPRECATED_README.md +33 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/README.md +112 -0
- abstractruntime-0.0.1/docs/backlog/deprecated/abstractruntime_docs_final_02a7373b.plan.md +124 -0
- abstractruntime-0.0.1/docs/backlog/planned/008_signatures_and_keys.md +21 -0
- abstractruntime-0.0.1/docs/backlog/planned/010_examples_and_composition.md +29 -0
- abstractruntime-0.0.1/docs/backlog/planned/014_remote_tool_worker_executor.md +69 -0
- abstractruntime-0.0.1/docs/integrations/abstractcore.md +97 -0
- abstractruntime-0.0.1/docs/manual_testing.md +309 -0
- abstractruntime-0.0.1/docs/proposal.md +166 -0
- abstractruntime-0.0.1/docs/provenance.md +36 -0
- abstractruntime-0.0.1/docs/snapshots.md +47 -0
- {abstractruntime-0.0.0 → abstractruntime-0.0.1}/pyproject.toml +6 -4
- abstractruntime-0.0.1/src/abstractruntime/__init__.py +110 -0
- abstractruntime-0.0.1/src/abstractruntime/core/__init__.py +19 -0
- abstractruntime-0.0.1/src/abstractruntime/core/models.py +239 -0
- abstractruntime-0.0.1/src/abstractruntime/core/policy.py +166 -0
- abstractruntime-0.0.1/src/abstractruntime/core/runtime.py +581 -0
- abstractruntime-0.0.1/src/abstractruntime/core/spec.py +53 -0
- abstractruntime-0.0.1/src/abstractruntime/identity/__init__.py +7 -0
- abstractruntime-0.0.1/src/abstractruntime/identity/fingerprint.py +57 -0
- abstractruntime-0.0.1/src/abstractruntime/integrations/__init__.py +11 -0
- abstractruntime-0.0.1/src/abstractruntime/integrations/abstractcore/__init__.py +43 -0
- abstractruntime-0.0.1/src/abstractruntime/integrations/abstractcore/effect_handlers.py +89 -0
- abstractruntime-0.0.1/src/abstractruntime/integrations/abstractcore/factory.py +150 -0
- abstractruntime-0.0.1/src/abstractruntime/integrations/abstractcore/llm_client.py +296 -0
- abstractruntime-0.0.1/src/abstractruntime/integrations/abstractcore/logging.py +27 -0
- abstractruntime-0.0.1/src/abstractruntime/integrations/abstractcore/tool_executor.py +89 -0
- abstractruntime-0.0.1/src/abstractruntime/scheduler/__init__.py +13 -0
- abstractruntime-0.0.1/src/abstractruntime/scheduler/convenience.py +324 -0
- abstractruntime-0.0.1/src/abstractruntime/scheduler/registry.py +101 -0
- abstractruntime-0.0.1/src/abstractruntime/scheduler/scheduler.py +431 -0
- abstractruntime-0.0.1/src/abstractruntime/storage/__init__.py +25 -0
- abstractruntime-0.0.1/src/abstractruntime/storage/artifacts.py +488 -0
- abstractruntime-0.0.1/src/abstractruntime/storage/base.py +107 -0
- abstractruntime-0.0.1/src/abstractruntime/storage/in_memory.py +119 -0
- abstractruntime-0.0.1/src/abstractruntime/storage/json_files.py +208 -0
- abstractruntime-0.0.1/src/abstractruntime/storage/ledger_chain.py +153 -0
- abstractruntime-0.0.1/src/abstractruntime/storage/snapshots.py +217 -0
- abstractruntime-0.0.1/tests/README.md +274 -0
- abstractruntime-0.0.1/tests/conftest.py +20 -0
- abstractruntime-0.0.1/tests/test_artifacts.py +689 -0
- abstractruntime-0.0.1/tests/test_integration_abstractcore.py +281 -0
- abstractruntime-0.0.1/tests/test_integrations_abstractcore.py +125 -0
- abstractruntime-0.0.1/tests/test_ledger_chain.py +57 -0
- abstractruntime-0.0.1/tests/test_pause_resume.py +98 -0
- abstractruntime-0.0.1/tests/test_queryable_run_store.py +360 -0
- abstractruntime-0.0.1/tests/test_real_integration.py +758 -0
- abstractruntime-0.0.1/tests/test_remote_llm_client.py +53 -0
- abstractruntime-0.0.1/tests/test_retry_idempotency.py +534 -0
- abstractruntime-0.0.1/tests/test_scheduler.py +700 -0
- abstractruntime-0.0.1/tests/test_snapshots.py +47 -0
- abstractruntime-0.0.1/tests/test_subworkflow.py +725 -0
- abstractruntime-0.0.0/.gitignore +0 -54
- abstractruntime-0.0.0/PKG-INFO +0 -89
- abstractruntime-0.0.0/README.md +0 -67
- abstractruntime-0.0.0/src/abstractruntime/__init__.py +0 -8
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
|
|
53
|
+
# mypy
|
|
54
|
+
.mypy_cache/
|
|
55
|
+
.dmypy.json
|
|
56
|
+
dmypy.json
|
|
57
|
+
|
|
58
|
+
# Environments
|
|
59
|
+
.env
|
|
60
|
+
.venv
|
|
61
|
+
env/
|
|
62
|
+
venv/
|
|
63
|
+
ENV/
|
|
64
|
+
env.bak/
|
|
65
|
+
venv.bak/
|
|
66
|
+
|
|
67
|
+
# IDEs and editors
|
|
68
|
+
.idea/
|
|
69
|
+
.vscode/
|
|
70
|
+
*.swp
|
|
71
|
+
*.swo
|
|
72
|
+
|
|
73
|
+
# Jupyter Notebook
|
|
74
|
+
.ipynb_checkpoints
|
|
75
|
+
|
|
76
|
+
# Pyre type checker
|
|
77
|
+
.pyre/
|
|
78
|
+
|
|
79
|
+
# profiling
|
|
80
|
+
.prof
|
|
81
|
+
|
|
82
|
+
# Local config
|
|
83
|
+
*.env
|
|
84
|
+
.env.*
|
|
85
|
+
# End Generation Here
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
.DS_Store
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2025
|
|
3
|
+
Copyright (c) 2025 Laurent-Philippe Albou
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -21,3 +21,5 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
21
21
|
SOFTWARE.
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: AbstractRuntime
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: AbstractRuntime: a durable graph runner designed to pair with AbstractCore.
|
|
5
|
+
Project-URL: AbstractCore (website), https://www.abstractcore.ai/
|
|
6
|
+
Project-URL: AbstractCore (GitHub), https://github.com/lpalbou/abstractruntime
|
|
7
|
+
Author: Laurent-Philippe Albou
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agents,checkpoint,durable,graph,llm,resume,workflow
|
|
11
|
+
Classifier: Development Status :: 1 - Planning
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
## AbstractRuntime
|
|
24
|
+
|
|
25
|
+
**AbstractRuntime** is a low-level **durable workflow runtime**:
|
|
26
|
+
- Execute workflow graphs (state machines)
|
|
27
|
+
- **Interrupt → checkpoint → resume** (hours/days) without keeping Python stacks alive
|
|
28
|
+
- Append-only **ledger** ("journal d’exécution") for audit/debug/provenance
|
|
29
|
+
|
|
30
|
+
**Status**: MVP kernel + file persistence + AbstractCore integration adapters are implemented.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
### Key concepts
|
|
35
|
+
- **WorkflowSpec**: graph definition (node handlers keyed by id)
|
|
36
|
+
- **RunState**: durable state (`current_node`, `vars`, `waiting`, etc.)
|
|
37
|
+
- **Effect**: a side-effect request (`llm_call`, `tool_calls`, `ask_user`, `wait_event`, ...)
|
|
38
|
+
- **Ledger**: append-only step records (`StepRecord`) describing what happened
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
### Quick start (pause + resume)
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from abstractruntime import Effect, EffectType, Runtime, StepPlan, WorkflowSpec
|
|
46
|
+
from abstractruntime.storage import InMemoryLedgerStore, InMemoryRunStore
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def ask(run, ctx):
|
|
50
|
+
return StepPlan(
|
|
51
|
+
node_id="ask",
|
|
52
|
+
effect=Effect(
|
|
53
|
+
type=EffectType.ASK_USER,
|
|
54
|
+
payload={"prompt": "Continue?"},
|
|
55
|
+
result_key="user_answer",
|
|
56
|
+
),
|
|
57
|
+
next_node="done",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def done(run, ctx):
|
|
62
|
+
return StepPlan(node_id="done", complete_output={"answer": run.vars.get("user_answer")})
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
wf = WorkflowSpec(workflow_id="demo", entry_node="ask", nodes={"ask": ask, "done": done})
|
|
66
|
+
rt = Runtime(run_store=InMemoryRunStore(), ledger_store=InMemoryLedgerStore())
|
|
67
|
+
|
|
68
|
+
run_id = rt.start(workflow=wf)
|
|
69
|
+
state = rt.tick(workflow=wf, run_id=run_id)
|
|
70
|
+
assert state.status.value == "waiting"
|
|
71
|
+
|
|
72
|
+
state = rt.resume(
|
|
73
|
+
workflow=wf,
|
|
74
|
+
run_id=run_id,
|
|
75
|
+
wait_key=state.waiting.wait_key,
|
|
76
|
+
payload={"text": "yes"},
|
|
77
|
+
)
|
|
78
|
+
assert state.status.value == "completed"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### Built-in Scheduler
|
|
84
|
+
|
|
85
|
+
AbstractRuntime includes a zero-config scheduler for automatic run resumption:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from abstractruntime import create_scheduled_runtime
|
|
89
|
+
|
|
90
|
+
# Zero-config: defaults to in-memory storage, auto-starts scheduler
|
|
91
|
+
sr = create_scheduled_runtime()
|
|
92
|
+
|
|
93
|
+
# run() does start + tick in one call
|
|
94
|
+
run_id, state = sr.run(my_workflow)
|
|
95
|
+
|
|
96
|
+
# If waiting for user input, respond (auto-finds wait_key)
|
|
97
|
+
if state.status.value == "waiting":
|
|
98
|
+
state = sr.respond(run_id, {"answer": "yes"})
|
|
99
|
+
|
|
100
|
+
# Stop scheduler when done
|
|
101
|
+
sr.stop()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For production with persistent storage:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from abstractruntime import create_scheduled_runtime, JsonFileRunStore, JsonlLedgerStore
|
|
108
|
+
|
|
109
|
+
sr = create_scheduled_runtime(
|
|
110
|
+
run_store=JsonFileRunStore("./data"),
|
|
111
|
+
ledger_store=JsonlLedgerStore("./data"),
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### AbstractCore integration (LLM + tools)
|
|
118
|
+
|
|
119
|
+
AbstractRuntime’s kernel stays dependency-light; AbstractCore integration lives in:
|
|
120
|
+
- `src/abstractruntime/integrations/abstractcore/`
|
|
121
|
+
|
|
122
|
+
Execution modes:
|
|
123
|
+
- **Local**: in-process AbstractCore providers + local tool execution
|
|
124
|
+
- **Remote**: HTTP to AbstractCore server (`/v1/chat/completions`) + tool passthrough (default)
|
|
125
|
+
- **Hybrid**: remote LLM + local tool execution
|
|
126
|
+
|
|
127
|
+
See: `docs/integrations/abstractcore.md`.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### Snapshots / bookmarks
|
|
132
|
+
|
|
133
|
+
Snapshots are named, searchable checkpoints of a run state:
|
|
134
|
+
- `Snapshot(snapshot_id, run_id, name, description, tags, run_state)`
|
|
135
|
+
|
|
136
|
+
See: `docs/snapshots.md`.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### Provenance (tamper-evident ledger)
|
|
141
|
+
|
|
142
|
+
You can wrap any `LedgerStore` with a hash chain:
|
|
143
|
+
|
|
144
|
+
- `HashChainedLedgerStore(inner_store)`
|
|
145
|
+
- `verify_ledger_chain(records)`
|
|
146
|
+
|
|
147
|
+
This is **tamper-evident**, not non-forgeable (signatures are optional future work).
|
|
148
|
+
|
|
149
|
+
See: `docs/provenance.md`.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
### Documentation index
|
|
154
|
+
|
|
155
|
+
| Document | Description |
|
|
156
|
+
|----------|-------------|
|
|
157
|
+
| [Proposal](docs/proposal.md) | Design goals, core concepts, and scope |
|
|
158
|
+
| [ROADMAP](ROADMAP.md) | Prioritized next steps with rationale |
|
|
159
|
+
| [ADRs](docs/adr/) | Architectural decisions and their rationale |
|
|
160
|
+
| [Backlog](docs/backlog/) | Completed and planned work items |
|
|
161
|
+
| [Integrations](docs/integrations/) | AbstractCore integration guide |
|
|
162
|
+
| [Snapshots](docs/snapshots.md) | Named checkpoints for run state |
|
|
163
|
+
| [Provenance](docs/provenance.md) | Tamper-evident ledger documentation |
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
## AbstractRuntime
|
|
2
|
+
|
|
3
|
+
**AbstractRuntime** is a low-level **durable workflow runtime**:
|
|
4
|
+
- Execute workflow graphs (state machines)
|
|
5
|
+
- **Interrupt → checkpoint → resume** (hours/days) without keeping Python stacks alive
|
|
6
|
+
- Append-only **ledger** ("journal d’exécution") for audit/debug/provenance
|
|
7
|
+
|
|
8
|
+
**Status**: MVP kernel + file persistence + AbstractCore integration adapters are implemented.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
### Key concepts
|
|
13
|
+
- **WorkflowSpec**: graph definition (node handlers keyed by id)
|
|
14
|
+
- **RunState**: durable state (`current_node`, `vars`, `waiting`, etc.)
|
|
15
|
+
- **Effect**: a side-effect request (`llm_call`, `tool_calls`, `ask_user`, `wait_event`, ...)
|
|
16
|
+
- **Ledger**: append-only step records (`StepRecord`) describing what happened
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
### Quick start (pause + resume)
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from abstractruntime import Effect, EffectType, Runtime, StepPlan, WorkflowSpec
|
|
24
|
+
from abstractruntime.storage import InMemoryLedgerStore, InMemoryRunStore
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def ask(run, ctx):
|
|
28
|
+
return StepPlan(
|
|
29
|
+
node_id="ask",
|
|
30
|
+
effect=Effect(
|
|
31
|
+
type=EffectType.ASK_USER,
|
|
32
|
+
payload={"prompt": "Continue?"},
|
|
33
|
+
result_key="user_answer",
|
|
34
|
+
),
|
|
35
|
+
next_node="done",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def done(run, ctx):
|
|
40
|
+
return StepPlan(node_id="done", complete_output={"answer": run.vars.get("user_answer")})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
wf = WorkflowSpec(workflow_id="demo", entry_node="ask", nodes={"ask": ask, "done": done})
|
|
44
|
+
rt = Runtime(run_store=InMemoryRunStore(), ledger_store=InMemoryLedgerStore())
|
|
45
|
+
|
|
46
|
+
run_id = rt.start(workflow=wf)
|
|
47
|
+
state = rt.tick(workflow=wf, run_id=run_id)
|
|
48
|
+
assert state.status.value == "waiting"
|
|
49
|
+
|
|
50
|
+
state = rt.resume(
|
|
51
|
+
workflow=wf,
|
|
52
|
+
run_id=run_id,
|
|
53
|
+
wait_key=state.waiting.wait_key,
|
|
54
|
+
payload={"text": "yes"},
|
|
55
|
+
)
|
|
56
|
+
assert state.status.value == "completed"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Built-in Scheduler
|
|
62
|
+
|
|
63
|
+
AbstractRuntime includes a zero-config scheduler for automatic run resumption:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from abstractruntime import create_scheduled_runtime
|
|
67
|
+
|
|
68
|
+
# Zero-config: defaults to in-memory storage, auto-starts scheduler
|
|
69
|
+
sr = create_scheduled_runtime()
|
|
70
|
+
|
|
71
|
+
# run() does start + tick in one call
|
|
72
|
+
run_id, state = sr.run(my_workflow)
|
|
73
|
+
|
|
74
|
+
# If waiting for user input, respond (auto-finds wait_key)
|
|
75
|
+
if state.status.value == "waiting":
|
|
76
|
+
state = sr.respond(run_id, {"answer": "yes"})
|
|
77
|
+
|
|
78
|
+
# Stop scheduler when done
|
|
79
|
+
sr.stop()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For production with persistent storage:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from abstractruntime import create_scheduled_runtime, JsonFileRunStore, JsonlLedgerStore
|
|
86
|
+
|
|
87
|
+
sr = create_scheduled_runtime(
|
|
88
|
+
run_store=JsonFileRunStore("./data"),
|
|
89
|
+
ledger_store=JsonlLedgerStore("./data"),
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### AbstractCore integration (LLM + tools)
|
|
96
|
+
|
|
97
|
+
AbstractRuntime’s kernel stays dependency-light; AbstractCore integration lives in:
|
|
98
|
+
- `src/abstractruntime/integrations/abstractcore/`
|
|
99
|
+
|
|
100
|
+
Execution modes:
|
|
101
|
+
- **Local**: in-process AbstractCore providers + local tool execution
|
|
102
|
+
- **Remote**: HTTP to AbstractCore server (`/v1/chat/completions`) + tool passthrough (default)
|
|
103
|
+
- **Hybrid**: remote LLM + local tool execution
|
|
104
|
+
|
|
105
|
+
See: `docs/integrations/abstractcore.md`.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### Snapshots / bookmarks
|
|
110
|
+
|
|
111
|
+
Snapshots are named, searchable checkpoints of a run state:
|
|
112
|
+
- `Snapshot(snapshot_id, run_id, name, description, tags, run_state)`
|
|
113
|
+
|
|
114
|
+
See: `docs/snapshots.md`.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### Provenance (tamper-evident ledger)
|
|
119
|
+
|
|
120
|
+
You can wrap any `LedgerStore` with a hash chain:
|
|
121
|
+
|
|
122
|
+
- `HashChainedLedgerStore(inner_store)`
|
|
123
|
+
- `verify_ledger_chain(records)`
|
|
124
|
+
|
|
125
|
+
This is **tamper-evident**, not non-forgeable (signatures are optional future work).
|
|
126
|
+
|
|
127
|
+
See: `docs/provenance.md`.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### Documentation index
|
|
132
|
+
|
|
133
|
+
| Document | Description |
|
|
134
|
+
|----------|-------------|
|
|
135
|
+
| [Proposal](docs/proposal.md) | Design goals, core concepts, and scope |
|
|
136
|
+
| [ROADMAP](ROADMAP.md) | Prioritized next steps with rationale |
|
|
137
|
+
| [ADRs](docs/adr/) | Architectural decisions and their rationale |
|
|
138
|
+
| [Backlog](docs/backlog/) | Completed and planned work items |
|
|
139
|
+
| [Integrations](docs/integrations/) | AbstractCore integration guide |
|
|
140
|
+
| [Snapshots](docs/snapshots.md) | Named checkpoints for run state |
|
|
141
|
+
| [Provenance](docs/provenance.md) | Tamper-evident ledger documentation |
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# AbstractRuntime Roadmap
|
|
2
|
+
|
|
3
|
+
## Current Status: MVP Complete (v0.1)
|
|
4
|
+
|
|
5
|
+
AbstractRuntime has a functional MVP kernel with:
|
|
6
|
+
- Durable workflow execution (start/tick/resume)
|
|
7
|
+
- Explicit waiting states (wait_event, wait_until, ask_user)
|
|
8
|
+
- Persistence (in-memory + JSON/JSONL file backends)
|
|
9
|
+
- Snapshots/bookmarks for named checkpoints
|
|
10
|
+
- Tamper-evident provenance (hash-chained ledger)
|
|
11
|
+
- AbstractCore integration (local/remote/hybrid execution modes)
|
|
12
|
+
- **QueryableRunStore** for listing/filtering runs ✅
|
|
13
|
+
- **Built-in Scheduler** with zero-config operation ✅
|
|
14
|
+
- Test coverage for core functionality (81% overall, 57 tests)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why AbstractRuntime Exists
|
|
19
|
+
|
|
20
|
+
AbstractRuntime is the **durable execution substrate** that sits below both AbstractAgent and AbstractFlow:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
AbstractFlow (UI graphs, multi-agent orchestration, templates)
|
|
24
|
+
│
|
|
25
|
+
AbstractAgent (ReAct, CodeAct, specialized agents like DeepSearch)
|
|
26
|
+
│
|
|
27
|
+
AbstractRuntime (interrupt/checkpoint/resume, ledger, snapshots)
|
|
28
|
+
│
|
|
29
|
+
AbstractCore (LLM calls, tool execution, server API)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Key insight**: You cannot keep Python stacks alive for days. When an agent needs to:
|
|
33
|
+
- Ask a user a question and wait hours/days for a response
|
|
34
|
+
- Wait until a scheduled time
|
|
35
|
+
- Wait for an external job to complete
|
|
36
|
+
|
|
37
|
+
...you need **durable state that survives process restarts**. This is what AbstractRuntime provides.
|
|
38
|
+
|
|
39
|
+
**Why not in AbstractFlow?** Because individual agents need these primitives directly. A ReAct agent that calls `ask_user` needs to pause and resume — that's not orchestration, that's the agent itself needing durability.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Phase 1: Core Completeness ✅ COMPLETE
|
|
44
|
+
|
|
45
|
+
### 1.1 Built-in Scheduler (Zero-Config) ✅
|
|
46
|
+
**Status: COMPLETE** | **Backlog: 004**
|
|
47
|
+
|
|
48
|
+
**What shipped**:
|
|
49
|
+
- `WorkflowRegistry` for mapping workflow_id → WorkflowSpec
|
|
50
|
+
- `Scheduler` class with background polling thread
|
|
51
|
+
- `ScheduledRuntime` convenience wrapper
|
|
52
|
+
- `create_scheduled_runtime()` factory function
|
|
53
|
+
- Event ingestion via `scheduler.resume_event()`
|
|
54
|
+
- Stats tracking and callbacks
|
|
55
|
+
|
|
56
|
+
**Usage**:
|
|
57
|
+
```python
|
|
58
|
+
# Zero-config: defaults to in-memory, auto-starts scheduler
|
|
59
|
+
sr = create_scheduled_runtime()
|
|
60
|
+
|
|
61
|
+
# run() does start + tick in one call
|
|
62
|
+
run_id, state = sr.run(my_workflow)
|
|
63
|
+
|
|
64
|
+
# respond() auto-finds wait_key
|
|
65
|
+
if state.status.value == "waiting":
|
|
66
|
+
state = sr.respond(run_id, {"answer": "yes"})
|
|
67
|
+
|
|
68
|
+
sr.stop()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Phase 2: Composition and Examples (Next Priority)
|
|
74
|
+
|
|
75
|
+
### 2.1 Subworkflow Support
|
|
76
|
+
**Priority: High** | **Effort: Medium** | **Backlog: 011**
|
|
77
|
+
|
|
78
|
+
**Why**: `EffectType.START_SUBWORKFLOW` exists but has no handler. Without this:
|
|
79
|
+
- Multi-agent orchestration is impossible
|
|
80
|
+
- Workflow composition (DeepSearch as a node) doesn't work
|
|
81
|
+
- AbstractFlow cannot compose agents
|
|
82
|
+
|
|
83
|
+
**Deliverables**:
|
|
84
|
+
- Workflow registry interface
|
|
85
|
+
- `START_SUBWORKFLOW` effect handler
|
|
86
|
+
- Sync and async subworkflow modes
|
|
87
|
+
- Tests for parent/child workflow interaction
|
|
88
|
+
|
|
89
|
+
**Success criteria**: A workflow can invoke another workflow by ID and receive its output.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### 2.2 Examples and Documentation
|
|
94
|
+
**Priority: High** | **Effort: Low** | **Backlog: 010**
|
|
95
|
+
|
|
96
|
+
**Why**: The MVP is functional but lacks concrete examples. Developers cannot understand how to build workflows without reading source code.
|
|
97
|
+
|
|
98
|
+
**Deliverables**:
|
|
99
|
+
- `examples/` directory with runnable workflows
|
|
100
|
+
- ask_user interrupt example (pause for days, resume)
|
|
101
|
+
- wait_until timer example
|
|
102
|
+
- LLM call + tool execution example
|
|
103
|
+
- Subworkflow composition example
|
|
104
|
+
|
|
105
|
+
**Success criteria**: A developer can copy an example and have a working workflow in 5 minutes.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Phase 3: Production Readiness
|
|
110
|
+
|
|
111
|
+
### 3.1 Artifact Store
|
|
112
|
+
**Priority: Medium** | **Effort: Medium** | **Backlog: 009**
|
|
113
|
+
|
|
114
|
+
**Why**: Large payloads (documents, images, tool outputs) embedded in `RunState.vars` cause performance issues. The constraint that vars must be JSON-serializable becomes painful without by-reference storage.
|
|
115
|
+
|
|
116
|
+
**Deliverables**:
|
|
117
|
+
- `ArtifactStore` interface
|
|
118
|
+
- File-based implementation
|
|
119
|
+
- `ArtifactRef` type for referencing stored artifacts
|
|
120
|
+
- Integration with RunState serialization
|
|
121
|
+
|
|
122
|
+
**Success criteria**: A workflow can store a 10MB document without bloating the checkpoint.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### 3.2 Effect Retries and Idempotency
|
|
127
|
+
**Priority: Medium** | **Effort: Medium** | **Backlog: 013**
|
|
128
|
+
|
|
129
|
+
**Why**: At-least-once execution is a real risk. If a process crashes after an LLM call but before checkpointing, the call may be duplicated on restart.
|
|
130
|
+
|
|
131
|
+
**Deliverables**:
|
|
132
|
+
- `EffectPolicy` protocol (max_attempts, backoff, idempotency_key)
|
|
133
|
+
- Ledger-based deduplication
|
|
134
|
+
- Retry logic in runtime loop
|
|
135
|
+
|
|
136
|
+
**Success criteria**: A workflow survives a crash/restart without duplicating side effects.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Phase 4: Advanced Features
|
|
141
|
+
|
|
142
|
+
### 4.1 Cryptographic Signatures
|
|
143
|
+
**Priority: Low** | **Effort: High** | **Backlog: 008**
|
|
144
|
+
|
|
145
|
+
**Why**: The hash chain provides tamper-evidence but not non-forgeability. For high-accountability scenarios (AI fingerprinting, regulatory compliance), cryptographic signatures are needed.
|
|
146
|
+
|
|
147
|
+
**Dependencies**: 007 (Hash Chain - complete)
|
|
148
|
+
|
|
149
|
+
**Deliverables**:
|
|
150
|
+
- Ed25519 keypair generation
|
|
151
|
+
- `actor_id` bound to public key
|
|
152
|
+
- Signed `StepRecord` entries
|
|
153
|
+
- Signature verification in `verify_ledger_chain`
|
|
154
|
+
- Key storage patterns (file, OS keychain, Vault)
|
|
155
|
+
|
|
156
|
+
**Success criteria**: A ledger can prove which actor produced each step.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### 4.2 Remote Tool Worker
|
|
161
|
+
**Priority: Low** | **Effort: Medium** | **Backlog: 014**
|
|
162
|
+
|
|
163
|
+
**Why**: Some deployments need centralized tool execution (thin clients, sandboxed environments). The current passthrough mode requires the host to execute tools; a worker service would handle this automatically.
|
|
164
|
+
|
|
165
|
+
**Deliverables**:
|
|
166
|
+
- Tool worker API contract
|
|
167
|
+
- `RemoteToolExecutor` implementation
|
|
168
|
+
- Job-based waiting semantics
|
|
169
|
+
|
|
170
|
+
**Success criteria**: A thin client can run workflows with tools by delegating to a worker service.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Relationship to the Abstract Series
|
|
175
|
+
|
|
176
|
+
AbstractRuntime is the **durable execution substrate** that enables both agents and memory to have long-running, interruptible workflows.
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
AbstractFlow (UI graphs, multi-agent orchestration, templates)
|
|
180
|
+
│
|
|
181
|
+
├── AbstractSwarm (swarm of agents - future)
|
|
182
|
+
│
|
|
183
|
+
AbstractAgent (ReAct, CodeAct, DeepSearch, specialized agents)
|
|
184
|
+
│
|
|
185
|
+
├── AbstractMemory (agentic memory - consolidation, reflection, forgetting)
|
|
186
|
+
│
|
|
187
|
+
AbstractRuntime (interrupt/checkpoint/resume, ledger, snapshots)
|
|
188
|
+
│
|
|
189
|
+
AbstractCore (LLM calls, tool execution, server API)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Why this layering?**
|
|
193
|
+
- **AbstractAgent** needs durability primitives directly (an agent might `ask_user` and wait days)
|
|
194
|
+
- **AbstractMemory** needs the same primitives (memory consolidation might run on a schedule)
|
|
195
|
+
- **AbstractFlow** composes agents and workflows, using AbstractRuntime for execution
|
|
196
|
+
- **AbstractRuntime** provides the substrate; it doesn't know about "agents" or "memory" — just workflows
|
|
197
|
+
|
|
198
|
+
**Snapshots enable time-travel debugging:**
|
|
199
|
+
- Save a named checkpoint at any point
|
|
200
|
+
- Restore a multi-agent system to a previous state
|
|
201
|
+
- Rebuild context from ledger + snapshot
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Decision Log
|
|
206
|
+
|
|
207
|
+
| Decision | Rationale |
|
|
208
|
+
|----------|-----------|
|
|
209
|
+
| Kernel stays dependency-light | Enables portability, stability, and clear integration boundaries |
|
|
210
|
+
| AbstractCore integration is opt-in | Layered coupling prevents kernel from breaking when AbstractCore changes |
|
|
211
|
+
| Hash chain before signatures | Provides value immediately without key management complexity |
|
|
212
|
+
| Built-in scheduler (not external) | UX principle: simplify as much as possible; zero-config for simple cases |
|
|
213
|
+
| Graph representation for all workflows | A loop is a simple graph; graphs enable visualization, checkpointing, composition |
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Timeline (Estimated)
|
|
218
|
+
|
|
219
|
+
| Phase | Items | Status |
|
|
220
|
+
|-------|-------|--------|
|
|
221
|
+
| 1.1 | Scheduler + RunStore Query | ✅ Complete |
|
|
222
|
+
| 2.1 | Subworkflow | 2-3 days |
|
|
223
|
+
| 2.2 | Examples | 1-2 days |
|
|
224
|
+
| 3.1 | Artifact Store | 2-3 days |
|
|
225
|
+
| 3.2 | Retries/Idempotency | 3-4 days |
|
|
226
|
+
| 4.1 | Signatures | 1 week |
|
|
227
|
+
| 4.2 | Remote Worker | 3-4 days |
|
|
228
|
+
|
|
229
|
+
**Phase 1 (Core Completeness)**: ✅ Complete
|
|
230
|
+
**Phase 2 (Composition)**: ~3-5 days
|
|
231
|
+
**Phase 3 (Production Readiness)**: ~1 week
|
|
232
|
+
**Phase 4 (Advanced Features)**: ~2 weeks
|
|
233
|
+
|
|
234
|
+
Remaining: ~3-4 weeks for full roadmap.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## ADR 0001: Layered coupling with AbstractCore
|
|
2
|
+
|
|
3
|
+
### Status
|
|
4
|
+
Accepted (2025-12-11)
|
|
5
|
+
|
|
6
|
+
### Context
|
|
7
|
+
AbstractRuntime needs to reuse AbstractCore capabilities (LLM calls, tool execution, structured logging), but we must keep the **durable kernel** stable and dependency-light.
|
|
8
|
+
|
|
9
|
+
Mixing AbstractCore imports into the kernel creates:
|
|
10
|
+
- brittle coupling to AbstractCore internals
|
|
11
|
+
- larger runtime footprint for processes that only need bookkeeping (stores/ledger)
|
|
12
|
+
- harder evolution of the durable state machine
|
|
13
|
+
|
|
14
|
+
### Decision
|
|
15
|
+
- The kernel (`abstractruntime.core`, `abstractruntime.storage`, `abstractruntime.identity`) must **not** import AbstractCore.
|
|
16
|
+
- AbstractCore integration is an explicit opt-in module:
|
|
17
|
+
- `abstractruntime.integrations.abstractcore`
|
|
18
|
+
|
|
19
|
+
### Consequences
|
|
20
|
+
- Kernel remains stable and reusable across topologies.
|
|
21
|
+
- AbstractCore can evolve; only the integration module needs updates.
|
|
22
|
+
- We still support heavy reuse of AbstractCore in real deployments (local/remote/hybrid execution modes).
|
|
23
|
+
|
|
24
|
+
### See Also
|
|
25
|
+
- Implementation: [`backlog/completed/005_abstractcore_integration.md`](../backlog/completed/005_abstractcore_integration.md)
|
|
26
|
+
- Code: `src/abstractruntime/integrations/abstractcore/`
|
|
27
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
## ADR 0002: Execution modes (local, remote, hybrid)
|
|
2
|
+
|
|
3
|
+
### Status
|
|
4
|
+
Accepted (2025-12-11)
|
|
5
|
+
|
|
6
|
+
### Context
|
|
7
|
+
Agents/workflows must run in multiple deployment topologies:
|
|
8
|
+
- thin clients (mobile/web) calling a backend LLM gateway
|
|
9
|
+
- backend orchestration calling GPU inference fleets
|
|
10
|
+
- local/dev mode (everything on one machine)
|
|
11
|
+
|
|
12
|
+
AbstractCore already provides two compatible boundaries:
|
|
13
|
+
- in-process python API (`create_llm(...).generate(...)`)
|
|
14
|
+
- HTTP server boundary (`/v1/chat/completions`)
|
|
15
|
+
|
|
16
|
+
### Decision
|
|
17
|
+
AbstractRuntime supports three execution modes:
|
|
18
|
+
|
|
19
|
+
- **Local**: in-process AbstractCore LLM + local tool execution
|
|
20
|
+
- **Remote**: HTTP to AbstractCore server; tools default to passthrough (untrusted)
|
|
21
|
+
- **Hybrid**: remote LLM + local tool execution
|
|
22
|
+
|
|
23
|
+
### Consequences
|
|
24
|
+
- Thin-mode clients can run the workflow logic while delegating inference to a server.
|
|
25
|
+
- Remote mode supports AbstractCore per-request `base_url` routing (dynamic endpoint selection).
|
|
26
|
+
- Tool execution can be gated by trust/sandbox policy outside the router.
|
|
27
|
+
|
|
28
|
+
### See Also
|
|
29
|
+
- Implementation: [`backlog/completed/005_abstractcore_integration.md`](../backlog/completed/005_abstractcore_integration.md)
|
|
30
|
+
- Integration guide: [`integrations/abstractcore.md`](../integrations/abstractcore.md)
|
|
31
|
+
- Code: `src/abstractruntime/integrations/abstractcore/factory.py`
|
|
32
|
+
|