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.
Files changed (62) hide show
  1. {programasweights-0.2.7 → programasweights-0.3.0}/AGENTS.md +28 -9
  2. {programasweights-0.2.7 → programasweights-0.3.0}/PKG-INFO +1 -1
  3. {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/001-llama-cpp-over-pytorch.md +1 -1
  4. {programasweights-0.2.7 → programasweights-0.3.0}/docs/api-reference/cli.md +1 -1
  5. {programasweights-0.2.7 → programasweights-0.3.0}/docs/api-reference/python-sdk.md +1 -1
  6. {programasweights-0.2.7 → programasweights-0.3.0}/docs/api-reference/rest-api.md +10 -2
  7. {programasweights-0.2.7 → programasweights-0.3.0}/docs/architecture.md +1 -1
  8. programasweights-0.3.0/docs/case-studies/log-monitoring.md +132 -0
  9. programasweights-0.3.0/docs/case-studies/tool-calling.md +477 -0
  10. programasweights-0.3.0/docs/guide/writing-good-specs.md +11 -0
  11. {programasweights-0.2.7 → programasweights-0.3.0}/mkdocs.yml +2 -0
  12. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/__init__.py +76 -10
  13. programasweights-0.3.0/programasweights/_output.py +34 -0
  14. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/cache.py +7 -4
  15. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/client.py +24 -0
  16. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/runtime_llamacpp.py +4 -0
  17. {programasweights-0.2.7 → programasweights-0.3.0}/pyproject.toml +1 -1
  18. programasweights-0.2.7/docs/guide/writing-good-specs.md +0 -36
  19. {programasweights-0.2.7 → programasweights-0.3.0}/.gitignore +0 -0
  20. {programasweights-0.2.7 → programasweights-0.3.0}/.readthedocs.yaml +0 -0
  21. {programasweights-0.2.7 → programasweights-0.3.0}/CHANGELOG.md +0 -0
  22. {programasweights-0.2.7 → programasweights-0.3.0}/LICENSE +0 -0
  23. {programasweights-0.2.7 → programasweights-0.3.0}/PYPI_README.md +0 -0
  24. {programasweights-0.2.7 → programasweights-0.3.0}/README.md +0 -0
  25. {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/002-q4_0-adapter-format.md +0 -0
  26. {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/003-single-spec-field.md +0 -0
  27. {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/004-compiler-naming.md +0 -0
  28. {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/005-vllm-hidden-states.md +0 -0
  29. {programasweights-0.2.7 → programasweights-0.3.0}/docs/adr/006-email-api-key-auth.md +0 -0
  30. {programasweights-0.2.7 → programasweights-0.3.0}/docs/advanced/adrs.md +0 -0
  31. {programasweights-0.2.7 → programasweights-0.3.0}/docs/advanced/architecture.md +0 -0
  32. {programasweights-0.2.7 → programasweights-0.3.0}/docs/case-studies/semantic-search.md +0 -0
  33. {programasweights-0.2.7 → programasweights-0.3.0}/docs/case-studies/site-navigation.md +0 -0
  34. {programasweights-0.2.7 → programasweights-0.3.0}/docs/getting-started/first-program.md +0 -0
  35. {programasweights-0.2.7 → programasweights-0.3.0}/docs/getting-started/installation.md +0 -0
  36. {programasweights-0.2.7 → programasweights-0.3.0}/docs/getting-started/naming-programs.md +0 -0
  37. {programasweights-0.2.7 → programasweights-0.3.0}/docs/guide/browser-inference.md +0 -0
  38. {programasweights-0.2.7 → programasweights-0.3.0}/docs/guide/how-it-works.md +0 -0
  39. {programasweights-0.2.7 → programasweights-0.3.0}/docs/guide/local-inference.md +0 -0
  40. {programasweights-0.2.7 → programasweights-0.3.0}/docs/hub/browsing-programs.md +0 -0
  41. {programasweights-0.2.7 → programasweights-0.3.0}/docs/hub/feedback-cases.md +0 -0
  42. {programasweights-0.2.7 → programasweights-0.3.0}/docs/hub/publishing-programs.md +0 -0
  43. {programasweights-0.2.7 → programasweights-0.3.0}/docs/index.md +0 -0
  44. {programasweights-0.2.7 → programasweights-0.3.0}/docs/requirements.txt +0 -0
  45. {programasweights-0.2.7 → programasweights-0.3.0}/examples/flask_app.py +0 -0
  46. {programasweights-0.2.7 → programasweights-0.3.0}/examples/jupyter_notebook.py +0 -0
  47. {programasweights-0.2.7 → programasweights-0.3.0}/examples/langchain_integration.py +0 -0
  48. {programasweights-0.2.7 → programasweights-0.3.0}/examples/paw_monitor.py +0 -0
  49. {programasweights-0.2.7 → programasweights-0.3.0}/examples/replace_openai.py +0 -0
  50. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/artifacts.py +0 -0
  51. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/cli.py +0 -0
  52. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/compiler/__init__.py +0 -0
  53. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/compiler/dummy.py +0 -0
  54. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/config.py +0 -0
  55. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/convert_peft_to_paw.py +0 -0
  56. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/paw_format.py +0 -0
  57. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/runtime/__init__.py +0 -0
  58. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/runtime/interpreter.py +0 -0
  59. {programasweights-0.2.7 → programasweights-0.3.0}/programasweights/runtime/interpreter_onnx.py +0 -0
  60. {programasweights-0.2.7 → programasweights-0.3.0}/tests/test_offline_cache.py +0 -0
  61. {programasweights-0.2.7 → programasweights-0.3.0}/tests/test_sdk.py +0 -0
  62. {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
- ## Case Studies
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
- ## Limits
222
+ ## Case Studies
203
223
 
204
- - Spec + input + output share a ~2048 token context window. Inputs that exceed it will error.
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.2.7
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 (~623MB for Q6_K) and shared across all functions
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. If input + output together exceed the window, generation will error. For short inputs, high `max_tokens` is safe (stops at EOS). For long inputs, set `max_tokens` conservatively.
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 (JSON):** includes `job_id`, `status`, `program_id`, `slug` (if created), `pseudo_program`, and `timings`.
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
- Redirects to the Hugging Face CDN for the `.paw` artifact. Private programs return 404 to non-owners.
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 (623MB, downloaded once) and hot-swaps Q4_0 adapters per function call. No PyTorch or transformers required.
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.