programasweights 0.4.7__tar.gz → 0.4.8__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 (80) hide show
  1. {programasweights-0.4.7 → programasweights-0.4.8}/CHANGELOG.md +5 -0
  2. {programasweights-0.4.7 → programasweights-0.4.8}/PKG-INFO +1 -1
  3. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/__init__.py +1 -1
  4. {programasweights-0.4.7 → programasweights-0.4.8}/pyproject.toml +11 -1
  5. programasweights-0.4.7/.cursor/rules/releases.mdc +0 -17
  6. programasweights-0.4.7/.github/workflows/release.yml +0 -72
  7. programasweights-0.4.7/.github/workflows/test.yml +0 -94
  8. programasweights-0.4.7/.readthedocs.yaml +0 -13
  9. programasweights-0.4.7/AGENTS.md +0 -291
  10. programasweights-0.4.7/RELEASING.md +0 -82
  11. programasweights-0.4.7/docs/adr/001-llama-cpp-over-pytorch.md +0 -21
  12. programasweights-0.4.7/docs/adr/002-q4_0-adapter-format.md +0 -32
  13. programasweights-0.4.7/docs/adr/003-single-spec-field.md +0 -26
  14. programasweights-0.4.7/docs/adr/004-compiler-naming.md +0 -32
  15. programasweights-0.4.7/docs/adr/005-vllm-hidden-states.md +0 -24
  16. programasweights-0.4.7/docs/adr/006-email-api-key-auth.md +0 -41
  17. programasweights-0.4.7/docs/advanced/adrs.md +0 -67
  18. programasweights-0.4.7/docs/advanced/architecture.md +0 -47
  19. programasweights-0.4.7/docs/api-reference/cli.md +0 -110
  20. programasweights-0.4.7/docs/api-reference/python-sdk.md +0 -363
  21. programasweights-0.4.7/docs/api-reference/rest-api.md +0 -200
  22. programasweights-0.4.7/docs/architecture.md +0 -38
  23. programasweights-0.4.7/docs/case-studies/alien-taboo.md +0 -117
  24. programasweights-0.4.7/docs/case-studies/log-monitoring.md +0 -132
  25. programasweights-0.4.7/docs/case-studies/semantic-search.md +0 -146
  26. programasweights-0.4.7/docs/case-studies/site-navigation.md +0 -127
  27. programasweights-0.4.7/docs/case-studies/tool-calling.md +0 -477
  28. programasweights-0.4.7/docs/getting-started/first-program.md +0 -88
  29. programasweights-0.4.7/docs/getting-started/installation.md +0 -57
  30. programasweights-0.4.7/docs/getting-started/naming-programs.md +0 -78
  31. programasweights-0.4.7/docs/guide/browser-inference.md +0 -141
  32. programasweights-0.4.7/docs/guide/how-it-works.md +0 -47
  33. programasweights-0.4.7/docs/guide/local-inference.md +0 -48
  34. programasweights-0.4.7/docs/guide/writing-good-specs.md +0 -11
  35. programasweights-0.4.7/docs/hub/browsing-programs.md +0 -37
  36. programasweights-0.4.7/docs/hub/feedback-cases.md +0 -34
  37. programasweights-0.4.7/docs/hub/publishing-programs.md +0 -31
  38. programasweights-0.4.7/docs/index.md +0 -96
  39. programasweights-0.4.7/docs/requirements.txt +0 -2
  40. programasweights-0.4.7/examples/flask_app.py +0 -52
  41. programasweights-0.4.7/examples/jupyter_notebook.py +0 -52
  42. programasweights-0.4.7/examples/langchain_integration.py +0 -52
  43. programasweights-0.4.7/examples/paw_monitor.py +0 -180
  44. programasweights-0.4.7/examples/replace_openai.py +0 -44
  45. programasweights-0.4.7/mkdocs.yml +0 -87
  46. programasweights-0.4.7/scripts/release_metadata.py +0 -156
  47. programasweights-0.4.7/tests/test_api_errors.py +0 -222
  48. programasweights-0.4.7/tests/test_base_interpreter.py +0 -845
  49. programasweights-0.4.7/tests/test_cli_auth.py +0 -265
  50. programasweights-0.4.7/tests/test_compile_timeouts.py +0 -129
  51. programasweights-0.4.7/tests/test_desktop_sdk.py +0 -1221
  52. programasweights-0.4.7/tests/test_local_program.py +0 -676
  53. programasweights-0.4.7/tests/test_offline_cache.py +0 -97
  54. programasweights-0.4.7/tests/test_release_metadata.py +0 -296
  55. programasweights-0.4.7/tests/test_remote_inference.py +0 -294
  56. programasweights-0.4.7/tests/test_runtime_registry_sdk.py +0 -123
  57. programasweights-0.4.7/tests/test_sdk.py +0 -484
  58. programasweights-0.4.7/tests/test_sdk.sh +0 -89
  59. {programasweights-0.4.7 → programasweights-0.4.8}/.gitignore +0 -0
  60. {programasweights-0.4.7 → programasweights-0.4.8}/LICENSE +0 -0
  61. {programasweights-0.4.7 → programasweights-0.4.8}/PYPI_README.md +0 -0
  62. {programasweights-0.4.7 → programasweights-0.4.8}/README.md +0 -0
  63. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/_output.py +0 -0
  64. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/_program_reference.py +0 -0
  65. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/_remote.py +0 -0
  66. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/artifacts.py +0 -0
  67. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/cache.py +0 -0
  68. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/cli.py +0 -0
  69. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/client.py +0 -0
  70. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/compiler/__init__.py +0 -0
  71. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/compiler/dummy.py +0 -0
  72. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/config.py +0 -0
  73. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/convert_peft_to_paw.py +0 -0
  74. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/errors.py +0 -0
  75. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/local_program.py +0 -0
  76. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/paw_format.py +0 -0
  77. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/runtime/__init__.py +0 -0
  78. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/runtime/interpreter.py +0 -0
  79. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/runtime/interpreter_onnx.py +0 -0
  80. {programasweights-0.4.7 → programasweights-0.4.8}/programasweights/runtime_llamacpp.py +0 -0
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.8 (2026-09-20)
4
+
5
+ - Reduce the source distribution to the SDK and files needed to build and
6
+ document the package.
7
+
3
8
  ## 0.4.7 (2026-09-20)
