programasweights 0.2.7__tar.gz → 0.3.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.
- {programasweights-0.2.7 → programasweights-0.3.0}/AGENTS.md +28 -9
- {programasweights-0.2.7 → programasweights-0.3.0}/PKG-INFO +1 -1
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/001-llama-cpp-over-pytorch.md +1 -1
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/api-reference/cli.md +1 -1
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/api-reference/python-sdk.md +1 -1
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/api-reference/rest-api.md +10 -2
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/architecture.md +1 -1
- programasweights-0.3.0/docs/case-studies/log-monitoring.md +132 -0
- programasweights-0.3.0/docs/case-studies/tool-calling.md +477 -0
- programasweights-0.3.0/docs/guide/writing-good-specs.md +11 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/mkdocs.yml +2 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/__init__.py +76 -10
- programasweights-0.3.0/programasweights/_output.py +34 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/cache.py +7 -4
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/client.py +24 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/runtime_llamacpp.py +4 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/pyproject.toml +1 -1
- programasweights-0.2.7/docs/guide/writing-good-specs.md +0 -36
- {programasweights-0.2.7 → programasweights-0.3.0}/.gitignore +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/.readthedocs.yaml +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/CHANGELOG.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/LICENSE +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/PYPI_README.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/README.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/002-q4_0-adapter-format.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/003-single-spec-field.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/004-compiler-naming.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/005-vllm-hidden-states.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/006-email-api-key-auth.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/advanced/adrs.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/advanced/architecture.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/case-studies/semantic-search.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/case-studies/site-navigation.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/getting-started/first-program.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/getting-started/installation.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/getting-started/naming-programs.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/guide/browser-inference.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/guide/how-it-works.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/guide/local-inference.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/hub/browsing-programs.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/hub/feedback-cases.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/hub/publishing-programs.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/index.md +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/docs/requirements.txt +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/examples/flask_app.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/examples/jupyter_notebook.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/examples/langchain_integration.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/examples/paw_monitor.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/examples/replace_openai.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/artifacts.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/cli.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/compiler/__init__.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/compiler/dummy.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/config.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/convert_peft_to_paw.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/paw_format.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/runtime/__init__.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/runtime/interpreter.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/runtime/interpreter_onnx.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/tests/test_offline_cache.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/tests/test_sdk.py +0 -0
- {programasweights-0.2.7 → programasweights-0.3.0}/tests/test_sdk.sh +0 -0
|
@@ -78,6 +78,11 @@ Output: delete
|
|
|
78
78
|
- **Include examples from your actual data**: Examples outperform prose-only descriptions.
|
|
79
79
|
- **Debug failures before sweeping**: Look at specific failing examples and understand WHY before trying many variants.
|
|
80
80
|
|
|
81
|
+
## Context Window
|
|
82
|
+
|
|
83
|
+
- Spec + input + output share a ~2048 token context window. Inputs that exceed it will error.
|
|
84
|
+
- `max_tokens` defaults to `None`: generation runs until EOS or the context limit.
|
|
85
|
+
|
|
81
86
|
## Chaining Functions
|
|
82
87
|
|
|
83
88
|
Multiple PAW functions can be composed for multi-step tasks:
|
|
@@ -93,11 +98,7 @@ if label != "other":
|
|
|
93
98
|
|
|
94
99
|
Chain them with regular Python logic.
|
|
95
100
|
|
|
96
|
-
##
|
|
97
|
-
|
|
98
|
-
Production examples with iterative spec-tuning walkthroughs: [site navigation](https://programasweights.readthedocs.io/en/latest/case-studies/site-navigation/) (5-program pipeline, Cmd+K helper) and [semantic search](https://programasweights.readthedocs.io/en/latest/case-studies/semantic-search/) (reranking without a vector database).
|
|
99
|
-
|
|
100
|
-
## Event-Driven Monitoring
|
|
101
|
+
## Log Monitoring
|
|
101
102
|
|
|
102
103
|
PAW functions can classify log output. Compile once with examples from your specific logs, then reuse the function locally forever:
|
|
103
104
|
|
|
@@ -160,6 +161,22 @@ Generate API keys at https://programasweights.com/settings.
|
|
|
160
161
|
|
|
161
162
|
Commands: `paw compile --spec "..." --json`, `paw run --program <id> --input "..."`, `paw info <id>`, `paw rename <id> <slug>`, `paw login`. All support `--json` for structured output.
|
|
162
163
|
|
|
164
|
+
## Versioning
|
|
165
|
+
|
|
166
|
+
Slugs support version history. Recompiling with the same slug creates a new version:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
p1 = paw.compile("Count words v1", slug="word-counter") # v1
|
|
170
|
+
p2 = paw.compile("Count words v2", slug="word-counter") # v2 (auto-bumps)
|
|
171
|
+
|
|
172
|
+
fn = paw.function("da03/word-counter") # resolves to main (latest)
|
|
173
|
+
fn = paw.function("da03/word-counter@v1") # pinned to v1
|
|
174
|
+
|
|
175
|
+
versions = paw.list_versions("da03/word-counter") # all versions
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Pinned versions (`@v1`) are immutable and cached locally forever. Bare slugs always check the server for the latest main version (falls back to cache if offline).
|
|
179
|
+
|
|
163
180
|
## Full API Reference
|
|
164
181
|
|
|
165
182
|
```python
|
|
@@ -169,16 +186,19 @@ program = paw.compile(
|
|
|
169
186
|
slug=None, # URL-safe handle (requires auth)
|
|
170
187
|
public=True, # list on public hub
|
|
171
188
|
)
|
|
172
|
-
# Returns: Program(id, slug, status, timings, error)
|
|
189
|
+
# Returns: Program(id, slug, status, version, version_action, timings, error)
|
|
173
190
|
|
|
174
191
|
fn = paw.function(program) # accepts Program object, hash ID, or slug
|
|
175
192
|
fn = paw.function("a6b454023d41ac9ca845")
|
|
176
193
|
fn = paw.function("da03/my-classifier")
|
|
194
|
+
fn = paw.function("da03/my-classifier@v2") # pinned version
|
|
195
|
+
fn = paw.function("da03/my-classifier", offline=True) # skip server check
|
|
177
196
|
|
|
178
197
|
result: str = fn(input_text: str, max_tokens=None, temperature=0.0)
|
|
179
198
|
|
|
180
199
|
fn = paw.compile_and_load(spec, compiler="paw-4b-qwen3-0.6b")
|
|
181
200
|
|
|
201
|
+
versions = paw.list_versions("da03/my-classifier") # version history
|
|
182
202
|
programs = paw.list_programs(sort="recent", per_page=20) # requires auth
|
|
183
203
|
|
|
184
204
|
paw.login()
|
|
@@ -199,8 +219,7 @@ paw.login()
|
|
|
199
219
|
- **Cache**: `~/.cache/programasweights/`. Override with `PAW_CACHE_DIR`.
|
|
200
220
|
- **Offline** after first download.
|
|
201
221
|
|
|
202
|
-
##
|
|
222
|
+
## Case Studies
|
|
203
223
|
|
|
204
|
-
|
|
205
|
-
- `max_tokens` defaults to `None`: generation runs until EOS or the context limit.
|
|
224
|
+
Detailed walkthroughs of building production systems with PAW, including what we tried and what we learned: [log monitoring](https://programasweights.readthedocs.io/en/latest/case-studies/log-monitoring/), [site navigation](https://programasweights.readthedocs.io/en/latest/case-studies/site-navigation/), [semantic search](https://programasweights.readthedocs.io/en/latest/case-studies/semantic-search/), [tool calling](https://programasweights.readthedocs.io/en/latest/case-studies/tool-calling/).
|
|
206
225
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: programasweights
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Compile natural language specifications into neural programs that run locally via llama.cpp.
|
|
5
5
|
Project-URL: Homepage, https://programasweights.com
|
|
6
6
|
Project-URL: Repository, https://github.com/programasweights/programasweights-python
|
|
@@ -17,5 +17,5 @@ Replace the PyTorch runtime with llama-cpp-python (~80MB install). Use GGUF mode
|
|
|
17
17
|
- Inference uses Metal (Mac), CPU (Linux/Windows) — no GPU required
|
|
18
18
|
- Must pre-render chat templates server-side (no transformers tokenizer on client)
|
|
19
19
|
- .paw format must change from v2 (safetensors) to v3 (GGUF adapter)
|
|
20
|
-
- Base model is downloaded once (~
|
|
20
|
+
- Base model is downloaded once (~594 MB for Q6_K) and shared across all functions
|
|
21
21
|
- Per-function adapter download is ~23MB (Q4_0, confirmed lossless at 4096-scale eval)
|
|
@@ -57,7 +57,7 @@ paw rename <program_id> <new-slug> [--json]
|
|
|
57
57
|
| Argument | Description |
|
|
58
58
|
|----------|-------------|
|
|
59
59
|
| `program_id` | Program hash ID or current slug. |
|
|
60
|
-
| `new-slug` | New slug name (2-50 chars, lowercase alphanumeric and hyphens). |
|
|
60
|
+
| `new-slug` | New slug name (2-50 chars, lowercase alphanumeric and hyphens). Pass `""` to remove. |
|
|
61
61
|
|
|
62
62
|
## `paw info`
|
|
63
63
|
|
|
@@ -41,7 +41,7 @@ output: str = fn(input_text, max_tokens=None, temperature=0.0)
|
|
|
41
41
|
| `max_tokens` | Maximum tokens to generate. `None` (default) = use all remaining context window. |
|
|
42
42
|
| `temperature` | Sampling temperature (default `0.0`). |
|
|
43
43
|
|
|
44
|
-
**Context limits:** Spec + input + output share a ~2048 token window.
|
|
44
|
+
**Context limits:** Spec + input + output share a ~2048 token window. Inputs that exceed it will error. `max_tokens` defaults to `None`: generation runs until EOS or the context limit.
|
|
45
45
|
|
|
46
46
|
## `paw.compile`
|
|
47
47
|
|
|
@@ -34,7 +34,11 @@ Compile a specification.
|
|
|
34
34
|
| `name` | string | No | Display title. Auto-generated if omitted. |
|
|
35
35
|
| `tags` | string[] | No | Tags for discovery. |
|
|
36
36
|
|
|
37
|
-
**Response
|
|
37
|
+
**Response:**
|
|
38
|
+
- **202 Accepted** on success — JSON with `job_id`, `status`, `program_id`, `slug` (if created), `pseudo_program`, and `timings`
|
|
39
|
+
- **400** — unknown compiler
|
|
40
|
+
- **422** — spec too short, too long, or token limit exceeded
|
|
41
|
+
- **500** — compilation failed (server error)
|
|
38
42
|
|
|
39
43
|
### `POST /infer`
|
|
40
44
|
|
|
@@ -88,7 +92,11 @@ Owner-only fields: `public`, `name`, `tags`. Any authenticated user can set thei
|
|
|
88
92
|
|
|
89
93
|
### `GET /programs/{id_or_slug}/download`
|
|
90
94
|
|
|
91
|
-
|
|
95
|
+
Downloads the `.paw` artifact. Returns one of:
|
|
96
|
+
- **302** redirect to HuggingFace CDN (program already uploaded)
|
|
97
|
+
- **200** file from server (freshly compiled, not yet on HF)
|
|
98
|
+
- **202 Accepted** with `Retry-After` header (program assets still generating, retry after the indicated seconds)
|
|
99
|
+
- **404** if program not found or private and not owned by the requester
|
|
92
100
|
|
|
93
101
|
### `GET /programs/resolve/{slug}`
|
|
94
102
|
|
|
@@ -31,7 +31,7 @@ vLLM multi-LoRA serving on GPU 2 (or GPU 1 in 2-GPU config). Each compiled progr
|
|
|
31
31
|
|
|
32
32
|
## Inference (Client-side SDK)
|
|
33
33
|
|
|
34
|
-
llama-cpp-python loads the base GGUF model (
|
|
34
|
+
llama-cpp-python loads the base GGUF model (594 MB, downloaded once) and hot-swaps Q4_0 adapters per function call. No PyTorch or transformers required.
|
|
35
35
|
|
|
36
36
|
## GPU Assignment
|
|
37
37
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Event-Driven Log Monitoring
|
|
2
|
+
|
|
3
|
+
Long-running processes -- training runs, deployments, data pipelines -- produce thousands of log lines. You want to know when something important happens (checkpoint saved, error, completion) without watching the terminal. PAW lets you compile a classifier that runs locally and alerts on the lines that matter.
|
|
4
|
+
|
|
5
|
+
**Full tool:** [examples/paw_monitor.py](https://github.com/programasweights/programasweights-python/blob/main/examples/paw_monitor.py) on GitHub.
|
|
6
|
+
|
|
7
|
+
## How we built it
|
|
8
|
+
|
|
9
|
+
### Attempt 1: Keyword matching
|
|
10
|
+
|
|
11
|
+
The obvious first approach: grep for "error", "fail", "complete".
|
|
12
|
+
|
|
13
|
+
**Result:** Too noisy. "error" matches "error_count=0" (routine metric). "complete" matches "batch complete" (every 10 seconds). Missing important lines like "Traceback" or "CUDA out of memory" that don't contain the keywords.
|
|
14
|
+
|
|
15
|
+
**Lesson:** The whole reason to use PAW is that keyword matching can't express "important enough to interrupt me."
|
|
16
|
+
|
|
17
|
+
### Attempt 2: One-shot description
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
Classify if this log line is important enough to alert on. Return ALERT or QUIET.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Result:** Too vague. The model alerted on everything that looked unusual, including routine debug output. "Important" means different things in different contexts.
|
|
24
|
+
|
|
25
|
+
**Lesson:** Don't rely on abstract instructions. The model needs concrete examples of what ALERT and QUIET look like in YOUR specific logs.
|
|
26
|
+
|
|
27
|
+
### Attempt 3: Example-based spec
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Classify log lines. Return ONLY one word: ALERT or QUIET.
|
|
31
|
+
|
|
32
|
+
Input: [step 100] loss=0.05 lr=0.0001
|
|
33
|
+
Output: QUIET
|
|
34
|
+
|
|
35
|
+
Input: [Checkpoint] Saved model at step 1000
|
|
36
|
+
Output: ALERT
|
|
37
|
+
|
|
38
|
+
Input: Traceback (most recent call last):
|
|
39
|
+
Output: ALERT
|
|
40
|
+
|
|
41
|
+
Input: Training complete. Final loss: 0.11
|
|
42
|
+
Output: ALERT
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Result:** This worked. The model learned the boundary between routine metrics (QUIET) and significant events (ALERT) from the examples. Adding 3-5 examples from actual logs was the key.
|
|
46
|
+
|
|
47
|
+
**Lesson:** Include examples from your actual data. 3-5 representative examples consistently outperform prose-only descriptions.
|
|
48
|
+
|
|
49
|
+
### Refinement: Developer feedback
|
|
50
|
+
|
|
51
|
+
A developer using PAW for monitoring diffusion model training shared their experience. Key insights:
|
|
52
|
+
|
|
53
|
+
- **Positive instructions work better than negative ones.** "Output ALERT only if X, Y, or Z" is more reliable than "Don't alert on routine output."
|
|
54
|
+
- **The spec should match the monitoring context.** Different training phases produce different log patterns. Examples should cover the specific domain.
|
|
55
|
+
- **Stall detection needs a separate mechanism.** PAW classifies what it sees -- it can't detect the absence of output. The monitoring tool needs a timer for "no new output in N minutes."
|
|
56
|
+
|
|
57
|
+
## The solution
|
|
58
|
+
|
|
59
|
+
### The spec
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
program = paw.compile("""
|
|
63
|
+
Classify log lines. Return ONLY one word: ALERT or QUIET.
|
|
64
|
+
|
|
65
|
+
Input: [step 100] loss=0.05 lr=0.0001
|
|
66
|
+
Output: QUIET
|
|
67
|
+
|
|
68
|
+
Input: [Checkpoint] Saved model at step 1000
|
|
69
|
+
Output: ALERT
|
|
70
|
+
|
|
71
|
+
Input: Traceback (most recent call last):
|
|
72
|
+
Output: ALERT
|
|
73
|
+
|
|
74
|
+
Input: Training complete. Final loss: 0.11
|
|
75
|
+
Output: ALERT
|
|
76
|
+
""")
|
|
77
|
+
|
|
78
|
+
fn = paw.function(program.id)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Compile once, save the program ID, reuse forever. The function runs locally with no internet after the first download.
|
|
82
|
+
|
|
83
|
+
### The monitoring loop
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import time
|
|
87
|
+
|
|
88
|
+
fn = paw.function("your-program-id")
|
|
89
|
+
|
|
90
|
+
last_size = 0
|
|
91
|
+
while True:
|
|
92
|
+
size = os.path.getsize(log_file)
|
|
93
|
+
if size > last_size:
|
|
94
|
+
with open(log_file) as f:
|
|
95
|
+
f.seek(last_size)
|
|
96
|
+
new_text = f.read()
|
|
97
|
+
last_size = size
|
|
98
|
+
|
|
99
|
+
# Truncate to last ~1000 chars to fit context window
|
|
100
|
+
chunk = new_text[-1000:] if len(new_text) > 1000 else new_text
|
|
101
|
+
result = fn(chunk)
|
|
102
|
+
if result.strip() == "ALERT":
|
|
103
|
+
send_notification(chunk)
|
|
104
|
+
|
|
105
|
+
time.sleep(10)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Full tool
|
|
109
|
+
|
|
110
|
+
The complete [paw_monitor.py](https://github.com/programasweights/programasweights-python/blob/main/examples/paw_monitor.py) adds:
|
|
111
|
+
|
|
112
|
+
- **File watching** with `seek()` to only process new content
|
|
113
|
+
- **Input truncation** to fit the ~2048 token context window
|
|
114
|
+
- **Stall detection** -- alerts if no new output for a configurable timeout
|
|
115
|
+
- **`--focus` and `--ignore`** flags to guide what the classifier pays attention to
|
|
116
|
+
- **`--local` flag** to run entirely offline after first compile
|
|
117
|
+
- **`--json` output** for integration with other tools
|
|
118
|
+
|
|
119
|
+
## Adapting this for your use case
|
|
120
|
+
|
|
121
|
+
1. **Collect 5-10 example log lines** from your actual process -- a mix of routine output and important events
|
|
122
|
+
2. **Write a spec** with `Input: ... Output: ALERT/QUIET` pairs
|
|
123
|
+
3. **Test with real logs** -- pipe a log file through the classifier and check which lines it flags
|
|
124
|
+
4. **Iterate** -- if it alerts too much, add more QUIET examples. If it misses things, add ALERT examples.
|
|
125
|
+
5. **Save the program ID** -- compile once, monitor forever
|
|
126
|
+
|
|
127
|
+
## Takeaways
|
|
128
|
+
|
|
129
|
+
- **Examples beat descriptions** for teaching the model your specific log patterns.
|
|
130
|
+
- **Compile once, run forever.** The compiled function is cached locally and needs no internet.
|
|
131
|
+
- **PAW classifies what it sees** -- combine with a timer for stall detection.
|
|
132
|
+
- **Iterate with real data.** The first spec is rarely perfect. Test, check failures, adjust examples.
|