programasweights 0.1.0.dev6__py3-none-any.whl → 0.1.0.dev8__py3-none-any.whl
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/__init__.py +73 -63
- programasweights/cache.py +38 -1
- programasweights/cli.py +102 -30
- programasweights/client.py +10 -3
- programasweights/runtime/interpreter_onnx.py +1 -1
- programasweights/runtime_llamacpp.py +91 -18
- programasweights-0.1.0.dev8.dist-info/METADATA +142 -0
- {programasweights-0.1.0.dev6.dist-info → programasweights-0.1.0.dev8.dist-info}/RECORD +10 -10
- programasweights-0.1.0.dev6.dist-info/METADATA +0 -127
- {programasweights-0.1.0.dev6.dist-info → programasweights-0.1.0.dev8.dist-info}/WHEEL +0 -0
- {programasweights-0.1.0.dev6.dist-info → programasweights-0.1.0.dev8.dist-info}/entry_points.txt +0 -0
programasweights/__init__.py
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
"""
|
|
2
|
-
ProgramAsWeights (PAW): Compile natural language specs into neural
|
|
2
|
+
ProgramAsWeights (PAW): Compile natural language specs into tiny neural
|
|
3
|
+
functions that run locally.
|
|
3
4
|
|
|
4
5
|
Quick start:
|
|
5
6
|
import programasweights as paw
|
|
6
7
|
|
|
7
|
-
#
|
|
8
|
-
|
|
8
|
+
# Use a pre-compiled function (downloads once, runs locally forever)
|
|
9
|
+
fn = paw.function("email-triage")
|
|
10
|
+
fn("Urgent: server is down!") # "immediate"
|
|
9
11
|
|
|
10
|
-
#
|
|
12
|
+
# Compile your own from a description
|
|
13
|
+
program = paw.compile("Fix malformed JSON: repair missing quotes and trailing commas")
|
|
11
14
|
fn = paw.function(program.id)
|
|
12
|
-
fn("
|
|
15
|
+
fn("{name: 'Alice',}") # '{"name": "Alice"}'
|
|
13
16
|
|
|
14
17
|
API reference:
|
|
15
18
|
paw.compile(spec) Compile a spec on the server, returns Program
|
|
16
19
|
paw.function(program_id) Load a compiled program for local inference
|
|
17
|
-
paw.login(
|
|
20
|
+
paw.login() Save API key for higher rate limits
|
|
18
21
|
paw.api_url Server URL (default: https://programasweights.com)
|
|
19
22
|
paw.api_key API key (set via login() or PAW_API_KEY env var)
|
|
20
23
|
"""
|
|
21
24
|
|
|
22
|
-
__version__ = "0.1.0.
|
|
25
|
+
__version__ = "0.1.0.dev8"
|
|
23
26
|
|
|
24
27
|
from .config import get_api_url, get_api_key, set_api_key
|
|
25
28
|
|
|
@@ -34,41 +37,44 @@ def compile(
|
|
|
34
37
|
name: str | None = None,
|
|
35
38
|
tags: list[str] | None = None,
|
|
36
39
|
public: bool = True,
|
|
40
|
+
slug: str | None = None,
|
|
37
41
|
):
|
|
38
42
|
"""Compile a natural language specification into a neural program.
|
|
39
43
|
|
|
40
44
|
The compilation runs on the PAW server. The resulting program can be
|
|
41
|
-
downloaded and run locally via ``paw.function(program.id)
|
|
45
|
+
downloaded and run locally via ``paw.function(program.id)`` or
|
|
46
|
+
``paw.function(program.slug)`` if a slug was provided.
|
|
42
47
|
|
|
43
48
|
Args:
|
|
44
49
|
spec: Full specification text. Include examples in the text if desired.
|
|
45
50
|
compiler: Compiler model (alias or snapshot name).
|
|
46
|
-
name: Human-readable program name for the hub.
|
|
51
|
+
name: Human-readable program name (display title for the hub).
|
|
47
52
|
tags: Tags for hub discovery.
|
|
48
53
|
public: Whether to list on the public hub.
|
|
54
|
+
slug: URL-safe handle (e.g. 'message-classifier'). Creates a
|
|
55
|
+
``username/slug`` alias. Requires authentication.
|
|
49
56
|
|
|
50
57
|
Returns:
|
|
51
|
-
A ``Program`` object with ``id``, ``status``, and ``timings``.
|
|
58
|
+
A ``Program`` object with ``id``, ``slug``, ``status``, and ``timings``.
|
|
52
59
|
|
|
53
60
|
Example:
|
|
54
61
|
>>> program = paw.compile(
|
|
55
|
-
... "
|
|
56
|
-
... "
|
|
57
|
-
... "Input: I love it\\nOutput: positive\\n"
|
|
58
|
-
... "Input: I hate it\\nOutput: negative"
|
|
62
|
+
... "Fix malformed JSON: repair missing quotes and trailing commas",
|
|
63
|
+
... slug="json-fixer"
|
|
59
64
|
... )
|
|
60
|
-
>>>
|
|
61
|
-
'
|
|
65
|
+
>>> fn = paw.function(program.slug) # or paw.function(program.id)
|
|
66
|
+
>>> fn("{name: 'Alice',}")
|
|
67
|
+
'{"name": "Alice"}'
|
|
62
68
|
"""
|
|
63
69
|
from .client import PAWClient
|
|
64
70
|
client = PAWClient(api_url=api_url, api_key=api_key)
|
|
65
|
-
return client.compile(spec, compiler=compiler, name=name, tags=tags, public=public)
|
|
71
|
+
return client.compile(spec, compiler=compiler, name=name, tags=tags, public=public, slug=slug)
|
|
66
72
|
|
|
67
73
|
|
|
68
74
|
def function(
|
|
69
75
|
program_id: str,
|
|
70
76
|
n_ctx: int = 2048,
|
|
71
|
-
n_gpu_layers: int =
|
|
77
|
+
n_gpu_layers: int | None = None,
|
|
72
78
|
verbose: bool = False,
|
|
73
79
|
):
|
|
74
80
|
"""Load a compiled program for local inference via llama.cpp.
|
|
@@ -79,26 +85,36 @@ def function(
|
|
|
79
85
|
Args:
|
|
80
86
|
program_id: The program ID from ``paw.compile()``.
|
|
81
87
|
n_ctx: Context window size for llama.cpp.
|
|
82
|
-
n_gpu_layers: GPU layers (-1 = all, 0 = CPU only).
|
|
88
|
+
n_gpu_layers: GPU layers (-1 = all, 0 = CPU only). Defaults to CPU.
|
|
89
|
+
Set ``PAW_GPU_LAYERS`` env var or pass explicitly for GPU acceleration.
|
|
83
90
|
verbose: Print llama.cpp debug output.
|
|
84
91
|
|
|
85
92
|
Returns:
|
|
86
93
|
A callable ``PawFunction`` that takes an input string and returns output.
|
|
87
94
|
|
|
88
95
|
Example:
|
|
89
|
-
>>> fn = paw.function("
|
|
90
|
-
>>> fn("
|
|
91
|
-
'
|
|
96
|
+
>>> fn = paw.function("email-triage")
|
|
97
|
+
>>> fn("Urgent: the server is down!")
|
|
98
|
+
'immediate'
|
|
92
99
|
"""
|
|
100
|
+
import os
|
|
93
101
|
import re
|
|
94
|
-
from .cache import is_program_cached, get_program_dir
|
|
102
|
+
from .cache import is_program_cached, get_program_dir, get_cached_slug, save_slug_mapping
|
|
95
103
|
from .runtime_llamacpp import PawFunction
|
|
96
104
|
|
|
105
|
+
if n_gpu_layers is None:
|
|
106
|
+
n_gpu_layers = int(os.environ.get("PAW_GPU_LAYERS", "0"))
|
|
107
|
+
|
|
97
108
|
resolved_id = program_id
|
|
98
109
|
if not re.fullmatch(r"[a-f0-9]{16,64}", program_id):
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
cached = get_cached_slug(program_id)
|
|
111
|
+
if cached:
|
|
112
|
+
resolved_id = cached
|
|
113
|
+
else:
|
|
114
|
+
from .client import PAWClient
|
|
115
|
+
client = PAWClient(api_url=api_url, api_key=api_key)
|
|
116
|
+
resolved_id = client.resolve_slug(program_id)
|
|
117
|
+
save_slug_mapping(program_id, resolved_id)
|
|
102
118
|
|
|
103
119
|
if not is_program_cached(resolved_id):
|
|
104
120
|
from .client import PAWClient
|
|
@@ -111,58 +127,52 @@ def function(
|
|
|
111
127
|
)
|
|
112
128
|
|
|
113
129
|
|
|
114
|
-
def login(
|
|
115
|
-
"""
|
|
130
|
+
def login(key: str | None = None):
|
|
131
|
+
"""Store an API key for authenticating with the PAW server.
|
|
116
132
|
|
|
117
|
-
If
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
If no key is provided, opens the Settings page in a browser
|
|
134
|
+
and prompts interactively for the key.
|
|
135
|
+
|
|
136
|
+
Generate your API key at https://programasweights.com/settings
|
|
120
137
|
|
|
121
138
|
Args:
|
|
122
|
-
|
|
139
|
+
key: API key string (``paw_sk_...``). If None, prompts interactively.
|
|
123
140
|
|
|
124
141
|
Example:
|
|
125
|
-
>>> paw.login(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"""
|
|
130
|
-
import httpx
|
|
142
|
+
>>> paw.login()
|
|
143
|
+
Generate an API key at https://programasweights.com/settings
|
|
144
|
+
Paste your API key: ********
|
|
145
|
+
API key saved.
|
|
131
146
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
>>> paw.login("paw_sk_abc123...")
|
|
148
|
+
API key saved.
|
|
149
|
+
"""
|
|
150
|
+
if key is None:
|
|
151
|
+
settings_url = api_url.rstrip("/") + "/settings"
|
|
152
|
+
print(f"Generate an API key at {settings_url}")
|
|
153
|
+
try:
|
|
154
|
+
import webbrowser
|
|
155
|
+
webbrowser.open(settings_url)
|
|
156
|
+
except Exception:
|
|
157
|
+
pass
|
|
136
158
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
f"{url}/api/v1/auth/email",
|
|
140
|
-
json={"email": email},
|
|
141
|
-
timeout=10.0,
|
|
142
|
-
)
|
|
143
|
-
resp.raise_for_status()
|
|
144
|
-
print(f"Verification code sent to {email}")
|
|
159
|
+
import getpass
|
|
160
|
+
key = getpass.getpass("Paste your API key: ").strip()
|
|
145
161
|
|
|
146
|
-
|
|
147
|
-
|
|
162
|
+
if not key:
|
|
163
|
+
print("No key provided. Aborted.")
|
|
164
|
+
return
|
|
148
165
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
f"{url}/api/v1/auth/verify",
|
|
152
|
-
json={"email": email, "code": code},
|
|
153
|
-
timeout=10.0,
|
|
154
|
-
)
|
|
155
|
-
resp.raise_for_status()
|
|
156
|
-
data = resp.json()
|
|
166
|
+
if not key.startswith("paw_sk_"):
|
|
167
|
+
print("Warning: key doesn't start with 'paw_sk_'. Saving anyway.")
|
|
157
168
|
|
|
158
|
-
key = data.get("api_key", "")
|
|
159
169
|
set_api_key(key)
|
|
160
170
|
|
|
161
171
|
global api_key
|
|
162
172
|
api_key = key
|
|
163
173
|
|
|
164
|
-
print(
|
|
165
|
-
print(
|
|
174
|
+
print("API key saved to ~/.config/programasweights/config.json")
|
|
175
|
+
print("You can also set the PAW_API_KEY environment variable.")
|
|
166
176
|
|
|
167
177
|
|
|
168
178
|
__all__ = [
|
programasweights/cache.py
CHANGED
|
@@ -10,10 +10,12 @@ Cache structure:
|
|
|
10
10
|
adapter.gguf # ~23 MB, Q4_0 LoRA
|
|
11
11
|
prompt_template.txt
|
|
12
12
|
meta.json
|
|
13
|
+
slug_cache.json # slug -> program_id mapping
|
|
13
14
|
"""
|
|
14
15
|
|
|
15
16
|
from __future__ import annotations
|
|
16
17
|
|
|
18
|
+
import json
|
|
17
19
|
import os
|
|
18
20
|
from pathlib import Path
|
|
19
21
|
|
|
@@ -22,11 +24,13 @@ import httpx
|
|
|
22
24
|
from . import config
|
|
23
25
|
|
|
24
26
|
BASE_MODEL_URLS = {
|
|
25
|
-
"qwen3-0.6b-q6_k": "https://huggingface.co/
|
|
27
|
+
"qwen3-0.6b-q6_k": "https://huggingface.co/programasweights/Qwen3-0.6B-GGUF-Q6_K/resolve/main/qwen3-0.6b-q6_k.gguf",
|
|
28
|
+
"gpt2-q6_k": "https://huggingface.co/programasweights/GPT2-GGUF-Q6_K/resolve/main/gpt2-q6_k.gguf",
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
INTERPRETER_TO_GGUF = {
|
|
29
32
|
"Qwen/Qwen3-0.6B": "qwen3-0.6b-q6_k",
|
|
33
|
+
"gpt2": "gpt2-q6_k",
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
|
|
@@ -78,3 +82,36 @@ def _download_file(url: str, dest: Path):
|
|
|
78
82
|
mb = downloaded / 1024 / 1024
|
|
79
83
|
print(f"\r {mb:.1f} MB ({pct:.0f}%)", end="", flush=True)
|
|
80
84
|
print()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _slug_cache_path() -> Path:
|
|
88
|
+
return config.get_cache_dir() / "slug_cache.json"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_cached_slug(slug: str) -> str | None:
|
|
92
|
+
"""Look up a slug in the local cache. Returns program_id or None."""
|
|
93
|
+
path = _slug_cache_path()
|
|
94
|
+
if not path.exists():
|
|
95
|
+
return None
|
|
96
|
+
try:
|
|
97
|
+
data = json.loads(path.read_text())
|
|
98
|
+
program_id = data.get(slug)
|
|
99
|
+
if program_id and is_program_cached(program_id):
|
|
100
|
+
return program_id
|
|
101
|
+
except (json.JSONDecodeError, OSError):
|
|
102
|
+
pass
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def save_slug_mapping(slug: str, program_id: str) -> None:
|
|
107
|
+
"""Save a slug -> program_id mapping to the local cache."""
|
|
108
|
+
path = _slug_cache_path()
|
|
109
|
+
data: dict = {}
|
|
110
|
+
if path.exists():
|
|
111
|
+
try:
|
|
112
|
+
data = json.loads(path.read_text())
|
|
113
|
+
except (json.JSONDecodeError, OSError):
|
|
114
|
+
pass
|
|
115
|
+
data[slug] = program_id
|
|
116
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
117
|
+
path.write_text(json.dumps(data))
|
programasweights/cli.py
CHANGED
|
@@ -7,9 +7,12 @@ Usage:
|
|
|
7
7
|
paw run <program_id> <input> Run a program locally
|
|
8
8
|
paw login Authenticate with email
|
|
9
9
|
paw info <program_id> Show program metadata
|
|
10
|
+
|
|
11
|
+
All commands support --json for structured output (agent-friendly).
|
|
10
12
|
"""
|
|
11
13
|
|
|
12
14
|
import argparse
|
|
15
|
+
import json
|
|
13
16
|
import sys
|
|
14
17
|
|
|
15
18
|
|
|
@@ -20,23 +23,38 @@ def cmd_compile(args):
|
|
|
20
23
|
if args.api_key:
|
|
21
24
|
paw.api_key = args.api_key
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
if not args.json:
|
|
27
|
+
print(f"Compiling: {args.spec[:80]}...")
|
|
28
|
+
|
|
29
|
+
program = paw.compile(args.spec, compiler=args.compiler, slug=getattr(args, 'slug', None))
|
|
30
|
+
|
|
31
|
+
if args.json:
|
|
32
|
+
print(json.dumps({
|
|
33
|
+
"program_id": program.id,
|
|
34
|
+
"slug": program.slug,
|
|
35
|
+
"status": program.status,
|
|
36
|
+
"error": program.error,
|
|
37
|
+
"timings": program.timings,
|
|
38
|
+
}))
|
|
39
|
+
return 1 if program.error else 0
|
|
25
40
|
|
|
26
41
|
if program.error:
|
|
27
42
|
print(f"Error: {program.error}")
|
|
28
43
|
return 1
|
|
29
44
|
|
|
30
45
|
print(f"Program ID: {program.id}")
|
|
46
|
+
if program.slug:
|
|
47
|
+
print(f"Slug: {program.slug}")
|
|
31
48
|
print(f"Status: {program.status}")
|
|
32
49
|
if program.timings:
|
|
33
50
|
total = program.timings.get("total_ms", 0)
|
|
34
51
|
print(f"Total time: {total:.0f}ms")
|
|
52
|
+
ref = program.slug or program.id
|
|
35
53
|
print(f"\nTo run locally:")
|
|
36
|
-
print(f" paw run {
|
|
54
|
+
print(f" paw run --program \"{ref}\" --input \"your input here\"")
|
|
37
55
|
print(f"\nOr in Python:")
|
|
38
56
|
print(f" import programasweights as paw")
|
|
39
|
-
print(f" fn = paw.function(\"{
|
|
57
|
+
print(f" fn = paw.function(\"{ref}\")")
|
|
40
58
|
print(f" fn(\"your input here\")")
|
|
41
59
|
return 0
|
|
42
60
|
|
|
@@ -46,9 +64,15 @@ def cmd_run(args):
|
|
|
46
64
|
if args.api_url:
|
|
47
65
|
paw.api_url = args.api_url
|
|
48
66
|
|
|
49
|
-
fn = paw.function(
|
|
67
|
+
fn = paw.function(
|
|
68
|
+
args.program, verbose=args.verbose,
|
|
69
|
+
)
|
|
50
70
|
result = fn(args.input, max_tokens=args.max_tokens, temperature=args.temperature)
|
|
51
|
-
|
|
71
|
+
|
|
72
|
+
if args.json:
|
|
73
|
+
print(json.dumps({"program": args.program, "input": args.input, "output": result}))
|
|
74
|
+
else:
|
|
75
|
+
print(result)
|
|
52
76
|
return 0
|
|
53
77
|
|
|
54
78
|
|
|
@@ -56,7 +80,38 @@ def cmd_login(args):
|
|
|
56
80
|
import programasweights as paw
|
|
57
81
|
if args.api_url:
|
|
58
82
|
paw.api_url = args.api_url
|
|
59
|
-
paw.login(args.
|
|
83
|
+
paw.login(args.key)
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def cmd_rename(args):
|
|
88
|
+
import programasweights as paw
|
|
89
|
+
if args.api_url:
|
|
90
|
+
paw.api_url = args.api_url
|
|
91
|
+
if args.api_key:
|
|
92
|
+
paw.api_key = args.api_key
|
|
93
|
+
|
|
94
|
+
import httpx
|
|
95
|
+
from programasweights.client import PAWClient
|
|
96
|
+
client = PAWClient(api_url=paw.api_url, api_key=paw.api_key)
|
|
97
|
+
|
|
98
|
+
resp = httpx.patch(
|
|
99
|
+
f"{client._api_url}/api/v1/programs/{args.program}",
|
|
100
|
+
json={"slug": args.new_slug},
|
|
101
|
+
headers=client._headers(),
|
|
102
|
+
timeout=10.0,
|
|
103
|
+
)
|
|
104
|
+
resp.raise_for_status()
|
|
105
|
+
data = resp.json()
|
|
106
|
+
|
|
107
|
+
if args.json:
|
|
108
|
+
print(json.dumps(data))
|
|
109
|
+
else:
|
|
110
|
+
slug = data.get("slug")
|
|
111
|
+
if slug:
|
|
112
|
+
print(f"Renamed to: {slug}")
|
|
113
|
+
else:
|
|
114
|
+
print("Slug removed.")
|
|
60
115
|
return 0
|
|
61
116
|
|
|
62
117
|
|
|
@@ -65,23 +120,32 @@ def cmd_info(args):
|
|
|
65
120
|
if args.api_url:
|
|
66
121
|
paw.api_url = args.api_url
|
|
67
122
|
|
|
68
|
-
from programasweights.
|
|
69
|
-
|
|
123
|
+
from programasweights.client import PAWClient
|
|
124
|
+
client = PAWClient(api_url=paw.api_url, api_key=paw.api_key)
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
meta = client.get_program_meta(args.program)
|
|
128
|
+
except Exception:
|
|
129
|
+
meta = None
|
|
70
130
|
|
|
71
|
-
if
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
meta
|
|
76
|
-
print(f"
|
|
77
|
-
print(f" Spec: {meta.get('spec', 'N/A')[:100]}")
|
|
131
|
+
if meta and meta.get("id"):
|
|
132
|
+
if args.json:
|
|
133
|
+
print(json.dumps(meta))
|
|
134
|
+
else:
|
|
135
|
+
print(f"Program: {meta.get('id')}")
|
|
136
|
+
print(f" Spec: {(meta.get('spec') or 'N/A')[:100]}")
|
|
78
137
|
print(f" Interpreter: {meta.get('interpreter', 'N/A')}")
|
|
79
138
|
print(f" Compiler: {meta.get('compiler_snapshot', 'N/A')}")
|
|
80
|
-
print(f"
|
|
81
|
-
print(f"
|
|
82
|
-
|
|
139
|
+
print(f" Aliases: {', '.join(meta.get('aliases', []))}")
|
|
140
|
+
print(f" Downloads: {meta.get('downloads', 0)}")
|
|
141
|
+
if meta.get('hf_url'):
|
|
142
|
+
print(f" HF URL: {meta['hf_url']}")
|
|
143
|
+
return 0
|
|
83
144
|
|
|
84
|
-
|
|
145
|
+
if args.json:
|
|
146
|
+
print(json.dumps({"error": "not_found", "program": args.program}))
|
|
147
|
+
else:
|
|
148
|
+
print(f"Program {args.program} not found.")
|
|
85
149
|
return 1
|
|
86
150
|
|
|
87
151
|
|
|
@@ -92,29 +156,36 @@ def main():
|
|
|
92
156
|
)
|
|
93
157
|
parser.add_argument("--api-url", default=None, help="PAW server URL")
|
|
94
158
|
parser.add_argument("--api-key", default=None, help="API key")
|
|
159
|
+
parser.add_argument("--json", action="store_true",
|
|
160
|
+
help="Output structured JSON (agent-friendly)")
|
|
95
161
|
|
|
96
162
|
sub = parser.add_subparsers(dest="command")
|
|
97
163
|
|
|
98
|
-
# paw compile
|
|
99
164
|
p = sub.add_parser("compile", help="Compile a spec on the server")
|
|
100
|
-
p.add_argument("spec", help="Natural language specification")
|
|
165
|
+
p.add_argument("--spec", required=True, help="Natural language specification")
|
|
101
166
|
p.add_argument("--compiler", default="paw-4b-qwen3-0.6b", help="Compiler model")
|
|
167
|
+
p.add_argument("--slug", default=None, help="URL-safe handle (e.g. 'message-classifier')")
|
|
168
|
+
p.add_argument("--json", action="store_true", help="JSON output")
|
|
102
169
|
|
|
103
|
-
# paw run
|
|
104
170
|
p = sub.add_parser("run", help="Run a program locally via llama.cpp")
|
|
105
|
-
p.add_argument("
|
|
106
|
-
p.add_argument("input", help="Input text")
|
|
171
|
+
p.add_argument("--program", required=True, help="Program name or ID")
|
|
172
|
+
p.add_argument("--input", required=True, help="Input text")
|
|
107
173
|
p.add_argument("--max-tokens", type=int, default=512)
|
|
108
174
|
p.add_argument("--temperature", type=float, default=0.0)
|
|
109
175
|
p.add_argument("--verbose", action="store_true")
|
|
176
|
+
p.add_argument("--json", action="store_true", help="JSON output")
|
|
177
|
+
|
|
178
|
+
p = sub.add_parser("login", help="Save API key for authentication")
|
|
179
|
+
p.add_argument("key", nargs="?", default=None, help="API key (paw_sk_...). Omit to open browser.")
|
|
110
180
|
|
|
111
|
-
|
|
112
|
-
p
|
|
113
|
-
p.add_argument("
|
|
181
|
+
p = sub.add_parser("rename", help="Set or change a program's slug")
|
|
182
|
+
p.add_argument("program", help="Program ID or current slug")
|
|
183
|
+
p.add_argument("new_slug", help="New slug (e.g. 'message-classifier') or empty string to remove")
|
|
184
|
+
p.add_argument("--json", action="store_true", help="JSON output")
|
|
114
185
|
|
|
115
|
-
# paw info
|
|
116
186
|
p = sub.add_parser("info", help="Show program info")
|
|
117
|
-
p.add_argument("
|
|
187
|
+
p.add_argument("program", help="Program name or ID")
|
|
188
|
+
p.add_argument("--json", action="store_true", help="JSON output")
|
|
118
189
|
|
|
119
190
|
args = parser.parse_args()
|
|
120
191
|
|
|
@@ -126,6 +197,7 @@ def main():
|
|
|
126
197
|
"compile": cmd_compile,
|
|
127
198
|
"run": cmd_run,
|
|
128
199
|
"login": cmd_login,
|
|
200
|
+
"rename": cmd_rename,
|
|
129
201
|
"info": cmd_info,
|
|
130
202
|
}
|
|
131
203
|
|
programasweights/client.py
CHANGED
|
@@ -23,6 +23,7 @@ class Program:
|
|
|
23
23
|
"""Result of a compilation."""
|
|
24
24
|
id: str
|
|
25
25
|
status: str
|
|
26
|
+
slug: Optional[str] = None
|
|
26
27
|
compiler_snapshot: Optional[str] = None
|
|
27
28
|
timings: Optional[dict] = None
|
|
28
29
|
error: Optional[str] = None
|
|
@@ -48,27 +49,32 @@ class PAWClient:
|
|
|
48
49
|
name: str | None = None,
|
|
49
50
|
tags: list[str] | None = None,
|
|
50
51
|
public: bool = True,
|
|
52
|
+
slug: str | None = None,
|
|
51
53
|
) -> Program:
|
|
52
54
|
"""Compile a spec into a neural program on the server.
|
|
53
55
|
|
|
54
56
|
Args:
|
|
55
57
|
spec: Natural language specification. Include examples in the text.
|
|
56
58
|
compiler: Compiler name (alias or snapshot).
|
|
57
|
-
name: Human-readable program name.
|
|
59
|
+
name: Human-readable program name (display title).
|
|
58
60
|
tags: Tags for hub discovery.
|
|
59
61
|
public: Whether to list on the public hub.
|
|
62
|
+
slug: URL-safe handle (e.g. 'message-classifier'). Creates a
|
|
63
|
+
``username/slug`` alias for easy reference. Requires auth.
|
|
60
64
|
|
|
61
65
|
Returns:
|
|
62
|
-
Program with id, status, and timings.
|
|
66
|
+
Program with id, slug, status, and timings.
|
|
63
67
|
|
|
64
68
|
Raises:
|
|
65
69
|
httpx.HTTPStatusError: On API errors (422 for validation, 429 for rate limit).
|
|
66
70
|
"""
|
|
67
|
-
body = {"spec": spec, "compiler": compiler, "public": public}
|
|
71
|
+
body: dict = {"spec": spec, "compiler": compiler, "public": public}
|
|
68
72
|
if name:
|
|
69
73
|
body["name"] = name
|
|
70
74
|
if tags:
|
|
71
75
|
body["tags"] = tags
|
|
76
|
+
if slug:
|
|
77
|
+
body["slug"] = slug
|
|
72
78
|
|
|
73
79
|
resp = httpx.post(
|
|
74
80
|
f"{self._api_url}/api/v1/compile",
|
|
@@ -82,6 +88,7 @@ class PAWClient:
|
|
|
82
88
|
return Program(
|
|
83
89
|
id=data.get("program_id", ""),
|
|
84
90
|
status=data.get("status", "unknown"),
|
|
91
|
+
slug=data.get("slug"),
|
|
85
92
|
compiler_snapshot=data.get("compiler_snapshot"),
|
|
86
93
|
timings=data.get("timings"),
|
|
87
94
|
error=data.get("error"),
|
|
@@ -82,7 +82,7 @@ def download_onnx_models(model_name: str, download_image_encoder: bool = False)
|
|
|
82
82
|
Downloads only required files (lazy loading for optional components).
|
|
83
83
|
|
|
84
84
|
Args:
|
|
85
|
-
model_name: HuggingFace repo ID (e.g., "
|
|
85
|
+
model_name: HuggingFace repo ID (e.g., "programasweights/paw-interpreter-onnx")
|
|
86
86
|
OR local path to ONNX models directory
|
|
87
87
|
download_image_encoder: If True, also download image encoder (default: False, lazy)
|
|
88
88
|
|
|
@@ -3,13 +3,15 @@ llama.cpp runtime for local inference with LoRA adapters.
|
|
|
3
3
|
|
|
4
4
|
Loads a base GGUF model (Q6_K) and applies a Q4_0 LoRA adapter per-program.
|
|
5
5
|
Uses the pre-rendered prompt template from the .paw bundle.
|
|
6
|
+
|
|
7
|
+
Prefix KV cache is saved to disk after the first call and reloaded on
|
|
8
|
+
subsequent runs, eliminating the ~2-3s cold-start prefix evaluation.
|
|
6
9
|
"""
|
|
7
10
|
|
|
8
11
|
from __future__ import annotations
|
|
9
12
|
|
|
10
13
|
import json
|
|
11
14
|
from pathlib import Path
|
|
12
|
-
from typing import Callable
|
|
13
15
|
|
|
14
16
|
import llama_cpp
|
|
15
17
|
from llama_cpp import Llama
|
|
@@ -29,13 +31,13 @@ class PawFunction:
|
|
|
29
31
|
self,
|
|
30
32
|
program_dir: str | Path,
|
|
31
33
|
n_ctx: int = 2048,
|
|
32
|
-
n_gpu_layers: int =
|
|
34
|
+
n_gpu_layers: int = 0,
|
|
33
35
|
verbose: bool = False,
|
|
34
36
|
):
|
|
35
37
|
program_dir = Path(program_dir)
|
|
36
38
|
self._program_dir = program_dir
|
|
39
|
+
self._verbose = verbose
|
|
37
40
|
|
|
38
|
-
# Load metadata
|
|
39
41
|
meta_path = program_dir / "meta.json"
|
|
40
42
|
if meta_path.exists():
|
|
41
43
|
with open(meta_path) as f:
|
|
@@ -43,17 +45,14 @@ class PawFunction:
|
|
|
43
45
|
else:
|
|
44
46
|
self._meta = {}
|
|
45
47
|
|
|
46
|
-
# Load prompt template
|
|
47
48
|
template_path = program_dir / "prompt_template.txt"
|
|
48
49
|
if not template_path.exists():
|
|
49
50
|
raise FileNotFoundError(f"No prompt_template.txt in {program_dir}")
|
|
50
51
|
self._template = template_path.read_text()
|
|
51
52
|
|
|
52
|
-
# Get base model GGUF
|
|
53
53
|
interpreter = self._meta.get("interpreter", "Qwen/Qwen3-0.6B")
|
|
54
54
|
base_model_path = cache.get_base_model_path(interpreter)
|
|
55
55
|
|
|
56
|
-
# Load base model
|
|
57
56
|
self._llm = Llama(
|
|
58
57
|
model_path=str(base_model_path),
|
|
59
58
|
n_ctx=n_ctx,
|
|
@@ -61,7 +60,6 @@ class PawFunction:
|
|
|
61
60
|
verbose=verbose,
|
|
62
61
|
)
|
|
63
62
|
|
|
64
|
-
# Load LoRA adapter
|
|
65
63
|
adapter_path = program_dir / "adapter.gguf"
|
|
66
64
|
if adapter_path.exists():
|
|
67
65
|
self._adapter = llama_cpp.llama_adapter_lora_init(
|
|
@@ -74,6 +72,69 @@ class PawFunction:
|
|
|
74
72
|
else:
|
|
75
73
|
self._adapter = None
|
|
76
74
|
|
|
75
|
+
placeholder = "{INPUT_PLACEHOLDER}"
|
|
76
|
+
self._use_special = interpreter not in ("gpt2",)
|
|
77
|
+
|
|
78
|
+
if placeholder in self._template:
|
|
79
|
+
prefix_text = self._template.split(placeholder)[0]
|
|
80
|
+
suffix_text = self._template.split(placeholder)[1]
|
|
81
|
+
else:
|
|
82
|
+
prefix_text = self._template
|
|
83
|
+
suffix_text = ""
|
|
84
|
+
|
|
85
|
+
self._prefix_tokens = self._llm.tokenize(
|
|
86
|
+
prefix_text.encode("utf-8"),
|
|
87
|
+
add_bos=not self._use_special,
|
|
88
|
+
special=self._use_special,
|
|
89
|
+
)
|
|
90
|
+
self._suffix_text = suffix_text
|
|
91
|
+
self._n_prefix = len(self._prefix_tokens)
|
|
92
|
+
|
|
93
|
+
self._load_or_eval_prefix()
|
|
94
|
+
|
|
95
|
+
def _load_or_eval_prefix(self):
|
|
96
|
+
"""Load prefix KV state from disk cache, or evaluate and save it."""
|
|
97
|
+
cache_path = self._program_dir / "prefix_kv_cache.bin"
|
|
98
|
+
|
|
99
|
+
if cache_path.exists():
|
|
100
|
+
try:
|
|
101
|
+
import ctypes
|
|
102
|
+
token_array = (llama_cpp.llama_token * self._n_prefix)(*self._prefix_tokens)
|
|
103
|
+
n_token_count = ctypes.c_size_t(0)
|
|
104
|
+
n_loaded = llama_cpp.llama_state_seq_load_file(
|
|
105
|
+
self._llm.ctx,
|
|
106
|
+
str(cache_path).encode("utf-8"),
|
|
107
|
+
0,
|
|
108
|
+
token_array,
|
|
109
|
+
self._n_prefix,
|
|
110
|
+
ctypes.byref(n_token_count),
|
|
111
|
+
)
|
|
112
|
+
if n_loaded > 0:
|
|
113
|
+
self._llm.n_tokens = self._n_prefix
|
|
114
|
+
self._llm.input_ids[:self._n_prefix] = self._prefix_tokens
|
|
115
|
+
if self._verbose:
|
|
116
|
+
print(f"Loaded prefix KV cache ({self._n_prefix} tokens) from disk")
|
|
117
|
+
return
|
|
118
|
+
except Exception:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
self._llm.eval(self._prefix_tokens)
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
token_array = (llama_cpp.llama_token * self._n_prefix)(*self._prefix_tokens)
|
|
125
|
+
result = llama_cpp.llama_state_seq_save_file(
|
|
126
|
+
self._llm.ctx,
|
|
127
|
+
str(cache_path).encode("utf-8"),
|
|
128
|
+
0,
|
|
129
|
+
token_array,
|
|
130
|
+
self._n_prefix,
|
|
131
|
+
)
|
|
132
|
+
if result and self._verbose:
|
|
133
|
+
size_mb = cache_path.stat().st_size / (1024 * 1024)
|
|
134
|
+
print(f"Saved prefix KV cache ({self._n_prefix} tokens, {size_mb:.1f} MB)")
|
|
135
|
+
except Exception:
|
|
136
|
+
pass
|
|
137
|
+
|
|
77
138
|
def __call__(
|
|
78
139
|
self,
|
|
79
140
|
input_text: str,
|
|
@@ -90,20 +151,32 @@ class PawFunction:
|
|
|
90
151
|
Returns:
|
|
91
152
|
The program's output as a string.
|
|
92
153
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
154
|
+
# Reset to prefix state: clear everything after the prefix
|
|
155
|
+
self._llm.n_tokens = self._n_prefix
|
|
156
|
+
|
|
157
|
+
input_with_suffix = input_text + self._suffix_text
|
|
158
|
+
input_tokens = self._llm.tokenize(
|
|
159
|
+
input_with_suffix.encode("utf-8"),
|
|
160
|
+
add_bos=False,
|
|
161
|
+
special=self._use_special,
|
|
97
162
|
)
|
|
98
163
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
164
|
+
self._llm.eval(input_tokens)
|
|
165
|
+
|
|
166
|
+
output_tokens = []
|
|
167
|
+
for _ in range(max_tokens):
|
|
168
|
+
token = self._llm.sample(
|
|
169
|
+
temp=temperature if temperature > 0 else 0,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if token == self._llm.token_eos():
|
|
173
|
+
break
|
|
174
|
+
|
|
175
|
+
output_tokens.append(token)
|
|
176
|
+
self._llm.eval([token])
|
|
105
177
|
|
|
106
|
-
|
|
178
|
+
output_bytes = self._llm.detokenize(output_tokens)
|
|
179
|
+
return output_bytes.decode("utf-8", errors="replace").strip()
|
|
107
180
|
|
|
108
181
|
def __del__(self):
|
|
109
182
|
if hasattr(self, "_adapter") and self._adapter:
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: programasweights
|
|
3
|
+
Version: 0.1.0.dev8
|
|
4
|
+
Summary: Compile natural language specifications into neural programs that run locally via llama.cpp.
|
|
5
|
+
Project-URL: Homepage, https://programasweights.com
|
|
6
|
+
Project-URL: Repository, https://github.com/programasweights/programasweights-python
|
|
7
|
+
Project-URL: Documentation, https://programasweights.readthedocs.io
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/programasweights/programasweights-python/issues
|
|
9
|
+
Author-email: ProgramAsWeights <support@programasweights.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
Keywords: inference,llama-cpp,lora,neural-programs,nlp
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Requires-Dist: httpx<1.0,>=0.27.0
|
|
25
|
+
Requires-Dist: llama-cpp-python<1.0,>=0.3.0
|
|
26
|
+
Provides-Extra: test
|
|
27
|
+
Requires-Dist: pytest; extra == 'test'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# ProgramAsWeights
|
|
31
|
+
|
|
32
|
+
**Compile natural language specs into tiny neural functions that run locally.**
|
|
33
|
+
|
|
34
|
+
Define what a function should do in plain English. PAW compiles it into a small neural program that runs on your machine — no API keys at runtime, no internet needed after setup, fully deterministic.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install programasweights
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import programasweights as paw
|
|
46
|
+
|
|
47
|
+
# Use a pre-compiled function (downloads once, runs locally forever)
|
|
48
|
+
fn = paw.function("email-triage")
|
|
49
|
+
fn("Urgent: the server is down!") # "immediate"
|
|
50
|
+
fn("Newsletter: spring picnic") # "wait"
|
|
51
|
+
|
|
52
|
+
# Compile your own from a description
|
|
53
|
+
program = paw.compile(
|
|
54
|
+
"Fix malformed JSON: repair missing quotes and trailing commas",
|
|
55
|
+
compiler="paw-4b-qwen3-0.6b" # or "paw-4b-gpt2" for smaller/faster
|
|
56
|
+
)
|
|
57
|
+
fn = paw.function(program.id)
|
|
58
|
+
fn("{name: 'Alice',}") # '{"name": "Alice"}'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Two Compilers
|
|
62
|
+
|
|
63
|
+
| | Standard (Qwen3 0.6B) | Compact (GPT-2 124M) |
|
|
64
|
+
|--------------------|-------------------------|------------------------|
|
|
65
|
+
| Compiler name | `paw-4b-qwen3-0.6b` | `paw-4b-gpt2` |
|
|
66
|
+
| Accuracy | Higher | Lower |
|
|
67
|
+
| Base model size | 594 MB | 105 MB |
|
|
68
|
+
| Program size | ~22 MB | ~5 MB |
|
|
69
|
+
| Inference speed | ~90ms (server) | ~50ms (server) |
|
|
70
|
+
| Runs in browser | No | Yes |
|
|
71
|
+
|
|
72
|
+
Default is Standard (Qwen3 0.6B). Use Compact (GPT-2) when you need smaller files or browser deployment.
|
|
73
|
+
|
|
74
|
+
## Browser SDK
|
|
75
|
+
|
|
76
|
+
Programs compiled with GPT-2 also run entirely in the browser via WebAssembly — no server needed, data never leaves the user's device.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install @programasweights/web
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
import paw from '@programasweights/web';
|
|
84
|
+
|
|
85
|
+
const fn = await paw.function('programasweights/email-triage');
|
|
86
|
+
const result = await fn('Urgent: the server is down!');
|
|
87
|
+
// result: "immediate"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
See the [browser SDK repo](https://github.com/programasweights/programasweights-js) for full documentation.
|
|
91
|
+
|
|
92
|
+
## Use with AI Agents
|
|
93
|
+
|
|
94
|
+
PAW works with Cursor, Claude, Codex, and other AI coding assistants. Paste this into your agent's chat:
|
|
95
|
+
|
|
96
|
+
> I want to use ProgramAsWeights (PAW) to create fuzzy text functions that run locally. Read the instructions at https://programasweights.com/agents and help me integrate it.
|
|
97
|
+
|
|
98
|
+
Or save [`AGENTS.md`](https://programasweights.com/agents) to your project root — agents read it automatically.
|
|
99
|
+
|
|
100
|
+
## When to Use PAW
|
|
101
|
+
|
|
102
|
+
- **Fuzzy search** — typo-tolerant matching, semantic search, near-duplicate detection
|
|
103
|
+
- **Format repair** — fix broken JSON, normalize dates, repair malformed inputs
|
|
104
|
+
- **Classification** — sentiment, urgency, categories defined in your own words
|
|
105
|
+
- **Extraction** — emails, names, dates from messy unstructured text
|
|
106
|
+
- **Log triage** — extract errors from verbose output, filter noise
|
|
107
|
+
- **Intent routing** — map user descriptions to the closest URL, menu item, or setting
|
|
108
|
+
- **Agent preprocessing** — parse tool calls, validate outputs, route tasks
|
|
109
|
+
|
|
110
|
+
## Authentication
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Option 1: environment variable (recommended)
|
|
114
|
+
export PAW_API_KEY=paw_sk_...
|
|
115
|
+
|
|
116
|
+
# Option 2: CLI login (opens browser to generate key)
|
|
117
|
+
paw login
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Generate API keys at [programasweights.com/settings](https://programasweights.com/settings). Authenticated users get higher rate limits.
|
|
121
|
+
|
|
122
|
+
## CLI
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
paw compile --spec "Extract error lines from logs" --json
|
|
126
|
+
paw run --program <program_id> --input "[ERROR] timeout" --json
|
|
127
|
+
paw login
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`--json` gives structured output for programmatic use.
|
|
131
|
+
|
|
132
|
+
## Links
|
|
133
|
+
|
|
134
|
+
- **Website**: [programasweights.com](https://programasweights.com)
|
|
135
|
+
- **Documentation**: [programasweights.readthedocs.io](https://programasweights.readthedocs.io)
|
|
136
|
+
- **Python SDK**: [github.com/programasweights/programasweights-python](https://github.com/programasweights/programasweights-python)
|
|
137
|
+
- **Browser SDK**: [github.com/programasweights/programasweights-js](https://github.com/programasweights/programasweights-js)
|
|
138
|
+
- **Program Hub**: [programasweights.com/hub](https://programasweights.com/hub)
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
programasweights/__init__.py,sha256=
|
|
1
|
+
programasweights/__init__.py,sha256=i6zgstkPcoxr00hkH_qWfFvBtRKaxjp87AUYirZeGp0,5952
|
|
2
2
|
programasweights/artifacts.py,sha256=bSRZgYadAYyuH9aIW6P3VocExfGDGAHeDuj8vod5-Bo,1968
|
|
3
|
-
programasweights/cache.py,sha256=
|
|
4
|
-
programasweights/cli.py,sha256=
|
|
5
|
-
programasweights/client.py,sha256=
|
|
3
|
+
programasweights/cache.py,sha256=69mOsx2Aqi3sQjFXjeBvhKySXDKdvKSINis2FprVDw4,3711
|
|
4
|
+
programasweights/cli.py,sha256=_HCBRXfn9at7a4oTvVOAi2lPMMKaDxleLngdc6WKi2c,6480
|
|
5
|
+
programasweights/client.py,sha256=Qk0ubKW54robuVnvEf0AyED5zX2Rw-W4HefBg_GQp8s,4391
|
|
6
6
|
programasweights/config.py,sha256=zQFnVNfYR4bFx2DsZheMnPy2yd9T4-fe0u2sG1GluE8,1553
|
|
7
7
|
programasweights/convert_peft_to_paw.py,sha256=yvavaAzLUqc-lFHEC_9AOhsuI-fCobu6k-GDMVQdOHk,6521
|
|
8
8
|
programasweights/paw_format.py,sha256=wRXolwtnPgKoopiJC8-yMVZZtiwB-PoAqaWpDFKMoj8,10679
|
|
9
|
-
programasweights/runtime_llamacpp.py,sha256=
|
|
9
|
+
programasweights/runtime_llamacpp.py,sha256=UWcmhKF9iNA2r3gDIKxP5bcdJ2WUvSNilW0_aMfvmuo,6461
|
|
10
10
|
programasweights/compiler/__init__.py,sha256=_q0L02k6Cl9zyvxKQlMuW2o6w7cX6OZ_bZ9wHuZqs18,9508
|
|
11
11
|
programasweights/compiler/dummy.py,sha256=PcLwijRNM4q2E9PNUcfCPf_y08QjsteVENR0TVsOIfY,1011
|
|
12
12
|
programasweights/runtime/__init__.py,sha256=S4jp7-eWAcMa0X417U7P6HkTL1j3v8ffQA-nneh9wY0,549
|
|
13
13
|
programasweights/runtime/interpreter.py,sha256=brYCtatSsu23lq87cuki4MViqZX6U32LBw7Z3USbQmA,19632
|
|
14
|
-
programasweights/runtime/interpreter_onnx.py,sha256=
|
|
15
|
-
programasweights-0.1.0.
|
|
16
|
-
programasweights-0.1.0.
|
|
17
|
-
programasweights-0.1.0.
|
|
18
|
-
programasweights-0.1.0.
|
|
14
|
+
programasweights/runtime/interpreter_onnx.py,sha256=kzYKmwg_h0tVhoQlixJGKoHPVhcCK22AcHG59oeYX2Q,21559
|
|
15
|
+
programasweights-0.1.0.dev8.dist-info/METADATA,sha256=yeRSFIIH216ZjEOEFlKTqIHN-BiyA1bFo_cXCnN3nls,5574
|
|
16
|
+
programasweights-0.1.0.dev8.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
17
|
+
programasweights-0.1.0.dev8.dist-info/entry_points.txt,sha256=l4ZnfCPU0oMzhGB9T2Fv9AnDbSCMM70KUUJVtSvMJBc,50
|
|
18
|
+
programasweights-0.1.0.dev8.dist-info/RECORD,,
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: programasweights
|
|
3
|
-
Version: 0.1.0.dev6
|
|
4
|
-
Summary: Compile natural language specifications into neural programs that run locally via llama.cpp.
|
|
5
|
-
Author-email: ProgramAsWeights <support@programasweights.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Keywords: inference,llama-cpp,lora,neural-programs,nlp
|
|
8
|
-
Classifier: Development Status :: 3 - Alpha
|
|
9
|
-
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: Intended Audience :: Science/Research
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
-
Requires-Python: >=3.9
|
|
20
|
-
Requires-Dist: httpx<1.0,>=0.27.0
|
|
21
|
-
Requires-Dist: llama-cpp-python<1.0,>=0.3.0
|
|
22
|
-
Provides-Extra: test
|
|
23
|
-
Requires-Dist: pytest; extra == 'test'
|
|
24
|
-
Description-Content-Type: text/markdown
|
|
25
|
-
|
|
26
|
-
# ProgramAsWeights
|
|
27
|
-
|
|
28
|
-
**Compile natural language specifications into neural programs (.paw files) that run locally.**
|
|
29
|
-
|
|
30
|
-
Programs are stored as weight blobs (KV cache prefix + optional LoRA adapters) interpreted by a small fixed model. No API calls needed at runtime — fully deterministic, local execution.
|
|
31
|
-
|
|
32
|
-
## Installation
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
pip install programasweights
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Quick Start
|
|
39
|
-
|
|
40
|
-
### Run a Program
|
|
41
|
-
|
|
42
|
-
```python
|
|
43
|
-
import programasweights as paw
|
|
44
|
-
|
|
45
|
-
# Load and run a compiled program
|
|
46
|
-
fn = paw.function("program_id_or_path.paw")
|
|
47
|
-
result = fn("Contact alice@company.com or bob@example.org")
|
|
48
|
-
print(result) # ["alice@company.com", "bob@example.org"]
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Compile a Program
|
|
52
|
-
|
|
53
|
-
```python
|
|
54
|
-
import programasweights as paw
|
|
55
|
-
|
|
56
|
-
# Compile from natural language specification
|
|
57
|
-
paw.compile(
|
|
58
|
-
"output.paw",
|
|
59
|
-
spec="Extract all email addresses from text and return as JSON list",
|
|
60
|
-
checkpoint_dir="path/to/trained/compiler",
|
|
61
|
-
)
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## LoRA Support (PEFT Compatible)
|
|
65
|
-
|
|
66
|
-
Already using PEFT for LoRA training? Convert to .paw in one line:
|
|
67
|
-
|
|
68
|
-
```python
|
|
69
|
-
import programasweights as paw
|
|
70
|
-
|
|
71
|
-
# Standard PEFT workflow:
|
|
72
|
-
# model = get_peft_model(base_model, LoraConfig(r=16, target_modules=["q_proj", "v_proj"]))
|
|
73
|
-
# trainer.train()
|
|
74
|
-
# model.save_pretrained("my_adapter/")
|
|
75
|
-
|
|
76
|
-
# Convert to .paw:
|
|
77
|
-
paw.from_peft(
|
|
78
|
-
"my_adapter/", # Your PEFT checkpoint
|
|
79
|
-
"sentiment.paw", # Output .paw file
|
|
80
|
-
spec="Classify sentiment as positive or negative",
|
|
81
|
-
tags=["sentiment", "classification"],
|
|
82
|
-
examples=[
|
|
83
|
-
{"input": "Great movie!", "output": "positive"},
|
|
84
|
-
{"input": "Terrible film.", "output": "negative"},
|
|
85
|
-
],
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
# Use it:
|
|
89
|
-
fn = paw.function("sentiment.paw")
|
|
90
|
-
print(fn("This is amazing!")) # → "positive"
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Load LoRA from a .paw file:
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
lora_weights, lora_config = paw.load_paw_lora("sentiment.paw")
|
|
97
|
-
print(lora_config) # {"rank": 16, "alpha": 32, ...}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Or use `save_lora_to_paw()` directly if you have raw tensors instead of a PEFT checkpoint.
|
|
101
|
-
|
|
102
|
-
## .paw File Format v2
|
|
103
|
-
|
|
104
|
-
A `.paw` file is a self-contained neural program that includes:
|
|
105
|
-
|
|
106
|
-
| Component | Description | Required |
|
|
107
|
-
|-----------|-------------|----------|
|
|
108
|
-
| KV cache prefix | Continuous program (prefix weights) | Optional |
|
|
109
|
-
| Pseudo-program | Discrete text instructions | Optional |
|
|
110
|
-
| LoRA adapter | Fine-tuned adapter weights | Optional |
|
|
111
|
-
| Generation config | Temperature, top_p, max_tokens | Optional |
|
|
112
|
-
| Metadata | Interpreter model, spec, author, tags | Required |
|
|
113
|
-
|
|
114
|
-
## Program Hub
|
|
115
|
-
|
|
116
|
-
Browse and share programs at [hub.programasweights.com](https://hub.programasweights.com)
|
|
117
|
-
|
|
118
|
-
## Links
|
|
119
|
-
|
|
120
|
-
- **Website**: [programasweights.com](https://programasweights.com)
|
|
121
|
-
- **Documentation**: [programasweights.readthedocs.io](https://programasweights.readthedocs.io)
|
|
122
|
-
- **GitHub**: [github.com/programasweights/programasweights](https://github.com/programasweights/programasweights)
|
|
123
|
-
- **Program Hub**: [hub.programasweights.com](https://hub.programasweights.com)
|
|
124
|
-
|
|
125
|
-
## License
|
|
126
|
-
|
|
127
|
-
MIT
|
|
File without changes
|
{programasweights-0.1.0.dev6.dist-info → programasweights-0.1.0.dev8.dist-info}/entry_points.txt
RENAMED
|
File without changes
|