4
9
 
5
10
  - Add `remote=True` to `paw.function` and `paw.compile_and_load`, plus
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: programasweights
3
- Version: 0.4.7
3
+ Version: 0.4.8
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
@@ -27,7 +27,7 @@ try:
27
27
  from importlib.metadata import version as _meta_version
28
28
  __version__ = _meta_version("programasweights")
29
29
  except Exception:
30
- __version__ = "0.4.7"
30
+ __version__ = "0.4.8"
31
31
 
32
32
  from ._output import ProgressCallback, ProgressEvent, report_progress
33
33
  from .cache import CachedProgram
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "programasweights"
7
- version = "0.4.7"
7
+ version = "0.4.8"
8
8
  description = "Compile natural language specifications into neural programs that run locally via llama.cpp."
9
9
  readme = "PYPI_README.md"
10
10
  requires-python = ">=3.9"
@@ -47,6 +47,16 @@ paw = "programasweights.cli:main"
47
47
  [tool.hatch.build.targets.wheel]
48
48
  packages = ["programasweights"]
49
49
 
50
+ [tool.hatch.build.targets.sdist]
51
+ only-include = [
52
+ "programasweights",
53
+ "README.md",
54
+ "PYPI_README.md",
55
+ "LICENSE",
56
+ "CHANGELOG.md",
57
+ "pyproject.toml",
58
+ ]
59
+
50
60
  [tool.pytest.ini_options]
51
61
  addopts = "-q"
52
62
  pythonpath = ["."]
