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.
Website: https://programasweights.com Full documentation: https://programasweights.readthedocs.io
pip install programasweights --extra-index-url https://pypi.programasweights.com/simple/import programasweights as paw
# Use a pre-compiled function (downloads once, runs locally forever)
# "email-triage" is an official pre-compiled program (slug)
fn = paw.function("email-triage")
fn("Urgent: server is down!") # "immediate"
fn("Newsletter: spring picnic") # "wait"
# Compile your own from a description
program = paw.compile(
"Fix malformed JSON: repair missing quotes and trailing commas",
compiler="paw-4b-qwen3-0.6b" # or "paw-4b-gpt2" for smaller/faster
)
fn = paw.function(program.id)
fn("{name: 'Alice',}") # '{"name":"Alice"}'
# Or compile and load in one step
fn = paw.compile_and_load("Classify sentiment as positive or negative")
fn("I love this!") # "positive"- Standard (
paw-4b-qwen3-0.6b) — higher accuracy, 594 MB base + ~22 MB/program. Default. - Compact (
paw-4b-gpt2) — smaller (134 MB base + ~5 MB/program), runs in browser via WebAssembly.
- Fuzzy search — typo-tolerant matching, semantic search, near-duplicate detection
- Format repair — fix broken JSON, normalize dates, repair malformed inputs
- Classification — sentiment, urgency, categories defined in your own words
- Extraction — emails, names, dates from messy unstructured text
- Log triage — extract errors from verbose output, filter noise
- Intent routing — map user descriptions to the closest URL, menu item, or setting
- Agent preprocessing — parse tool calls, validate outputs, route tasks
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.
A good spec has a description plus Input: ... Output: ... examples.
fn = paw.compile_and_load("""
Classify user intent. Return ONLY one of: search, create, delete, other.
Input: Find the latest report
Output: search
Input: Make a new folder
Output: create
Input: Remove old backups
Output: delete
""")Spec-tuning tips:
- Each function is stateless: one text input, one text output. No conversation history.
- State output constraints explicitly: "Return ONLY one of: X, Y, Z". Without this the model may produce free-form text.
- Include examples from your actual data: Examples outperform prose-only descriptions.
- Debug failures before sweeping: Look at specific failing examples and understand WHY before trying many variants.
- Spec + input + output share a ~2048 token context window. Inputs that exceed it will error.
max_tokensdefaults toNone: generation runs until EOS or the context limit.
Multiple PAW functions can be composed for multi-step tasks:
classifier = paw.compile_and_load("Classify the bug type. Return ONLY one of: off-by-one, type-error, other")
fixer = paw.compile_and_load("Fix the bug described in the first line. Return only the corrected code.")
label = classifier(code_snippet)
if label != "other":
fix = fixer(f"{label}: {code_snippet}")Chain them with regular Python logic.
PAW functions can classify log output. Compile once with examples from your specific logs, then reuse the function locally forever:
program = paw.compile("""
Classify log lines. Return ONLY one word: ALERT or QUIET.
Input: [step 100] loss=0.05 lr=0.0001
Output: QUIET
Input: [Checkpoint] Saved model at step 1000
Output: ALERT
Input: Traceback (most recent call last):
Output: ALERT
Input: Training complete. Final loss: 0.11
Output: ALERT
""")
fn = paw.function(program.id) # reuse with saved program.id
fn("[step 200] loss=0.04") # "QUIET"
fn("[Checkpoint] Saved model") # "ALERT"Full tool with file watching, truncation, and stall detection: examples/paw_monitor.py
Programs compiled with paw-4b-gpt2 run in the browser via WebAssembly.
npm install @programasweights/webimport paw from '@programasweights/web';
const fn = await paw.function('programasweights/email-triage');
const result = await fn('Urgent: server is down!');
// result: "immediate"Sign in for higher rate limits and program naming. Everything works without it.
export PAW_API_KEY=paw_sk_...Generate API keys at https://programasweights.com/settings.
| Anonymous | Authenticated | |
|---|---|---|
| Compile rate limit | 20/hr | 60/hr |
| Name programs (slugs) | No | Yes |
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.
Slugs support version history. Recompiling with the same slug creates a new version:
p1 = paw.compile("Count words v1", slug="word-counter") # v1
p2 = paw.compile("Count words v2", slug="word-counter") # v2 (auto-bumps)
fn = paw.function("da03/word-counter") # resolves to main (latest)
fn = paw.function("da03/word-counter@v1") # pinned to v1
versions = paw.list_versions("da03/word-counter") # all versionsPinned 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).
program = paw.compile(
spec, # natural language specification (str)
compiler="paw-4b-qwen3-0.6b",
slug=None, # URL-safe handle (requires auth)
public=True, # list on public hub
)
# Returns: Program(id, slug, status, version, version_action, timings, error)
fn = paw.function(program) # accepts Program object, hash ID, or slug
fn = paw.function("a6b454023d41ac9ca845")
fn = paw.function("da03/my-classifier")
fn = paw.function("da03/my-classifier@v2") # pinned version
fn = paw.function("da03/my-classifier", offline=True) # skip server check
result: str = fn(input_text: str, max_tokens=None, temperature=0.0)
fn = paw.compile_and_load(spec, compiler="paw-4b-qwen3-0.6b")
versions = paw.list_versions("da03/my-classifier") # version history
programs = paw.list_programs(sort="recent", per_page=20) # requires auth
paw.login()| Error | Cause | Fix |
|---|---|---|
RuntimeError: assets not ready on download |
Program still generating after compile | SDK polls automatically for up to 30s. If persistent, recompile. |
httpx.HTTPStatusError: 422 on compile |
Spec too short (<10 chars) | Adjust spec length. |
httpx.HTTPStatusError: 429 |
Rate limit exceeded | Wait, or sign in for higher limits. |
| 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. |
- GPU acceleration enabled by default (
n_gpu_layers=-1). Uses Metal on Mac, CUDA on Linux, falls back to CPU automatically. If GPU causes issues, setPAW_GPU_LAYERS=0or passn_gpu_layers=0. - First call ~1-5s (loads base model). Subsequent calls ~0.05-0.5s depending on input length and GPU availability.
- Base model shared across functions on disk. Each LoRA adapter adds ~22 MB.
- Cache:
~/.cache/programasweights/. Override withPAW_CACHE_DIR. - Offline after first download.
Detailed walkthroughs of building production systems with PAW, including what we tried and what we learned: log monitoring, site navigation, semantic search, tool calling.