weft 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.
- weft-0.1.0/.gitignore +77 -0
- weft-0.1.0/LICENSE +21 -0
- weft-0.1.0/PKG-INFO +515 -0
- weft-0.1.0/README.md +472 -0
- weft-0.1.0/docs/specifications/README.md +214 -0
- weft-0.1.0/pyproject.toml +141 -0
- weft-0.1.0/weft/__init__.py +25 -0
- weft-0.1.0/weft/__main__.py +6 -0
- weft-0.1.0/weft/_constants.py +428 -0
- weft-0.1.0/weft/cli.py +1208 -0
- weft-0.1.0/weft/cli_utils.py +119 -0
- weft-0.1.0/weft/commands/__init__.py +21 -0
- weft-0.1.0/weft/commands/dump.py +146 -0
- weft-0.1.0/weft/commands/handlers.py +94 -0
- weft-0.1.0/weft/commands/init.py +60 -0
- weft-0.1.0/weft/commands/interactive.py +298 -0
- weft-0.1.0/weft/commands/load.py +442 -0
- weft-0.1.0/weft/commands/queue.py +550 -0
- weft-0.1.0/weft/commands/result.py +362 -0
- weft-0.1.0/weft/commands/run.py +1539 -0
- weft-0.1.0/weft/commands/specs.py +166 -0
- weft-0.1.0/weft/commands/status.py +535 -0
- weft-0.1.0/weft/commands/tasks.py +218 -0
- weft-0.1.0/weft/commands/tidy.py +23 -0
- weft-0.1.0/weft/commands/validate_taskspec.py +94 -0
- weft-0.1.0/weft/commands/worker.py +281 -0
- weft-0.1.0/weft/config.py +0 -0
- weft-0.1.0/weft/context.py +305 -0
- weft-0.1.0/weft/core/__init__.py +57 -0
- weft-0.1.0/weft/core/callable.py +135 -0
- weft-0.1.0/weft/core/launcher.py +71 -0
- weft-0.1.0/weft/core/manager.py +798 -0
- weft-0.1.0/weft/core/resource_monitor.py +356 -0
- weft-0.1.0/weft/core/targets.py +136 -0
- weft-0.1.0/weft/core/tasks/__init__.py +21 -0
- weft-0.1.0/weft/core/tasks/base.py +1090 -0
- weft-0.1.0/weft/core/tasks/consumer.py +597 -0
- weft-0.1.0/weft/core/tasks/debugger.py +95 -0
- weft-0.1.0/weft/core/tasks/interactive.py +399 -0
- weft-0.1.0/weft/core/tasks/monitor.py +87 -0
- weft-0.1.0/weft/core/tasks/multiqueue_watcher.py +454 -0
- weft-0.1.0/weft/core/tasks/observer.py +97 -0
- weft-0.1.0/weft/core/tasks/runner.py +367 -0
- weft-0.1.0/weft/core/tasks/sessions.py +181 -0
- weft-0.1.0/weft/core/taskspec.py +1081 -0
- weft-0.1.0/weft/helpers.py +585 -0
- weft-0.1.0/weft/manager_process.py +37 -0
- weft-0.1.0/weft/shell/__init__.py +34 -0
- weft-0.1.0/weft/shell/known_interpreters.py +60 -0
weft-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
# Virtual environments
|
|
25
|
+
venv/
|
|
26
|
+
ENV/
|
|
27
|
+
env/
|
|
28
|
+
.venv
|
|
29
|
+
|
|
30
|
+
# IDEs
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
*~
|
|
36
|
+
|
|
37
|
+
# Testing
|
|
38
|
+
.coverage
|
|
39
|
+
.coverage.*
|
|
40
|
+
.pytest_cache/
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.mypy_cache/
|
|
45
|
+
.dmypy.json
|
|
46
|
+
dmypy.json
|
|
47
|
+
.ruff_cache/
|
|
48
|
+
.ruff/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
|
|
51
|
+
# SimpleBroker specific
|
|
52
|
+
*.db-shm
|
|
53
|
+
*.db-wal
|
|
54
|
+
.broker.db*
|
|
55
|
+
test.db
|
|
56
|
+
benchmark_pragma.py
|
|
57
|
+
|
|
58
|
+
# OS
|
|
59
|
+
.DS_Store
|
|
60
|
+
Thumbs.db
|
|
61
|
+
|
|
62
|
+
# Temporary files
|
|
63
|
+
*.tmp
|
|
64
|
+
*.bak
|
|
65
|
+
*.log
|
|
66
|
+
|
|
67
|
+
# Multi-agent
|
|
68
|
+
.claude
|
|
69
|
+
.mcp.json
|
|
70
|
+
agent_history/
|
|
71
|
+
.broker.db
|
|
72
|
+
.broker.db-shm
|
|
73
|
+
.broker.db-wal
|
|
74
|
+
*comments*.md
|
|
75
|
+
.code/
|
|
76
|
+
# This is in context for agents but we don't want it to check it in here
|
|
77
|
+
simplebroker/
|
weft-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Van Lindberg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
weft-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: weft
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight workflow execution framework
|
|
5
|
+
Project-URL: Homepage, https://github.com/VanL/weft
|
|
6
|
+
Project-URL: Documentation, https://github.com/VanL/weft#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/VanL/weft.git
|
|
8
|
+
Project-URL: Issues, https://github.com/VanL/weft/issues
|
|
9
|
+
Author-email: Van Lindberg <van@modelmonster.ai>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: automation,orchestration,task-execution,workflow
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Requires-Dist: aider-chat>=0.85.2
|
|
26
|
+
Requires-Dist: llm>=0.26
|
|
27
|
+
Requires-Dist: psutil>=7.0.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Requires-Dist: rich>=14.0.0
|
|
30
|
+
Requires-Dist: setproctitle>=1.3.7
|
|
31
|
+
Requires-Dist: simplebroker>=2.5.1
|
|
32
|
+
Requires-Dist: typer>=0.16.0
|
|
33
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-timeout>=2.4.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-xdist>=3.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff>=0.12; extra == 'dev'
|
|
41
|
+
Requires-Dist: types-psutil>=5.9.5; extra == 'dev'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# Weft
|
|
45
|
+
|
|
46
|
+
*A durable task execution system. Persistent workers, multiprocess isolation, comprehensive observability.*
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
$ weft run echo "hello world"
|
|
50
|
+
hello world
|
|
51
|
+
|
|
52
|
+
$ weft run --spec task.json
|
|
53
|
+
# streams task output and returns when complete
|
|
54
|
+
|
|
55
|
+
$ weft status
|
|
56
|
+
System: OK
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Weft is a queue-based task execution system focused on enabling interaction between AI agents, user-provided functions, and existing CLI tools. It combines the simplicity of direct command execution with the power of durable task queues, multiprocess isolation, and comprehensive state tracking.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Initialize project
|
|
66
|
+
$ weft init
|
|
67
|
+
Initialized weft in .weft/
|
|
68
|
+
|
|
69
|
+
# Run a simple command
|
|
70
|
+
$ weft run echo "hello world"
|
|
71
|
+
hello world
|
|
72
|
+
|
|
73
|
+
# Run with resource limits
|
|
74
|
+
$ weft run --memory 100 --cpu 50 python script.py
|
|
75
|
+
|
|
76
|
+
# Run and wait for completion
|
|
77
|
+
$ weft run --wait --timeout 30 ./long-task.sh
|
|
78
|
+
|
|
79
|
+
# Run a saved task spec (from .weft/tasks/)
|
|
80
|
+
$ weft run --spec data-cleanup
|
|
81
|
+
|
|
82
|
+
# Run a pipeline spec (from .weft/pipelines/)
|
|
83
|
+
$ weft run --pipeline etl-job
|
|
84
|
+
|
|
85
|
+
# Run a Python function
|
|
86
|
+
$ weft run --function mymodule:process_data --arg input.csv --kw mode=fast
|
|
87
|
+
|
|
88
|
+
# Check system status
|
|
89
|
+
$ weft status
|
|
90
|
+
System: OK
|
|
91
|
+
|
|
92
|
+
# Get task result
|
|
93
|
+
$ weft result 1234567890
|
|
94
|
+
{"status": "completed", "return_code": 0, "output": "..."}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Core Concepts
|
|
98
|
+
|
|
99
|
+
### Project Context
|
|
100
|
+
|
|
101
|
+
Weft uses `.weft/` directories for project isolation, similar to git repositories:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
myproject/
|
|
105
|
+
.weft/
|
|
106
|
+
broker.db # SimpleBroker database
|
|
107
|
+
tasks/ # Saved task specs
|
|
108
|
+
pipelines/ # Saved pipeline specs
|
|
109
|
+
autostart/ # Autostart manifests (lifecycle + defaults)
|
|
110
|
+
outputs/ # Large output spillover
|
|
111
|
+
logs/ # Centralized logging
|
|
112
|
+
...
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Run `weft` commands from anywhere in the project tree - it searches upward to find `.weft/`.
|
|
116
|
+
|
|
117
|
+
### Task IDs (TIDs)
|
|
118
|
+
|
|
119
|
+
Every task receives a unique 64-bit SimpleBroker timestamp (hybrid microseconds + logical counter), typically 19 digits:
|
|
120
|
+
|
|
121
|
+
- **Full TID**: `1837025672140161024` (Unique task ID)
|
|
122
|
+
- **Short TID**: `0161024` (last 10 digits for convenience)
|
|
123
|
+
- Used for correlation across queues and process titles
|
|
124
|
+
- Monotonic within a context, format-compatible with time.time_ns()
|
|
125
|
+
- The spawn-request message ID becomes the task TID for the full lifecycle
|
|
126
|
+
|
|
127
|
+
### Queue Structure
|
|
128
|
+
|
|
129
|
+
Each task gets its own queues:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
T{tid}.inbox # Work messages to process
|
|
133
|
+
T{tid}.reserved # Messages being processed (reservation pattern)
|
|
134
|
+
T{tid}.outbox # Results and output
|
|
135
|
+
T{tid}.ctrl_in # Control commands (STOP, STATUS, PING)
|
|
136
|
+
T{tid}.ctrl_out # Status responses
|
|
137
|
+
|
|
138
|
+
weft.log.tasks # Global state log (all tasks)
|
|
139
|
+
weft.spawn.requests # Task spawn requests to manager
|
|
140
|
+
weft.state.workers # Manager liveness tracking (runtime state)
|
|
141
|
+
weft.state.tid_mappings # Short->full TID mappings (runtime state)
|
|
142
|
+
weft.state.streaming # Active streaming sessions (runtime state)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Queues under `weft.state.*` are runtime-only and excluded from dumps by default.
|
|
146
|
+
|
|
147
|
+
### Reservation Pattern
|
|
148
|
+
|
|
149
|
+
Weft implements inbox -> reserved -> outbox flow for reliable message processing:
|
|
150
|
+
|
|
151
|
+
1. **Reserve**: Move message from inbox to reserved
|
|
152
|
+
2. **Process**: Execute work while message is in reserved
|
|
153
|
+
3. **Complete**: Write output to outbox, delete from reserved (or apply policy)
|
|
154
|
+
|
|
155
|
+
If a task crashes mid-work, the message remains in reserved for manual recovery or explicit requeue.
|
|
156
|
+
|
|
157
|
+
**Idempotency guidance**
|
|
158
|
+
- Single-message tasks may use `tid` as an idempotency key.
|
|
159
|
+
- Multi-message tasks should use the inbox/reserved message ID (timestamp).
|
|
160
|
+
- Recommended composite key: `tid:message_id`.
|
|
161
|
+
|
|
162
|
+
Configurable policies (`keep`, `requeue`, `clear`) control reserved queue behavior on errors.
|
|
163
|
+
|
|
164
|
+
### Managers
|
|
165
|
+
|
|
166
|
+
Persistent worker processes that:
|
|
167
|
+
- Monitor `weft.spawn.requests` for new tasks
|
|
168
|
+
- Launch child task processes
|
|
169
|
+
- Track process lifecycle
|
|
170
|
+
- Auto-terminate after idle timeout (default 600 seconds)
|
|
171
|
+
- Launch autostart tasks on boot
|
|
172
|
+
|
|
173
|
+
### Process Titles
|
|
174
|
+
|
|
175
|
+
Tasks update their process title for observability:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
$ ps aux | grep weft
|
|
179
|
+
weft-proj-0161024:mytask:running
|
|
180
|
+
weft-proj-0161025:worker:completed
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Format: `weft-{context_short}-{short_tid}:{name}:{status}[:details]`
|
|
184
|
+
|
|
185
|
+
## Command Reference
|
|
186
|
+
|
|
187
|
+
### Project Management
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Initialize new project
|
|
191
|
+
weft init [--autostart/--no-autostart]
|
|
192
|
+
|
|
193
|
+
# Show system status
|
|
194
|
+
weft status [--json]
|
|
195
|
+
|
|
196
|
+
# Task detail view
|
|
197
|
+
weft task status TID [--process] [--watch] [--json]
|
|
198
|
+
|
|
199
|
+
# List tasks
|
|
200
|
+
weft list [--stats] [--status STATUS] [--json]
|
|
201
|
+
|
|
202
|
+
# System maintenance
|
|
203
|
+
weft system tidy
|
|
204
|
+
weft system dump -o FILE
|
|
205
|
+
weft system load -i FILE
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Task Execution
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Run command
|
|
212
|
+
weft run COMMAND [args...]
|
|
213
|
+
weft run --spec NAME|PATH
|
|
214
|
+
weft run --pipeline NAME|PATH
|
|
215
|
+
weft run --function module:func [--arg VALUE] [--kw KEY=VALUE]
|
|
216
|
+
|
|
217
|
+
# Execution options
|
|
218
|
+
--wait # Wait for completion
|
|
219
|
+
--timeout N # Timeout in seconds
|
|
220
|
+
--memory N # Memory limit in MB
|
|
221
|
+
--cpu N # CPU limit (percentage)
|
|
222
|
+
--env KEY=VALUE # Environment variable
|
|
223
|
+
--autostart/--no-autostart # Enable/disable autostart manifests
|
|
224
|
+
--arg VALUE # Positional arg for --function (repeatable)
|
|
225
|
+
--kw KEY=VALUE # Keyword arg for --function (repeatable)
|
|
226
|
+
|
|
227
|
+
# Get results
|
|
228
|
+
weft result TID [--timeout N] [--stream] [--json]
|
|
229
|
+
weft result --all [--peek]
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Queue Operations
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Direct queue access
|
|
236
|
+
weft queue read QUEUE [--json] [--all]
|
|
237
|
+
weft queue write QUEUE MESSAGE
|
|
238
|
+
weft queue peek QUEUE [--json] [--all]
|
|
239
|
+
weft queue move SOURCE DEST [--all]
|
|
240
|
+
weft queue list [--pattern PATTERN]
|
|
241
|
+
weft queue watch QUEUE [--json] [--peek]
|
|
242
|
+
|
|
243
|
+
# Broadcast and aliases
|
|
244
|
+
weft queue broadcast MESSAGE [--pattern GLOB]
|
|
245
|
+
weft queue alias add ALIAS TARGET
|
|
246
|
+
weft queue alias remove ALIAS
|
|
247
|
+
weft queue alias list [--target QUEUE]
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Autostart Tasks
|
|
251
|
+
|
|
252
|
+
Manifest files in `.weft/autostart/*.json` are automatically launched when the manager starts. Autostart targets must reference stored task specs or pipelines (no inline TaskSpecs).
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# Save a task spec
|
|
256
|
+
$ cat > .weft/tasks/queue-monitor.json <<EOF
|
|
257
|
+
{
|
|
258
|
+
"name": "queue-monitor",
|
|
259
|
+
"spec": {
|
|
260
|
+
"type": "function",
|
|
261
|
+
"function_target": "monitoring.watch_queues",
|
|
262
|
+
"timeout": null
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
EOF
|
|
266
|
+
|
|
267
|
+
# Create autostart manifest
|
|
268
|
+
$ cat > .weft/autostart/monitor.json <<EOF
|
|
269
|
+
{
|
|
270
|
+
"name": "queue-monitor",
|
|
271
|
+
"target": { "type": "task", "name": "queue-monitor" },
|
|
272
|
+
"policy": { "mode": "ensure" }
|
|
273
|
+
}
|
|
274
|
+
EOF
|
|
275
|
+
|
|
276
|
+
# Next manager start will launch it automatically
|
|
277
|
+
$ weft run echo "trigger manager"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Control autostart behavior:
|
|
281
|
+
- `weft init --no-autostart` - Skip autostart directory creation
|
|
282
|
+
- `weft run --no-autostart` - Skip launching autostart tasks
|
|
283
|
+
- `WEFT_AUTOSTART_TASKS=false` - Disable via environment
|
|
284
|
+
|
|
285
|
+
## TaskSpec Format
|
|
286
|
+
|
|
287
|
+
Tasks are configured with JSON specifications:
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"name": "process-data",
|
|
292
|
+
"spec": {
|
|
293
|
+
"type": "command",
|
|
294
|
+
"process_target": "python",
|
|
295
|
+
"args": ["process.py"],
|
|
296
|
+
"timeout": 300,
|
|
297
|
+
"limits": {
|
|
298
|
+
"memory_mb": 512,
|
|
299
|
+
"cpu_percent": 75,
|
|
300
|
+
"max_fds": 100
|
|
301
|
+
},
|
|
302
|
+
"env": {"LOG_LEVEL": "debug"},
|
|
303
|
+
"stream_output": true,
|
|
304
|
+
"cleanup_on_exit": true
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Spec fields:**
|
|
310
|
+
- `type`: `"command"` or `"function"`
|
|
311
|
+
- `process_target`: Command executable (for commands)
|
|
312
|
+
- `function_target`: Module:function string (for functions)
|
|
313
|
+
- `args`: Additional argv items (appended for commands, *args for functions)
|
|
314
|
+
- `keyword_args`: Keyword args for function targets
|
|
315
|
+
- `timeout`: Seconds (null for no timeout)
|
|
316
|
+
- `limits`: Resource constraints
|
|
317
|
+
- `env`: Environment variables
|
|
318
|
+
- `stream_output`: Enable output streaming
|
|
319
|
+
- `cleanup_on_exit`: Delete empty queues on completion (outbox retained until consumed)
|
|
320
|
+
- `weft_context`: Runtime-expanded project context (set by Manager)
|
|
321
|
+
|
|
322
|
+
**Runtime expansion:**
|
|
323
|
+
- TaskSpec templates omit `tid`, `io`, `state`, and `spec.weft_context`.
|
|
324
|
+
- The Manager expands these at spawn time. The spawn-request message ID becomes the task TID.
|
|
325
|
+
|
|
326
|
+
## State Tracking
|
|
327
|
+
|
|
328
|
+
All state changes are logged to `weft.log.tasks`:
|
|
329
|
+
|
|
330
|
+
```json
|
|
331
|
+
{
|
|
332
|
+
"event": "work_completed",
|
|
333
|
+
"tid": "1837025672140161024",
|
|
334
|
+
"tid_short": "0161024",
|
|
335
|
+
"status": "completed",
|
|
336
|
+
"timestamp": 1705329000123456789,
|
|
337
|
+
"taskspec": {...},
|
|
338
|
+
"task_pid": 12345,
|
|
339
|
+
"return_code": 0
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Events include:
|
|
344
|
+
- `task_initialized` - Task startup
|
|
345
|
+
- `work_started` - Processing begins
|
|
346
|
+
- `work_completed` - Success
|
|
347
|
+
- `work_failed` - Execution error
|
|
348
|
+
- `work_timeout` - Timeout exceeded
|
|
349
|
+
- `work_limit_violation` - Resource limit hit
|
|
350
|
+
- `control_*` - Control message handling
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
## Resource Monitoring
|
|
354
|
+
|
|
355
|
+
Tasks track resource usage with psutil:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
# Resource limits in TaskSpec
|
|
359
|
+
"limits": {
|
|
360
|
+
"memory_mb": 512, # Max memory
|
|
361
|
+
"cpu_percent": 75, # Max CPU (0-100)
|
|
362
|
+
"max_fds": 100, # Max file descriptors
|
|
363
|
+
"max_connections": 50 # Max network connections
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Violations trigger `work_limit_violation` events and task termination.
|
|
368
|
+
|
|
369
|
+
## Exit Codes
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
0 - Success
|
|
373
|
+
1 - General error
|
|
374
|
+
2 - Not found (task, queue, spec)
|
|
375
|
+
124 - Timeout
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Common Patterns
|
|
379
|
+
|
|
380
|
+
### Background Processing
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
# Launch task without waiting
|
|
384
|
+
$ weft run ./background-job.sh
|
|
385
|
+
|
|
386
|
+
# Check status later
|
|
387
|
+
$ weft status
|
|
388
|
+
Tasks: 1 running
|
|
389
|
+
|
|
390
|
+
# Get result when ready
|
|
391
|
+
$ weft result <tid>
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Pipeline Processing
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
# Task chain via queues
|
|
398
|
+
$ weft run --spec extract.json # Writes to "raw.data"
|
|
399
|
+
$ weft run --spec transform.json # Reads "raw.data", writes "clean.data"
|
|
400
|
+
$ weft run --spec load.json # Reads "clean.data"
|
|
401
|
+
|
|
402
|
+
# Or use queue operations directly
|
|
403
|
+
$ weft queue write input.queue "data.csv"
|
|
404
|
+
$ weft run --spec processor.json
|
|
405
|
+
$ weft queue read output.queue
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Persistent Watchers
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
# Save task spec
|
|
412
|
+
$ cat > .weft/tasks/file-watcher.json <<EOF
|
|
413
|
+
{
|
|
414
|
+
"name": "file-watcher",
|
|
415
|
+
"spec": {
|
|
416
|
+
"type": "function",
|
|
417
|
+
"function_target": "watchers.watch_directory",
|
|
418
|
+
"timeout": null
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
EOF
|
|
422
|
+
|
|
423
|
+
# Create autostart manifest
|
|
424
|
+
$ cat > .weft/autostart/file-watcher.json <<EOF
|
|
425
|
+
{
|
|
426
|
+
"name": "file-watcher",
|
|
427
|
+
"target": { "type": "task", "name": "file-watcher" },
|
|
428
|
+
"policy": { "mode": "ensure" }
|
|
429
|
+
}
|
|
430
|
+
EOF
|
|
431
|
+
|
|
432
|
+
# Starts automatically with manager
|
|
433
|
+
$ weft run echo "start"
|
|
434
|
+
|
|
435
|
+
# Verify running
|
|
436
|
+
$ ps aux | grep weft
|
|
437
|
+
weft-proj-1234567:file-watcher:running
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Resource-Constrained Execution
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
# Limit memory and CPU
|
|
444
|
+
$ weft run --memory 100 --cpu 25 ./memory-intensive.py
|
|
445
|
+
|
|
446
|
+
# With timeout
|
|
447
|
+
$ weft run --timeout 60 --memory 500 ./task.sh
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## Architecture
|
|
451
|
+
|
|
452
|
+
### Components
|
|
453
|
+
|
|
454
|
+
- **TaskSpec**: Validated task configuration with partial immutability
|
|
455
|
+
- **Manager**: Persistent worker process for task spawning
|
|
456
|
+
- **Consumer**: Task executor with reservation pattern
|
|
457
|
+
- **BaseTask**: Abstract base providing queue wiring and state tracking
|
|
458
|
+
- **TaskRunner**: Multiprocess execution wrapper with timeout/monitoring
|
|
459
|
+
- **ResourceMonitor**: psutil-based resource tracking and limit enforcement
|
|
460
|
+
|
|
461
|
+
### Task Lifecycle
|
|
462
|
+
|
|
463
|
+
```
|
|
464
|
+
1. CLI: weft run COMMAND
|
|
465
|
+
2. Manager auto-started if needed
|
|
466
|
+
3. TaskSpec template created and validated
|
|
467
|
+
4. Spawn request written to weft.spawn.requests (message ID becomes TID)
|
|
468
|
+
5. Manager expands TaskSpec and spawns Consumer process
|
|
469
|
+
6. Consumer reserves work from inbox
|
|
470
|
+
7. TaskRunner executes in child process
|
|
471
|
+
8. Output written to outbox
|
|
472
|
+
9. State logged to weft.log.tasks
|
|
473
|
+
10. CLI retrieves result
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Multiprocess Isolation
|
|
477
|
+
|
|
478
|
+
Tasks execute in separate processes using `multiprocessing.spawn`:
|
|
479
|
+
- Clean process environment
|
|
480
|
+
- No inherited state from parent
|
|
481
|
+
- Resource monitoring per process
|
|
482
|
+
- Crash isolation
|
|
483
|
+
- Timeout enforcement
|
|
484
|
+
|
|
485
|
+
## Development
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# Install development dependencies
|
|
489
|
+
uv sync --all-extras
|
|
490
|
+
|
|
491
|
+
# Run tests
|
|
492
|
+
uv run pytest
|
|
493
|
+
uv run pytest tests/tasks/
|
|
494
|
+
uv run pytest tests/cli/
|
|
495
|
+
|
|
496
|
+
# Linting
|
|
497
|
+
uv run ruff check weft tests
|
|
498
|
+
uv run ruff format weft tests
|
|
499
|
+
uv run mypy weft
|
|
500
|
+
|
|
501
|
+
# Build
|
|
502
|
+
uv build
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Configuration
|
|
506
|
+
|
|
507
|
+
Environment variables:
|
|
508
|
+
|
|
509
|
+
- `WEFT_MANAGER_LIFETIME_TIMEOUT` - Manager idle timeout (default: 600s)
|
|
510
|
+
- `WEFT_MANAGER_REUSE_ENABLED` - Keep manager running (default: true)
|
|
511
|
+
- `WEFT_AUTOSTART_TASKS` - Enable autostart (default: true)
|
|
512
|
+
|
|
513
|
+
## License
|
|
514
|
+
|
|
515
|
+
MIT
|