You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add `@trace` to a function. offwork captures its source, dependencies, and imports automatically.
11
-
Workers reconstruct and execute everything from scratch — no shared filesystem, no deployment pipeline.
12
-
Missing packages are installed on the fly.
11
+
Add `@offwork.task` to a function. offwork captures its source, all dependencies, and all imports automatically. Workers reconstruct and execute everything from scratch — no shared filesystem, no deployment pipeline. Missing packages are installed on the fly.
13
12
14
13
## Quick start
15
14
@@ -19,14 +18,14 @@ pip install offwork
19
18
20
19
```python
21
20
import asyncio, math, offwork
22
-
from offwork importtrace
21
+
importoffwork
23
22
24
23
offwork.connect("local://localhost:9748")
25
24
26
25
defadd(a, b):
27
26
return a + b
28
27
29
-
@trace
28
+
@offwork.task
30
29
defhypotenuse(a: float, b: float) -> float:
31
30
return math.sqrt(add(a**2, b**2))
32
31
@@ -36,14 +35,32 @@ async def main():
36
35
asyncio.run(main())
37
36
```
38
37
39
-
Only the entry point needs `@trace` — everything it calls is captured automatically.
38
+
Only the entry point needs `@offwork.task` — everything it calls is captured automatically.
40
39
41
40
```bash
42
41
offwork worker --backend local://localhost:9748 --tmp # start a worker
43
42
python my_script.py # → 5.0
44
43
```
45
44
46
-
For multi-machine, swap `local://` for `redis://` or an `https://` managed broker URL. That's it.
45
+
For multi-machine, swap `local://` for `redis://` or an `https://` managed broker URL.
Copy file name to clipboardExpand all lines: docs/AGENTS.md
+9-18Lines changed: 9 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,12 +6,12 @@ This file is a compact, technical orientation for AI coding assistants. For user
6
6
7
7
A Python package that serializes a function — its source, its dependency graph, its imports, its closure, its arguments — into a self-contained JSON envelope, ships it to a worker process, and executes it there. Workers need no prior knowledge of the user's codebase: they reconstruct source from the payload, `pip install` missing packages on the fly, `compile` + `exec` the result, and return the value.
8
8
9
-
Add `@trace` to one entry-point function. Call `await func.run(...)`. That is the entire surface area.
9
+
Add `@offwork.task` to one entry-point function. Call `await func.run(...)`. That is the entire surface area.
10
10
11
11
## Design goals
12
12
13
13
-**Zero deployment** — no shared filesystem, no image rebuilds, no code sync. The client ships everything the worker needs in one envelope.
14
-
-**Zero setup for users** — one decorator (`@trace`), one connect call. Workers auto-install missing third-party packages.
14
+
-**Zero setup for users** — one decorator (`@offwork.task`), one connect call. Workers auto-install missing third-party packages.
15
15
-**Zero hard dependencies** — offwork itself has no required runtime deps. `redis`, `aio-pika`, `docker` are optional extras loaded lazily.
16
16
-**Async-native** — all I/O (`Backend`, `Worker`, `Result`, `_venv`) is `asyncio`. Sync user functions run in `loop.run_in_executor`.
The `__all__` in [offwork/__init__.py](../offwork/__init__.py) is the public surface. Anything else is internal and subject to change. Notable exports:
`func.run`, `func.start`, `func.map`, `func.run_in`, `func.run_at`, `func.run_every` are attributes attached by `@trace` ([graph/decorator.py](../offwork/graph/decorator.py)).
133
+
`func.run`, `func.start`, `func.map`, `func.run_in`, `func.run_at`, `func.run_every` are attributes attached by `@offwork.task` ([graph/decorator.py](../offwork/graph/decorator.py)).
134
134
135
135
## Conventions and invariants
136
136
137
137
-**Async by default.** Every `Backend` method is `async def`. Adding a sync helper is a smell — use `loop.run_in_executor` only for unavoidable blocking calls (pip subprocess, sync user code).
138
138
-**No required runtime dependencies.**`redis`, `aio_pika`, `docker` are imported lazily inside the modules that need them. Do not move these imports to the top of any always-imported file.
139
139
-**Content hash excludes structural data.**`FunctionNode`'s hash includes `source`, `imports`, `closure_*`, `module_vars`, `class_*` but NOT `dependencies`. This is load-bearing for cache reuse — see [core/models.py](../offwork/core/models.py).
140
-
-**`@trace` is stripped from reconstructed source.** Reconstructed code must not import offwork. Anything that survives reconstruction must be in stdlib or installable via pip.
140
+
-**`@offwork.task` is stripped from reconstructed source.** Reconstructed code must not import offwork. Anything that survives reconstruction must be in stdlib or installable via pip.
141
141
-**Closure capture is multi-tier.** Order matters: `repr()` → traced refs → lambdas → user funcs → stdlib constructor expressions → pickle → warning. See [graph/analyzer.py](../offwork/graph/analyzer.py).
142
142
-**Auto-discovery is recursive.** Calling an untraced user function from a traced one registers it transitively. Cross-module imports become inline edges.
143
143
-**Backend defaults are no-ops.**`Backend` ABC supplies safe defaults for cancellation, progress, throttling, scheduling, notifications. Subclasses override only what they support.
@@ -147,7 +147,7 @@ The `__all__` in [offwork/__init__.py](../offwork/__init__.py) is the public sur
147
147
148
148
## Where things live (cheat-sheet for common edits)
149
149
150
-
- New decorator option (e.g. `@trace(priority=...)`) → [graph/decorator.py](../offwork/graph/decorator.py), [core/task.py](../offwork/core/task.py), `Worker.run_with_policy` in [worker/worker.py](../offwork/worker/worker.py).
150
+
- New decorator option (e.g. `@offwork.task(priority=...)`) → [graph/decorator.py](../offwork/graph/decorator.py), [core/task.py](../offwork/core/task.py), `Worker.run_with_policy` in [worker/worker.py](../offwork/worker/worker.py).
151
151
- New backend → subclass `Backend` in [worker/backends/base.py](../offwork/worker/backends/base.py), wire URL scheme in [worker/remote.py](../offwork/worker/remote.py).
152
152
- New auto-discovery rule → [graph/analyzer.py](../offwork/graph/analyzer.py); update reconstruction in [graph/store.py](../offwork/graph/store.py); add fields to `FunctionNode` in [core/models.py](../offwork/core/models.py) (remember the content-hash inclusion rule).
153
153
- New CLI subcommand → [offwork/__main__.py](../offwork/__main__.py).
@@ -170,13 +170,4 @@ pytest
170
170
mypy offwork
171
171
```
172
172
173
-
Worker logs are concise and structured. The first execution of a new graph shows `build` + any `pip <pkg>` annotations; repeats show `build` (cached venv) or `cached` (subgraph cache hit).
174
-
175
-
176
-
----
177
-
178
-
# Question
179
-
180
-
Can you make sure all the example scripts in `examples/` can be run standalone ? For example, the FastAPI example need the user to make a request. I want these examples to represent real situations, but I'd like the user to be able to run them and see the results immediately.
181
-
182
-
This means: generating image data and report data in the script directly, and making the request for the user.
173
+
Worker logs are concise and structured. The first execution of a new graph shows `build` + any `pip <pkg>` annotations; repeats show `build` (cached venv) or `cached` (subgraph cache hit).
`start_at` and `start_in` return a `Result` handle (like `.start()`).
81
+
`run_at` and `run_in` return a `Result` handle (like `.start()`).
83
82
84
83
## Throttling
85
84
@@ -88,7 +87,7 @@ Rate-limit how often a function can be executed:
88
87
```python
89
88
from datetime import timedelta
90
89
91
-
@trace(throttle=timedelta(hours=24) /50) # ~29 min cooldown
90
+
@offwork.task(throttle=timedelta(hours=24) /50) # ~29 min cooldown
92
91
defexpensive_api_call(query: str) -> str: ...
93
92
```
94
93
@@ -121,7 +120,7 @@ with worker_only_import("opencv-python-headless"):
121
120
import cv2
122
121
```
123
122
124
-
The local `requests` and `cv2` resolve to lightweight stubs. They're fine to reference inside a `@trace` function (the worker re-imports them for real), but raise `WorkerOnlyError` if used directly on the client.
123
+
The local `requests` and `cv2` resolve to lightweight stubs. They're fine to reference inside a `@offwork.task` function (the worker re-imports them for real), but raise `WorkerOnlyError` if used directly on the client.
125
124
126
125
Only the names imported literally inside the `with` block are stubbed — real installed packages and their transitive imports are unaffected.
127
126
@@ -131,7 +130,7 @@ Only the names imported literally inside the `with` block are stubbed — real i
131
130
from offwork import progress, TaskCancelled, RemoteError, TaskStalled
132
131
133
132
# Inside a task — report progress (no-op when called locally)
0 commit comments