pugmark 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.
- pugmark-0.1.0/.gitignore +79 -0
- pugmark-0.1.0/PKG-INFO +155 -0
- pugmark-0.1.0/README.md +130 -0
- pugmark-0.1.0/pugmark/__init__.py +1676 -0
- pugmark-0.1.0/pugmark/http.py +240 -0
- pugmark-0.1.0/pugmark/protoipc.py +371 -0
- pugmark-0.1.0/pugmark/py.typed +0 -0
- pugmark-0.1.0/pyproject.toml +96 -0
- pugmark-0.1.0/tests/__init__.py +1 -0
- pugmark-0.1.0/tests/test_http.py +42 -0
- pugmark-0.1.0/tests/test_protoipc.py +225 -0
- pugmark-0.1.0/tests/test_pugmark.py +2564 -0
pugmark-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
share/python-wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg
|
|
23
|
+
MANIFEST
|
|
24
|
+
|
|
25
|
+
# PyInstaller
|
|
26
|
+
*.manifest
|
|
27
|
+
*.spec
|
|
28
|
+
|
|
29
|
+
# Unit test / coverage reports
|
|
30
|
+
htmlcov/
|
|
31
|
+
.tox/
|
|
32
|
+
.nox/
|
|
33
|
+
.coverage
|
|
34
|
+
.coverage.*
|
|
35
|
+
.cache
|
|
36
|
+
nosetests.xml
|
|
37
|
+
coverage.xml
|
|
38
|
+
*.cover
|
|
39
|
+
*.py,cover
|
|
40
|
+
.hypothesis/
|
|
41
|
+
.pytest_cache/
|
|
42
|
+
cover/
|
|
43
|
+
|
|
44
|
+
# Environments
|
|
45
|
+
.env
|
|
46
|
+
.venv
|
|
47
|
+
env/
|
|
48
|
+
venv/
|
|
49
|
+
ENV/
|
|
50
|
+
env.bak/
|
|
51
|
+
venv.bak/
|
|
52
|
+
|
|
53
|
+
# mypy
|
|
54
|
+
.mypy_cache/
|
|
55
|
+
.dmypy.json
|
|
56
|
+
dmypy.json
|
|
57
|
+
|
|
58
|
+
# Ruff
|
|
59
|
+
.ruff_cache/
|
|
60
|
+
|
|
61
|
+
# uv
|
|
62
|
+
.uv/
|
|
63
|
+
uv.lock
|
|
64
|
+
|
|
65
|
+
# IDEs
|
|
66
|
+
.vscode/
|
|
67
|
+
.idea/
|
|
68
|
+
*.swp
|
|
69
|
+
*.swo
|
|
70
|
+
*~
|
|
71
|
+
|
|
72
|
+
# OS
|
|
73
|
+
.DS_Store
|
|
74
|
+
.DS_Store?
|
|
75
|
+
._*
|
|
76
|
+
.Spotlight-V100
|
|
77
|
+
.Trashes
|
|
78
|
+
ehthumbs.db
|
|
79
|
+
Thumbs.db
|
pugmark-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pugmark
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Pugmark — the durable agent runtime built on object storage
|
|
5
|
+
Project-URL: Homepage, https://github.com/firetiger-oss/pugmark
|
|
6
|
+
Project-URL: Repository, https://github.com/firetiger-oss/pugmark
|
|
7
|
+
Author: Firetiger
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: protobuf>=4.0.0
|
|
19
|
+
Requires-Dist: zstandard>=0.19.0
|
|
20
|
+
Provides-Extra: otel
|
|
21
|
+
Requires-Dist: opentelemetry-api>=1.20.0; extra == 'otel'
|
|
22
|
+
Requires-Dist: opentelemetry-instrumentation-urllib>=0.40b0; extra == 'otel'
|
|
23
|
+
Requires-Dist: opentelemetry-sdk>=1.20.0; extra == 'otel'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Pugmark Python SDK
|
|
27
|
+
|
|
28
|
+
Python SDK for writing [Pugmark](https://github.com/firetiger-oss/pugmark) handlers. A handler is a process the Pugmark runtime invokes on each new event in a session; it reads inputs from stdin, fetches object bodies over HTTP, and writes outputs back to stdout.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install pugmark
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Minimal handler
|
|
37
|
+
|
|
38
|
+
A pugmark session is an append-only log of JSON events. On every invocation pugmark feeds the handler the full log on stdin. Walk it, decide one next event, append it.
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import pugmark
|
|
42
|
+
|
|
43
|
+
log = list(pugmark.read())
|
|
44
|
+
last = log[-1].decode_json() if log else None
|
|
45
|
+
match last:
|
|
46
|
+
case {"kind": "input", "text": text}:
|
|
47
|
+
pugmark.write({"kind": "echo", "text": text.upper()})
|
|
48
|
+
case _:
|
|
49
|
+
pugmark.pause("nothing to echo")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Run it under the Pugmark CLI:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pugmark start -b file:///tmp/pug
|
|
56
|
+
echo '{"kind":"input","text":"hello"}' | pugmark push
|
|
57
|
+
pugmark run --once -- python handler.py
|
|
58
|
+
pugmark log -A # input event, then echo event ({"text":"HELLO"})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Writing outputs
|
|
62
|
+
|
|
63
|
+
`pugmark.write` accepts either a `pugmark.Object` or a raw Python value. Content type is auto-detected unless overridden.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
pugmark.write("hello", name="reply") # text/plain
|
|
67
|
+
pugmark.write({"answer": 42}, name="result") # application/json
|
|
68
|
+
pugmark.write(b"\x00\x01", name="bin") # application/octet-stream
|
|
69
|
+
pugmark.write("# Title", name="doc", content_type="text/markdown")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Explicit helpers when you'd rather not rely on type dispatch:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
pugmark.write_text("hello", name="reply")
|
|
76
|
+
pugmark.write_json({"answer": 42}, name="result")
|
|
77
|
+
pugmark.write_bytes(b"\x00\x01", name="bin")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Reading inputs
|
|
81
|
+
|
|
82
|
+
`pugmark.read()` walks the full session log in order — including any inherited parent-session entries. This is the primary primitive for log-replay handlers; iterate it once per invocation to reconstruct everything you need.
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
for obj in pugmark.read():
|
|
86
|
+
event = obj.decode_json() # every entry is JSON
|
|
87
|
+
fold(state, event) # accumulate whatever the handler needs
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
For ad-hoc named lookups (slot-filling workflows where each name appears at most once), `pugmark.state()` returns a dict-like view of the session keyed by object name. Note that names are **collapsing** — only the most recently written object per name survives — so this isn't appropriate for conversations or any log with repeated entry kinds.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
state = pugmark.state() # dict-like; one entry per unique name
|
|
94
|
+
if "config" in state:
|
|
95
|
+
cfg = state["config"].decode_json()
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Named loads with the right decoder built in:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
text = pugmark.load_text("greeting")
|
|
102
|
+
reply = pugmark.load_json("result")
|
|
103
|
+
blob = pugmark.load_bytes("bin")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Object methods
|
|
107
|
+
|
|
108
|
+
Every object (local or remote) supports:
|
|
109
|
+
|
|
110
|
+
| Method | Returns |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `body()` | raw bytes (auto-decompressed) |
|
|
113
|
+
| `content_type()` | MIME type string |
|
|
114
|
+
| `name()` | optional name set by the writer |
|
|
115
|
+
| `metadata()` | `Dict[str, str]` |
|
|
116
|
+
| `decode_text()` | UTF-8 decoded `str` |
|
|
117
|
+
| `decode_json()` | parsed JSON value |
|
|
118
|
+
| `decode_data()` | raw bytes |
|
|
119
|
+
|
|
120
|
+
## Control flow
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
pugmark.pause("waiting for next user message") # suspend until next event
|
|
124
|
+
pugmark.sleep(60, reason="rate-limited") # suspend for 60 seconds
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Events
|
|
128
|
+
|
|
129
|
+
Each handler invocation is triggered by an event. Iterate `pugmark.events()` to inspect:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
for event in pugmark.events():
|
|
133
|
+
match event:
|
|
134
|
+
case pugmark.StartEvent(): ... # new root session
|
|
135
|
+
case pugmark.ForkEvent(): ... # forked from a parent
|
|
136
|
+
case pugmark.WakeEvent(): ... # resumed from pause/sleep
|
|
137
|
+
case pugmark.PushEvent() as e: ... # object was pushed; e.object is the Object
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## OpenTelemetry
|
|
141
|
+
|
|
142
|
+
The SDK ships optional OpenTelemetry trace context propagation. Install with the `otel` extra to enable:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pip install 'pugmark[otel]'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Requirements
|
|
149
|
+
|
|
150
|
+
- Python 3.11+
|
|
151
|
+
- `zstandard` (installed automatically)
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
Apache License 2.0.
|
pugmark-0.1.0/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Pugmark Python SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for writing [Pugmark](https://github.com/firetiger-oss/pugmark) handlers. A handler is a process the Pugmark runtime invokes on each new event in a session; it reads inputs from stdin, fetches object bodies over HTTP, and writes outputs back to stdout.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install pugmark
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Minimal handler
|
|
12
|
+
|
|
13
|
+
A pugmark session is an append-only log of JSON events. On every invocation pugmark feeds the handler the full log on stdin. Walk it, decide one next event, append it.
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import pugmark
|
|
17
|
+
|
|
18
|
+
log = list(pugmark.read())
|
|
19
|
+
last = log[-1].decode_json() if log else None
|
|
20
|
+
match last:
|
|
21
|
+
case {"kind": "input", "text": text}:
|
|
22
|
+
pugmark.write({"kind": "echo", "text": text.upper()})
|
|
23
|
+
case _:
|
|
24
|
+
pugmark.pause("nothing to echo")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Run it under the Pugmark CLI:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pugmark start -b file:///tmp/pug
|
|
31
|
+
echo '{"kind":"input","text":"hello"}' | pugmark push
|
|
32
|
+
pugmark run --once -- python handler.py
|
|
33
|
+
pugmark log -A # input event, then echo event ({"text":"HELLO"})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Writing outputs
|
|
37
|
+
|
|
38
|
+
`pugmark.write` accepts either a `pugmark.Object` or a raw Python value. Content type is auto-detected unless overridden.
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
pugmark.write("hello", name="reply") # text/plain
|
|
42
|
+
pugmark.write({"answer": 42}, name="result") # application/json
|
|
43
|
+
pugmark.write(b"\x00\x01", name="bin") # application/octet-stream
|
|
44
|
+
pugmark.write("# Title", name="doc", content_type="text/markdown")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Explicit helpers when you'd rather not rely on type dispatch:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
pugmark.write_text("hello", name="reply")
|
|
51
|
+
pugmark.write_json({"answer": 42}, name="result")
|
|
52
|
+
pugmark.write_bytes(b"\x00\x01", name="bin")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Reading inputs
|
|
56
|
+
|
|
57
|
+
`pugmark.read()` walks the full session log in order — including any inherited parent-session entries. This is the primary primitive for log-replay handlers; iterate it once per invocation to reconstruct everything you need.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
for obj in pugmark.read():
|
|
61
|
+
event = obj.decode_json() # every entry is JSON
|
|
62
|
+
fold(state, event) # accumulate whatever the handler needs
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For ad-hoc named lookups (slot-filling workflows where each name appears at most once), `pugmark.state()` returns a dict-like view of the session keyed by object name. Note that names are **collapsing** — only the most recently written object per name survives — so this isn't appropriate for conversations or any log with repeated entry kinds.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
state = pugmark.state() # dict-like; one entry per unique name
|
|
69
|
+
if "config" in state:
|
|
70
|
+
cfg = state["config"].decode_json()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Named loads with the right decoder built in:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
text = pugmark.load_text("greeting")
|
|
77
|
+
reply = pugmark.load_json("result")
|
|
78
|
+
blob = pugmark.load_bytes("bin")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Object methods
|
|
82
|
+
|
|
83
|
+
Every object (local or remote) supports:
|
|
84
|
+
|
|
85
|
+
| Method | Returns |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `body()` | raw bytes (auto-decompressed) |
|
|
88
|
+
| `content_type()` | MIME type string |
|
|
89
|
+
| `name()` | optional name set by the writer |
|
|
90
|
+
| `metadata()` | `Dict[str, str]` |
|
|
91
|
+
| `decode_text()` | UTF-8 decoded `str` |
|
|
92
|
+
| `decode_json()` | parsed JSON value |
|
|
93
|
+
| `decode_data()` | raw bytes |
|
|
94
|
+
|
|
95
|
+
## Control flow
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
pugmark.pause("waiting for next user message") # suspend until next event
|
|
99
|
+
pugmark.sleep(60, reason="rate-limited") # suspend for 60 seconds
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Events
|
|
103
|
+
|
|
104
|
+
Each handler invocation is triggered by an event. Iterate `pugmark.events()` to inspect:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
for event in pugmark.events():
|
|
108
|
+
match event:
|
|
109
|
+
case pugmark.StartEvent(): ... # new root session
|
|
110
|
+
case pugmark.ForkEvent(): ... # forked from a parent
|
|
111
|
+
case pugmark.WakeEvent(): ... # resumed from pause/sleep
|
|
112
|
+
case pugmark.PushEvent() as e: ... # object was pushed; e.object is the Object
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## OpenTelemetry
|
|
116
|
+
|
|
117
|
+
The SDK ships optional OpenTelemetry trace context propagation. Install with the `otel` extra to enable:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pip install 'pugmark[otel]'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Requirements
|
|
124
|
+
|
|
125
|
+
- Python 3.11+
|
|
126
|
+
- `zstandard` (installed automatically)
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
Apache License 2.0.
|