@@ -1,17 +0,0 @@
1
- ---
2
- description: Publish and verify complete Python SDK releases
3
- alwaysApply: true
4
- ---
5
-
6
- # SDK releases
7
-
8
- For release or publication work, read `RELEASING.md` first.
9
-
10
- - A release is complete only when its annotated remote tag, PyPI wheel and
11
- source distribution, and published GitHub Release all exist and agree.
12
- - Build from the clean, tested release commit; push its annotated version tag
13
- before uploading the exact checked artifacts to PyPI.
14
- - Verify the `GitHub release` workflow succeeded and the latest GitHub Release
15
- agrees with PyPI. A tag alone is not a GitHub Release entry.
16
- - Repair missing entries using the documented workflow dispatch. Never move a
17
- published tag, rebuild an existing version, or bypass artifact verification.
@@ -1,72 +0,0 @@
1
- name: GitHub release
2
-
3
- on:
4
- push:
5
- tags: ['v*']
6
- workflow_dispatch:
7
- inputs:
8
- tag:
9
- description: 'Existing stable version tag, e.g. v0.4.6'
10
- required: true
11
- type: string
12
-
13
- permissions:
14
- contents: write
15
-
16
- concurrency:
17
- group: github-release-${{ inputs.tag || github.ref_name }}
18
- cancel-in-progress: false
19
-
20
- jobs:
21
- release:
22
- runs-on: ubuntu-latest
23
- timeout-minutes: 15
24
- env:
25
- RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
26
- GH_TOKEN: ${{ github.token }}
27
- GH_REPO: ${{ github.repository }}
28
- steps:
29
- - name: Set the verification output directory
30
- run: echo "RELEASE_DIR=$RUNNER_TEMP/verified-release" >> "$GITHUB_ENV"
31
- - uses: actions/checkout@v4
32
- with:
33
- fetch-depth: 0
34
- - uses: actions/setup-python@v5
35
- with:
36
- python-version: '3.12'
37
- - name: Verify the remote tag and both published PyPI artifacts
38
- # Tags are pushed before uploading. Wait for the wheel AND sdist;
39
- # never announce an unpublished version or rebuild release assets.
40
- run: python scripts/release_metadata.py --tag "$RELEASE_TAG" --output-dir "$RELEASE_DIR" --wait-seconds 600
41
- - name: Publish the missing GitHub release
42
- shell: bash
43
- run: |
44
- set -euo pipefail
45
- if gh release view "$RELEASE_TAG" --json tagName >/dev/null 2>&1; then
46
- echo "Release already exists; preserving its notes and assets."
47
- else
48
- latest=$(jq -r .latest "$RELEASE_DIR/verified.json")
49
- gh release create "$RELEASE_TAG" "$RELEASE_DIR"/artifacts/* \
50
- --verify-tag --title "$RELEASE_TAG" \
51
- --notes-file "$RELEASE_DIR/notes.md" --latest="$latest"
52
- fi
53
- gh release view "$RELEASE_TAG" --json tagName,isDraft,isPrerelease,url \
54
- | jq -e --arg tag "$RELEASE_TAG" '.tagName == $tag and .isDraft == false and .isPrerelease == false'
55
- - name: Verify the uploaded release assets and latest marker
56
- shell: bash
57
- run: |
58
- set -euo pipefail
59
- gh release download "$RELEASE_TAG" --pattern 'programasweights-*' \
60
- --dir "$RELEASE_DIR/github-assets"
61
- for artifact in "$RELEASE_DIR"/artifacts/*; do
62
- cmp "$artifact" "$RELEASE_DIR/github-assets/$(basename "$artifact")"
63
- done
64
- if [ "$(jq -r .latest "$RELEASE_DIR/verified.json")" = true ]; then
65
- gh api "repos/$GH_REPO/releases/latest" \
66
- | jq -e --arg tag "$RELEASE_TAG" '.tag_name == $tag'
67
- fi
68
- - name: Retain the verification record
69
- uses: actions/upload-artifact@v4
70
- with:
71
- name: release-verification-${{ inputs.tag || github.ref_name }}
72
- path: ${{ runner.temp }}/verified-release/verified.json
@@ -1,94 +0,0 @@
1
- name: tests
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
-
8
- jobs:
9
- release-metadata:
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: actions/checkout@v4
13
- - uses: actions/setup-python@v5
14
- with:
15
- python-version: '3.12'
16
- - run: python -m pip install pytest
17
- - name: Test release verification without network or publishing
18
- run: python -m pytest tests/test_release_metadata.py
19
-
20
- test:
21
- runs-on: ubuntu-latest
22
- strategy:
23
- fail-fast: false
24
- matrix:
25
- python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
26
- steps:
27
- - uses: actions/checkout@v4
28
-
29
- - name: Set up Python ${{ matrix.python-version }}
30
- uses: actions/setup-python@v5
31
- with:
32
- python-version: ${{ matrix.python-version }}
33
-
34
- - name: Install (hermetic deps only)
35
- # Install httpx + pytest and the package itself without pulling the heavy
36
- # llama-cpp-python build. Runtime tests inject a fake llama_cpp module,
37
- # so CI needs neither the native extension nor a model download.
38
- run: |
39
- python -m pip install --upgrade pip
40
- python -m pip install httpx pytest
41
- python -m pip install -e . --no-deps
42
-
43
- - name: Run hermetic tests
44
- # Scoped to tests that need no network, no model download, and no
45
- # PAW_API_KEY. Auth tests (@needs_auth) auto-skip without a key; the
46
- # network/model-download tests in test_sdk.py are excluded here and can
47
- # be run separately against a live server.
48
- run: |
49
- pytest \
50
- tests/test_api_errors.py \
51
- tests/test_compile_timeouts.py \
52
- tests/test_local_program.py \
53
- tests/test_base_interpreter.py \
54
- tests/test_cli_auth.py \
55
- tests/test_remote_inference.py \
56
- tests/test_desktop_sdk.py \
57
- tests/test_runtime_registry_sdk.py \
58
- tests/test_sdk.py::TestInstallAndImport
59
-
60
- local-files-windows:
61
- runs-on: windows-latest
62
- strategy:
63
- fail-fast: false
64
- matrix:
65
- python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
66
- steps:
67
- - uses: actions/checkout@v4
68
- - uses: actions/setup-python@v5
69
- with:
70
- python-version: ${{ matrix.python-version }}
71
- - name: Install hermetic test dependencies
72
- run: |
73
- python -m pip install httpx pytest
74
- python -m pip install -e . --no-deps
75
- - name: Test Windows local paths, cache locks, and compile errors
76
- run: python -m pytest tests/test_api_errors.py tests/test_compile_timeouts.py tests/test_local_program.py tests/test_remote_inference.py --junitxml=test-results.xml
77
- - name: Annotate Windows test failures
78
- if: failure()
79
- shell: python
80
- run: |
81
- from pathlib import Path
82
- import xml.etree.ElementTree as ET
83
-
84
- report = Path("test-results.xml")
85
- if report.exists():
86
- for case in ET.parse(report).iter("testcase"):
87
- for result in case:
88
- if result.tag in {"failure", "error"}:
89
- # GitHub truncates annotations, so retain the actual
90
- # exception at the end of long pytest tracebacks.
91
- detail = result.text or result.get("message", "")
92
- message = f"{case.get('name')}: {detail[-3500:]}"
93
- message = message.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
94
- print(f"::error::{message}")
@@ -1,13 +0,0 @@
1
- version: 2
2
-
3
- build:
4
- os: ubuntu-24.04
5
- tools:
6
- python: "3.12"
7
-
8
- mkdocs:
9
- configuration: mkdocs.yml
10
-
11
- python:
12
- install:
13
- - requirements: docs/requirements.txt
@@ -1,291 +0,0 @@
1
- # ProgramAsWeights (PAW)
2
-
3
- PAW compiles natural language specifications into tiny neural functions that run locally. Each function takes a single text input and returns a single text output. Use it when you need fuzzy text processing — classification, extraction, format repair, search, triage — that regex can't handle but a full LLM is overkill for.
4
-
5
- Website: https://programasweights.com
6
- Full documentation: https://programasweights.readthedocs.io
7
-
8
- ## When to Use PAW
9
-
10
- - **Fuzzy search** — typo-tolerant matching, semantic search, near-duplicate detection
11
- - **Format repair** — fix broken JSON, normalize dates, repair malformed inputs
12
- - **Classification** — sentiment, urgency, categories defined in your own words
13
- - **Extraction** — emails, names, dates from messy unstructured text
14
- - **Log triage** — extract errors from verbose output, filter noise
15
- - **Intent routing** — map user descriptions to the closest URL, menu item, or setting
16
- - **Agent preprocessing** — parse tool calls, validate outputs, route tasks
17
-
18
- ## Install
19
-
20
- ```bash
21
- pip install programasweights --extra-index-url https://pypi.programasweights.com/simple/
22
- ```
23
-
24
- ## Quickstart
25
-
26
- ```python
27
- import programasweights as paw
28
-
29
- # Use a pre-compiled function (downloads once, runs locally forever)
30
- fn = paw.function("email-triage")
31
- fn("Urgent: server is down!") # "immediate"
32
- fn("Newsletter: spring picnic") # "wait"
33
-
34
- # Compile your own from a description
35
- program = paw.compile(
36
- "Fix malformed JSON: repair missing quotes and trailing commas"
37
- )
38
- fn = paw.function(program.id)
39
- fn("{name: 'Alice',}") # '{"name":"Alice"}'
40
-
41
- # Or compile and load in one step
42
- fn = paw.compile_and_load("Classify sentiment as positive or negative")
43
- fn("I love this!") # "positive"
44
- ```
45
-
46
- Load a local `.paw` file with `paw.function("./classifier.paw")` (SDK 0.4.5+).
47
-
48
- If you want the smaller browser-compatible runtime explicitly, pass `compiler="paw-4b-gpt2"`. Otherwise, omit `compiler` and let the server default decide.
49
-
50
- ## Remote inference (optional)
51
-
52
- Use the hosted API for fast inference in around 150 ms, without a local model download.
53
-
54
- ### Python SDK
55
-
56
- ```python
57
- import programasweights as paw
58
-
59
- with paw.function("email-triage", remote=True) as remote_fn:
60
- print(remote_fn("Urgent: the server is down!"))
61
- ```
62
-
63
- For authenticated requests, set `PAW_API_KEY` or use `paw.login()`.
64
-
65
- ### Direct HTTP
66
-
67
- Use `httpx` directly without installing the PAW SDK.
68
-
69
- ```python
70
- import httpx
71
-
72
- with httpx.Client(timeout=60.0) as client:
73
- response = client.post(
74
- "https://programasweights.com/api/v1/infer",
75
- json={
76
- "program_id": "email-triage",
77
- "input": "Urgent: server is down!"
78
- },
79
- )
80
- response.raise_for_status()
81
- print(response.json()["output"])
82
- ```
83
-
84
- For authenticated requests, pass `headers={"X-API-Key": api_key}` to `client.post()`.
85
-
86
- ## Current Public Compilers
87
-
88
- - **Standard** (`paw-4b-qwen3-0.6b`) — higher accuracy, 594 MB base + ~22 MB/program. This is the current server default.
89
- - **Compact** (`paw-4b-gpt2`) — smaller (134 MB base + ~5 MB/program), runs in browser via WebAssembly.
90
-
91
- Best practice:
92
-
93
- - For quickstarts and reusable agent workflows, prefer `paw.compile(spec)` with no explicit compiler.
94
- - If you need to target a specific runtime, pass `compiler="paw-4b-gpt2"` or another supported alias explicitly.
95
- - If you need to inspect current server-supported compiler names at runtime, call `paw.list_compilers()`.
96
-
97
- ## Writing Good Specs
98
-
99
- **The #1 practice: iterate with test cases.** Do not accept low performance on the first try. Build a test suite of input/output pairs, measure accuracy, then iteratively adjust wording and formatting until performance is good enough. Treat spec writing like software engineering: test, debug specific failures, fix the wording, retest.
100
-
101
- A good spec has a description plus `Input: ... Output: ...` examples.
102
-
103
- ```python
104
- fn = paw.compile_and_load("""
105
- Classify user intent. Return ONLY one of: search, create, delete, other.
106
-
107
- Input: Find the latest report
108
- Output: search
109
-
110
- Input: Make a new folder
111
- Output: create
112
-
113
- Input: Remove old backups
114
- Output: delete
115
- """)
116
- ```
117
-
118
- **Spec-tuning tips:**
119
-
120
- - **State output constraints explicitly**: "Return ONLY one of: X, Y, Z". Without this the model may produce free-form text.
121
- - **Include examples from your actual data**: Examples outperform prose-only descriptions.
122
- - **Debug failures before sweeping**: Look at specific failing examples and understand WHY before trying many variants.
123
-
124
- ## Constraints And Runtime Behavior
125
-
126
- - Each PAW function is stateless: one text input, one text output. No conversation history.
127
- - Spec + input + output share a ~2048 token context window. Inputs that exceed it will error.
128
- - `max_tokens` defaults to `None`: generation runs until EOS or the context limit.
129
- - Compile runs on the hosted PAW API. Inference should usually run locally through the SDK.
130
- - Synchronous compile requests use a 40-minute read timeout.
131
- - **Run local inference sequentially by default.** With PAW’s current llama.cpp backend, simultaneous inference calls often perform worse. Reuse loaded functions and process inputs one at a time; never call the same function instance concurrently.
132
- - **GPU acceleration** is enabled by default (`n_gpu_layers=-1`). Uses Metal on Mac, CUDA on Linux, and falls back to CPU automatically. If GPU causes issues, set `PAW_GPU_LAYERS=0` or pass `n_gpu_layers=0`.
133
- - **First call** is usually ~1-5s because it loads the base model. Subsequent calls are typically ~0.05-0.5s depending on input length and GPU availability.
134
- - **Base model files are shared** across programs on disk. Each Standard LoRA adapter is ~22 MB; each Compact LoRA adapter is ~5 MB.
135
- - Cache root is `~/.cache/programasweights/`. Override with `PAW_CACHE_DIR`.
136
- - After the first download, inference works offline. Pass `offline=True` or set
137
- `PAW_OFFLINE=1` to prohibit network access and fail if any validated asset is
138
- missing.
139
- - Advanced only: `paw.function(None, interpreter="gpt2")` runs a supported
140
- base model without a compiled adapter; consult the Python API reference for
141
- its strict prompt and offline semantics.
142
-
143
- ## Common Errors
144
-
145
- Compile API HTTP errors raise `paw.APIError`. Check `error.code` and `error.message` for details. The SDK does not retry automatically.
146
-
147
- | Error | Cause | Fix |
148
- |-------|-------|-----|
149
- | `RuntimeError: assets not ready` on download | Program is still generating after compile | The SDK polls automatically for up to 60s. If it still fails, retry shortly or recompile. |
150
- | `httpx.HTTPStatusError: 422` on compile | Spec too short (<10 chars) or request validation failed | Adjust spec length or request shape. |
151
- | `httpx.HTTPStatusError: 429` | Hosted compile API limit exceeded | Wait, or sign in for higher compile limits. |
152
- | GPU/Metal errors on load | GPU backend not available or incompatible | Set `PAW_GPU_LAYERS=0` or pass `n_gpu_layers=0` to force CPU. |
153
-
154
- ## Browser / JavaScript SDK
155
-
156
- Programs compiled with `paw-4b-gpt2` run in the browser via WebAssembly.
157
-
158
- ```bash
159
- npm install @programasweights/web
160
- ```
161
-
162
- ```javascript
163
- import paw from '@programasweights/web';
164
-
165
- const fn = await paw.function('email-triage-browser');
166
- const result = await fn('Urgent: server is down!');
167
- // result: "immediate"
168
- ```
169
-
170
- The browser SDK resolves slugs through the PAW API, then downloads browser assets from Hugging Face and runs inference client-side. If you load by program ID, browser inference stays independent of the PAW API at runtime.
171
-
172
- ## Authentication (optional)
173
-
174
- Sign in for higher rate limits and program naming. Everything works without it.
175
-
176
- ```bash
177
- export PAW_API_KEY=paw_sk_...
178
- ```
179
-
180
- Generate API keys at https://programasweights.com/settings.
181
-
182
- | | Anonymous | Authenticated |
183
- |---|---|---|
184
- | Compile rate limit | 20/hr | 60/hr |
185
- | Concurrent compile requests | 1 | 2 |
186
- | Name programs (slugs) | No | Yes |
187
-
188
- Hosted API limits apply to compile requests. Most inference should run locally through the SDK.
189
-
190
- ## CLI
191
-
192
- Commands: `paw compile --spec "..." --json`, `paw run --program <id> --input "..." [--offline]`, `paw info <id>`, `paw rename <id> <slug>`, `paw login`. All support `--json` for structured output.
193
-
194
- ## Versioning
195
-
196
- Slugs support version history. Recompiling with the same slug creates a new version:
197
-
198
- ```python
199
- p1 = paw.compile("Count words v1", slug="word-counter") # v1
200
- p2 = paw.compile("Count words v2", slug="word-counter") # v2 (auto-bumps)
201
-
202
- fn = paw.function("da03/word-counter") # resolves to main (latest)
203
- fn = paw.function("da03/word-counter@v1") # pinned to v1
204
-
205
- versions = paw.list_versions("da03/word-counter") # all versions
206
- ```
207
-
208
- Pinned versions (`@v1`) are immutable and cached locally forever. Bare slugs always check the server for the latest main version and fall back to cache if offline.
209
-
210
- ## Full API Reference
211
-
212
- ```python
213
- program = paw.compile(
214
- spec, # natural language specification (10-16000 chars)
215
- compiler=None, # omit to use the current server default (today: paw-4b-qwen3-0.6b)
216
- slug=None, # URL-safe handle (requires auth)
217
- public=True, # list on public hub
218
- )
219
- # Returns: Program(id, slug, status, version, version_action, timings, error)
220
-
221
- fn = paw.function(program) # accepts Program object, hash ID, or slug
222
- fn = paw.function("a6b454023d41ac9ca845")
223
- fn = paw.function("da03/my-classifier")
224
- fn = paw.function("da03/my-classifier@v2") # pinned version
225
- fn = paw.function("da03/my-classifier", offline=True) # skip server check
226
-
227
- result: str = fn(input_text: str, max_tokens=None, temperature=0.0)
228
-
229
- prepared = paw.prepare_program("da03/my-classifier")
230
- ready = paw.is_offline_ready("da03/my-classifier") # zero network
231
- cached = paw.list_cached_programs()
232
-
233
- fn = paw.compile_and_load(spec)
234
-
235
- job = paw.compile_async(spec, compiler="paw-ft-bs48") # explicit finetune compiler required
236
- status = paw.get_compile_status(job["job_id"])
237
-
238
- versions = paw.list_versions("da03/my-classifier") # version history
239
- programs = paw.list_programs(sort="recent", per_page=20) # requires auth
240
- compilers = paw.list_compilers() # discover available compilers at runtime
241
-
242
- paw.login()
243
- ```
244
-
245
- ## Chaining Functions
246
-
247
- Multiple PAW functions can be composed for multi-step tasks:
248
-
249
- ```python
250
- classifier = paw.compile_and_load("Classify the bug type. Return ONLY one of: off-by-one, type-error, other")
251
- fixer = paw.compile_and_load("Fix the bug described in the first line. Return only the corrected code.")
252
-
253
- label = classifier(code_snippet)
254
- if label != "other":
255
- fix = fixer(f"{label}: {code_snippet}")
256
- ```
257
-
258
- Chain them with regular Python logic.
259
-
260
- ## Worked Example: Log Monitoring
261
-
262
- PAW functions can classify log output. Compile once with examples from your specific logs, then reuse the function locally forever:
263
-
264
- ```python
265
- program = paw.compile("""
266
- Classify log lines. Return ONLY one word: ALERT or QUIET.
267
-
268
- Input: [step 100] loss=0.05 lr=0.0001
269
- Output: QUIET
270
-
271
- Input: [Checkpoint] Saved model at step 1000
272
- Output: ALERT
273
-
274
- Input: Traceback (most recent call last):
275
- Output: ALERT
276
-
277
- Input: Training complete. Final loss: 0.11
278
- Output: ALERT
279
- """)
280
-
281
- fn = paw.function(program.id) # reuse with saved program.id
282
- fn("[step 200] loss=0.04") # "QUIET"
283
- fn("[Checkpoint] Saved model") # "ALERT"
284
- ```
285
-
286
- Full tool with file watching, truncation, and stall detection: [examples/paw_monitor.py](https://github.com/programasweights/programasweights-python/blob/main/examples/paw_monitor.py)
287
-
288
- ## Case Studies
289
-
290
- 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/).
291
-
@@ -1,82 +0,0 @@
1
- # Releasing the Python SDK
2
-
3
- A release has three separate records: an annotated Git tag identifies the source,
4
- PyPI serves the installable wheel and source distribution, and a GitHub Release
5
- shows the version and release notes on the repository. A tag or PyPI upload alone
6
- does not create a GitHub Release. Finish and verify all three before reporting a
7
- release complete.
8
-
9
- ## Publish a new version
10
-
11
- 1. Update `pyproject.toml`, the fallback `__version__` in
12
- `programasweights/__init__.py`, and the matching section in `CHANGELOG.md`.
13
- Commit and push these changes to `main` using the existing user Git identity.
14
- Release from a clean checkout of that pushed commit; confirm `HEAD` equals
15
- `origin/main` and the `tests` workflow is green for that exact commit.
16
- 2. Set the intended version, then build a wheel and source distribution into a
17
- new, empty directory. Install `build` and `twine` in the release environment if
18
- needed. Use the two exact filenames below, not a shared `dist/*` directory.
19
-
20
- ```bash
21
- SDK_RELEASE_VERSION=0.4.7 # replace with the version being released
22
- SDK_RELEASE_DIR=$(mktemp -d)
23
- python -m build --outdir "$SDK_RELEASE_DIR"
24
- python -m twine check \
25
- "$SDK_RELEASE_DIR/programasweights-$SDK_RELEASE_VERSION-py3-none-any.whl" \
26
- "$SDK_RELEASE_DIR/programasweights-$SDK_RELEASE_VERSION.tar.gz"
27
- ```
28
-
29
- 3. Create and push the annotated version tag **before uploading to PyPI**. Check
30
- that the remote tag resolves to the intended commit and is an annotated tag.
31
- Never replace an existing release tag with a new target.
32
-
33
- ```bash
34
- git tag -a "v$SDK_RELEASE_VERSION" -m "Release v$SDK_RELEASE_VERSION"
35
- git push origin "v$SDK_RELEASE_VERSION"
36
- git ls-remote --tags origin "refs/tags/v$SDK_RELEASE_VERSION*"
37
- ```
38
-
39
- 4. Upload the exact wheel and source distribution that passed `twine check`:
40
-
41
- ```bash
42
- python -m twine upload \
43
- "$SDK_RELEASE_DIR/programasweights-$SDK_RELEASE_VERSION-py3-none-any.whl" \
44
- "$SDK_RELEASE_DIR/programasweights-$SDK_RELEASE_VERSION.tar.gz"
45
- ```
46
-
47
- 5. The `release.yml` workflow starts on the `v*` tag push. It waits up to 600
48
- seconds for PyPI, verifies the remote annotated tag, version metadata, and
49
- published wheel/source distribution against the tagged source, then creates
50
- the GitHub Release with notes from that tag's `CHANGELOG.md` and the published
51
- artifacts. It preserves an existing release and marks a new release as latest
52
- only when its version is the latest on PyPI. If upload takes longer than the
53
- wait window, dispatch the workflow again after PyPI publication succeeds.
54
- 6. Verify the workflow succeeded, the GitHub Releases page has the expected entry
55
- and notes, and the latest-release entry agrees with PyPI's latest version.
56
- Compare both uploaded files' SHA-256 hashes with the version's PyPI JSON
57
- (`https://pypi.org/pypi/programasweights/<version>/json`, `urls[].digests.sha256`)
58
- and with the GitHub Release assets. Do not treat a tag listing as confirmation
59
- that the release entry exists.
60
-
61
- ## Recover a missing GitHub Release
62
-
63
- When a version already exists on PyPI, keep its existing tag and published files.
64
- Do not rebuild or republish that version, and never move its tag. Verify the
65
- remote annotated tag, matching version metadata and changelog section, and the
66
- published artifacts first. `scripts/release_metadata.py` requires Python 3.11+
67
- and performs those checks, preparing the notes and assets in a new directory:
68
-
69
- ```bash
70
- SDK_RELEASE_RECOVERY_DIR=$(mktemp -d)
71
- git fetch origin --tags
72
- python scripts/release_metadata.py --tag v0.4.6 \
73
- --output-dir "$SDK_RELEASE_RECOVERY_DIR/verified" --wait-seconds 600
74
- gh workflow run release.yml --ref main -f tag=v0.4.6
75
- ```
76
-
77
- Replace `v0.4.6` with the verified existing tag to repair. Run this from current
78
- `main`, which contains the recovery workflow and script; the release content is
79
- read from the requested tag. The workflow is idempotent: an existing GitHub
80
- Release is preserved. Recheck the release entry, latest-version status, and
81
- artifact hashes after it finishes. A source or artifact mismatch needs
82
- investigation; do not retag or overwrite published artifacts to make it pass.
@@ -1,21 +0,0 @@
1
- # ADR-001: Use llama.cpp instead of PyTorch for SDK runtime
2
-
3
- ## Status: Accepted (2026-03-21)
4
-
5
- ## Context
6
-
7
- The SDK currently depends on torch + transformers (~2GB install). Users expect a lightweight package they can `pip install` in seconds. Most users run on CPU-only machines (laptops, desktops). The .paw format v2 stores raw safetensors LoRA weights that require PyTorch to apply.
8
-
9
- ## Decision
10
-
11
- Replace the PyTorch runtime with llama-cpp-python (~80MB install). Use GGUF model format for the base interpreter and Q4_0 quantized GGUF LoRA adapters. Pre-render chat templates server-side so the client needs no tokenizer library.
12
-
13
- ## Consequences
14
-
15
- - Install size drops from ~2GB to ~80MB (25x reduction)
16
- - Users no longer need CUDA, PyTorch, or transformers
17
- - Inference uses Metal (Mac), CPU (Linux/Windows) — no GPU required
18
- - Must pre-render chat templates server-side (no transformers tokenizer on client)
19
- - .paw format must change from v2 (safetensors) to v3 (GGUF adapter)
20
- - Base model is downloaded once (~594 MB Q6_K for Qwen3, ~134 MB Q8_0 for GPT-2) and shared across all functions
21
- - Per-function adapter download is ~23MB (Q4_0, confirmed lossless at 4096-scale eval)