programasweights 0.4.6__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.
- {programasweights-0.4.6 → programasweights-0.4.8}/CHANGELOG.md +16 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/PKG-INFO +15 -1
- {programasweights-0.4.6 → programasweights-0.4.8}/PYPI_README.md +14 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/README.md +14 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/__init__.py +64 -8
- programasweights-0.4.8/programasweights/_remote.py +99 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/cli.py +27 -10
- {programasweights-0.4.6 → programasweights-0.4.8}/pyproject.toml +11 -1
- programasweights-0.4.6/.github/workflows/test.yml +0 -82
- programasweights-0.4.6/.readthedocs.yaml +0 -13
- programasweights-0.4.6/AGENTS.md +0 -255
- programasweights-0.4.6/docs/adr/001-llama-cpp-over-pytorch.md +0 -21
- programasweights-0.4.6/docs/adr/002-q4_0-adapter-format.md +0 -32
- programasweights-0.4.6/docs/adr/003-single-spec-field.md +0 -26
- programasweights-0.4.6/docs/adr/004-compiler-naming.md +0 -32
- programasweights-0.4.6/docs/adr/005-vllm-hidden-states.md +0 -24
- programasweights-0.4.6/docs/adr/006-email-api-key-auth.md +0 -41
- programasweights-0.4.6/docs/advanced/adrs.md +0 -67
- programasweights-0.4.6/docs/advanced/architecture.md +0 -47
- programasweights-0.4.6/docs/api-reference/cli.md +0 -105
- programasweights-0.4.6/docs/api-reference/python-sdk.md +0 -332
- programasweights-0.4.6/docs/api-reference/rest-api.md +0 -180
- programasweights-0.4.6/docs/architecture.md +0 -38
- programasweights-0.4.6/docs/case-studies/alien-taboo.md +0 -117
- programasweights-0.4.6/docs/case-studies/log-monitoring.md +0 -132
- programasweights-0.4.6/docs/case-studies/semantic-search.md +0 -146
- programasweights-0.4.6/docs/case-studies/site-navigation.md +0 -127
- programasweights-0.4.6/docs/case-studies/tool-calling.md +0 -477
- programasweights-0.4.6/docs/getting-started/first-program.md +0 -77
- programasweights-0.4.6/docs/getting-started/installation.md +0 -54
- programasweights-0.4.6/docs/getting-started/naming-programs.md +0 -78
- programasweights-0.4.6/docs/guide/browser-inference.md +0 -141
- programasweights-0.4.6/docs/guide/how-it-works.md +0 -47
- programasweights-0.4.6/docs/guide/local-inference.md +0 -48
- programasweights-0.4.6/docs/guide/writing-good-specs.md +0 -11
- programasweights-0.4.6/docs/hub/browsing-programs.md +0 -37
- programasweights-0.4.6/docs/hub/feedback-cases.md +0 -34
- programasweights-0.4.6/docs/hub/publishing-programs.md +0 -31
- programasweights-0.4.6/docs/index.md +0 -82
- programasweights-0.4.6/docs/requirements.txt +0 -2
- programasweights-0.4.6/examples/flask_app.py +0 -52
- programasweights-0.4.6/examples/jupyter_notebook.py +0 -52
- programasweights-0.4.6/examples/langchain_integration.py +0 -52
- programasweights-0.4.6/examples/paw_monitor.py +0 -180
- programasweights-0.4.6/examples/replace_openai.py +0 -44
- programasweights-0.4.6/mkdocs.yml +0 -87
- programasweights-0.4.6/tests/test_api_errors.py +0 -222
- programasweights-0.4.6/tests/test_base_interpreter.py +0 -845
- programasweights-0.4.6/tests/test_cli_auth.py +0 -265
- programasweights-0.4.6/tests/test_compile_timeouts.py +0 -129
- programasweights-0.4.6/tests/test_desktop_sdk.py +0 -1221
- programasweights-0.4.6/tests/test_local_program.py +0 -676
- programasweights-0.4.6/tests/test_offline_cache.py +0 -97
- programasweights-0.4.6/tests/test_runtime_registry_sdk.py +0 -123
- programasweights-0.4.6/tests/test_sdk.py +0 -484
- programasweights-0.4.6/tests/test_sdk.sh +0 -89
- {programasweights-0.4.6 → programasweights-0.4.8}/.gitignore +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/LICENSE +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/_output.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/_program_reference.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/artifacts.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/cache.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/client.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/compiler/__init__.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/compiler/dummy.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/config.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/convert_peft_to_paw.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/errors.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/local_program.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/paw_format.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/runtime/__init__.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/runtime/interpreter.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/runtime/interpreter_onnx.py +0 -0
- {programasweights-0.4.6 → programasweights-0.4.8}/programasweights/runtime_llamacpp.py +0 -0
|
@@ -1,5 +1,21 @@
|
|
|
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
|
+
|
|
8
|
+
## 0.4.7 (2026-09-20)
|
|
9
|
+
|
|
10
|
+
- Add `remote=True` to `paw.function` and `paw.compile_and_load`, plus
|
|
11
|
+
`paw run --remote`, for hosted inference without downloading model assets
|
|
12
|
+
or loading the local runtime. Local inference remains the default.
|
|
13
|
+
- Use server generation defaults unless explicitly overridden. Reuse HTTP
|
|
14
|
+
connections, support `with` and `.close()`, and preserve structured
|
|
15
|
+
`paw.APIError` details.
|
|
16
|
+
- Reject remote inference combined with offline mode or incompatible local
|
|
17
|
+
runtime options.
|
|
18
|
+
|
|
3
19
|
## 0.4.6 (2026-09-13)
|
|
4
20
|
|
|
5
21
|
- Add an optional `logits_processor` argument to a compiled or base program
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: programasweights
|
|
3
|
-
Version: 0.4.
|
|
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
|
|
@@ -66,6 +66,19 @@ fn("I love this!") # "positive"
|
|
|
66
66
|
|
|
67
67
|
If you specifically want the smaller browser-compatible runtime, pass `compiler="paw-4b-gpt2"`. Otherwise, omit `compiler` and let the server default decide.
|
|
68
68
|
|
|
69
|
+
## Remote inference (optional)
|
|
70
|
+
|
|
71
|
+
Use the hosted API for fast inference in around 150 ms, without a local model download.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import programasweights as paw
|
|
75
|
+
|
|
76
|
+
with paw.function("email-triage", remote=True) as remote_fn:
|
|
77
|
+
print(remote_fn("Urgent: the server is down!"))
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For direct HTTP calls, see the [REST API reference](https://programasweights.readthedocs.io/en/latest/api-reference/rest-api/#post-infer).
|
|
81
|
+
|
|
69
82
|
## Current Public Compilers
|
|
70
83
|
|
|
71
84
|
|
|
@@ -189,6 +202,7 @@ Generate API keys at [programasweights.com/settings](https://programasweights.co
|
|
|
189
202
|
paw compile --spec "Extract error lines from logs" --json
|
|
190
203
|
paw run --program <program_id> --input "[ERROR] timeout" --json
|
|
191
204
|
paw run --program <program_id> --input "[ERROR] timeout" --offline --json
|
|
205
|
+
paw run --program <program_id> --input "[ERROR] timeout" --remote --json
|
|
192
206
|
paw login
|
|
193
207
|
```
|
|
194
208
|
|
|
@@ -35,6 +35,19 @@ fn("I love this!") # "positive"
|
|
|
35
35
|
|
|
36
36
|
If you specifically want the smaller browser-compatible runtime, pass `compiler="paw-4b-gpt2"`. Otherwise, omit `compiler` and let the server default decide.
|
|
37
37
|
|
|
38
|
+
## Remote inference (optional)
|
|
39
|
+
|
|
40
|
+
Use the hosted API for fast inference in around 150 ms, without a local model download.
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import programasweights as paw
|
|
44
|
+
|
|
45
|
+
with paw.function("email-triage", remote=True) as remote_fn:
|
|
46
|
+
print(remote_fn("Urgent: the server is down!"))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For direct HTTP calls, see the [REST API reference](https://programasweights.readthedocs.io/en/latest/api-reference/rest-api/#post-infer).
|
|
50
|
+
|
|
38
51
|
## Current Public Compilers
|
|
39
52
|
|
|
40
53
|
|
|
@@ -158,6 +171,7 @@ Generate API keys at [programasweights.com/settings](https://programasweights.co
|
|
|
158
171
|
paw compile --spec "Extract error lines from logs" --json
|
|
159
172
|
paw run --program <program_id> --input "[ERROR] timeout" --json
|
|
160
173
|
paw run --program <program_id> --input "[ERROR] timeout" --offline --json
|
|
174
|
+
paw run --program <program_id> --input "[ERROR] timeout" --remote --json
|
|
161
175
|
paw login
|
|
162
176
|
```
|
|
163
177
|
|
|
@@ -35,6 +35,19 @@ fn("I love this!") # "positive"
|
|
|
35
35
|
|
|
36
36
|
If you specifically want the smaller browser-compatible runtime, pass `compiler="paw-4b-gpt2"`. Otherwise, omit `compiler` and let the server default decide.
|
|
37
37
|
|
|
38
|
+
## Remote inference (optional)
|
|
39
|
+
|
|
40
|
+
Use the hosted API for fast inference in around 150 ms, without a local model download.
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import programasweights as paw
|
|
44
|
+
|
|
45
|
+
with paw.function("email-triage", remote=True) as remote_fn:
|
|
46
|
+
print(remote_fn("Urgent: the server is down!"))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For direct HTTP calls, see the [REST API reference](docs/api-reference/rest-api.md#post-infer).
|
|
50
|
+
|
|
38
51
|
## Current Public Compilers
|
|
39
52
|
|
|
40
53
|
|
|
@@ -159,6 +172,7 @@ Generate API keys at [programasweights.com/settings](https://programasweights.co
|
|
|
159
172
|
paw compile --spec "Extract error lines from logs" --json
|
|
160
173
|
paw run --program <program_id> --input "[ERROR] timeout" --json
|
|
161
174
|
paw run --program <program_id> --input "[ERROR] timeout" --offline --json
|
|
175
|
+
paw run --program <program_id> --input "[ERROR] timeout" --remote --json
|
|
162
176
|
paw login
|
|
163
177
|
```
|
|
164
178
|
|
|
@@ -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.
|
|
30
|
+
__version__ = "0.4.8"
|
|
31
31
|
|
|
32
32
|
from ._output import ProgressCallback, ProgressEvent, report_progress
|
|
33
33
|
from .cache import CachedProgram
|
|
@@ -390,6 +390,23 @@ def list_cached_programs() -> list[CachedProgram]:
|
|
|
390
390
|
return _list_cached_programs()
|
|
391
391
|
|
|
392
392
|
|
|
393
|
+
def _validate_remote_options(
|
|
394
|
+
*,
|
|
395
|
+
n_ctx: int,
|
|
396
|
+
n_gpu_layers: int | None,
|
|
397
|
+
verbose: bool,
|
|
398
|
+
offline: bool,
|
|
399
|
+
interpreter: str | None = None,
|
|
400
|
+
) -> None:
|
|
401
|
+
if offline:
|
|
402
|
+
raise ValueError("remote=True cannot be combined with offline mode.")
|
|
403
|
+
if (
|
|
404
|
+
n_ctx != 2048 or n_gpu_layers is not None
|
|
405
|
+
or verbose or interpreter is not None
|
|
406
|
+
):
|
|
407
|
+
raise ValueError("Local runtime options cannot be used with remote=True.")
|
|
408
|
+
|
|
409
|
+
|
|
393
410
|
def function(
|
|
394
411
|
program_id,
|
|
395
412
|
n_ctx: int = 2048,
|
|
@@ -397,13 +414,15 @@ def function(
|
|
|
397
414
|
verbose: bool = False,
|
|
398
415
|
offline: bool = False,
|
|
399
416
|
*,
|
|
417
|
+
remote: bool = False,
|
|
400
418
|
interpreter: str | None = None,
|
|
401
419
|
):
|
|
402
420
|
"""Load a compiled program, or explicitly load a bare base interpreter.
|
|
403
421
|
|
|
404
|
-
Hub references download the .paw bundle on first use;
|
|
405
|
-
it directly. Required runtime metadata and base models
|
|
406
|
-
unless offline mode is enabled. Subsequent calls reuse
|
|
422
|
+
For local inference, Hub references download the .paw bundle on first use;
|
|
423
|
+
local paths supply it directly. Required runtime metadata and base models
|
|
424
|
+
may still download unless offline mode is enabled. Subsequent calls reuse
|
|
425
|
+
validated caches.
|
|
407
426
|
|
|
408
427
|
Args:
|
|
409
428
|
program_id: Program ID (str), slug (``da03/my-program``), pinned version
|
|
@@ -418,12 +437,15 @@ def function(
|
|
|
418
437
|
offline: Prohibit network access. Local bundles may be imported, but
|
|
419
438
|
their runtime and base model must already be available locally.
|
|
420
439
|
Also set via ``PAW_OFFLINE=1`` env var.
|
|
440
|
+
remote: Run hosted inference without downloading model assets. Accepts
|
|
441
|
+
program IDs, slugs, pinned versions, and Program objects. Cannot
|
|
442
|
+
be combined with offline mode or non-default local runtime options.
|
|
421
443
|
interpreter: Advanced adapter-free mode. This is only valid when
|
|
422
444
|
``program_id`` is explicitly ``None``. Initially supported values
|
|
423
445
|
are ``"Qwen/Qwen3-0.6B"`` and ``"gpt2"``.
|
|
424
446
|
|
|
425
447
|
Returns:
|
|
426
|
-
A callable
|
|
448
|
+
A callable that takes an input string and returns an output string.
|
|
427
449
|
|
|
428
450
|
Example:
|
|
429
451
|
>>> fn = paw.function("email-triage")
|
|
@@ -432,6 +454,8 @@ def function(
|
|
|
432
454
|
|
|
433
455
|
>>> fn = paw.function("da03/my-program@v2") # pinned version
|
|
434
456
|
|
|
457
|
+
>>> fn = paw.function("email-triage", remote=True)
|
|
458
|
+
|
|
435
459
|
>>> fn = paw.function("./classifier.paw") # local bundle
|
|
436
460
|
|
|
437
461
|
>>> base = paw.function(None, interpreter="gpt2")
|
|
@@ -440,6 +464,22 @@ def function(
|
|
|
440
464
|
from . import cache
|
|
441
465
|
|
|
442
466
|
offline = _offline_requested(offline)
|
|
467
|
+
if remote:
|
|
468
|
+
_validate_remote_options(
|
|
469
|
+
n_ctx=n_ctx,
|
|
470
|
+
n_gpu_layers=n_gpu_layers,
|
|
471
|
+
verbose=verbose,
|
|
472
|
+
offline=offline,
|
|
473
|
+
interpreter=interpreter,
|
|
474
|
+
)
|
|
475
|
+
from ._program_reference import local_program_path
|
|
476
|
+
|
|
477
|
+
reference = _coerce_program_reference(program_id)
|
|
478
|
+
if not reference or local_program_path(reference) is not None:
|
|
479
|
+
raise ValueError("Remote inference requires a program ID or slug.")
|
|
480
|
+
from ._remote import load_remote_function
|
|
481
|
+
|
|
482
|
+
return load_remote_function(reference)
|
|
443
483
|
if n_gpu_layers is None:
|
|
444
484
|
n_gpu_layers = int(os.environ.get("PAW_GPU_LAYERS", "-1"))
|
|
445
485
|
|
|
@@ -567,9 +607,11 @@ def compile_and_load(
|
|
|
567
607
|
n_ctx: int = 2048,
|
|
568
608
|
n_gpu_layers: int | None = None,
|
|
569
609
|
verbose: bool = False,
|
|
610
|
+
*,
|
|
611
|
+
remote: bool = False,
|
|
570
612
|
**compile_kwargs,
|
|
571
613
|
):
|
|
572
|
-
"""Compile a spec and
|
|
614
|
+
"""Compile a spec and load it for local or remote inference.
|
|
573
615
|
|
|
574
616
|
Convenience wrapper that combines ``paw.compile()`` and ``paw.function()``
|
|
575
617
|
into a single call.
|
|
@@ -581,18 +623,32 @@ def compile_and_load(
|
|
|
581
623
|
n_ctx: Context window size for llama.cpp.
|
|
582
624
|
n_gpu_layers: GPU layers (-1 = all, 0 = CPU only).
|
|
583
625
|
verbose: Print llama.cpp debug output.
|
|
626
|
+
remote: Run hosted inference without downloading model assets.
|
|
584
627
|
**compile_kwargs: Additional args passed to compile (slug, public, etc.)
|
|
585
628
|
|
|
586
629
|
Returns:
|
|
587
|
-
A callable
|
|
630
|
+
A callable that takes an input string and returns output.
|
|
588
631
|
|
|
589
632
|
Example:
|
|
590
633
|
>>> fn = paw.compile_and_load("Classify sentiment as positive or negative")
|
|
591
634
|
>>> fn("I love this!")
|
|
592
635
|
'positive'
|
|
593
636
|
"""
|
|
637
|
+
if remote:
|
|
638
|
+
_validate_remote_options(
|
|
639
|
+
n_ctx=n_ctx,
|
|
640
|
+
n_gpu_layers=n_gpu_layers,
|
|
641
|
+
verbose=verbose,
|
|
642
|
+
offline=_offline_requested(False),
|
|
643
|
+
)
|
|
594
644
|
program = compile(spec, compiler=compiler, **compile_kwargs)
|
|
595
|
-
return function(
|
|
645
|
+
return function(
|
|
646
|
+
program,
|
|
647
|
+
n_ctx=n_ctx,
|
|
648
|
+
n_gpu_layers=n_gpu_layers,
|
|
649
|
+
verbose=verbose,
|
|
650
|
+
remote=remote,
|
|
651
|
+
)
|
|
596
652
|
|
|
597
653
|
|
|
598
654
|
def list_versions(slug: str) -> dict:
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from .errors import raise_for_api_status
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _infer(
|
|
9
|
+
client: httpx.Client,
|
|
10
|
+
program_id: str,
|
|
11
|
+
input_text: str,
|
|
12
|
+
*,
|
|
13
|
+
max_tokens: int | None = None,
|
|
14
|
+
temperature: float | None = None,
|
|
15
|
+
) -> str:
|
|
16
|
+
if not isinstance(input_text, str):
|
|
17
|
+
raise TypeError("input_text must be a string.")
|
|
18
|
+
|
|
19
|
+
payload: dict[str, object] = {"program_id": program_id, "input": input_text}
|
|
20
|
+
if max_tokens is not None:
|
|
21
|
+
payload["max_tokens"] = max_tokens
|
|
22
|
+
if temperature is not None:
|
|
23
|
+
payload["temperature"] = temperature
|
|
24
|
+
|
|
25
|
+
response = client.post("api/v1/infer", json=payload)
|
|
26
|
+
raise_for_api_status(response)
|
|
27
|
+
data = response.json()
|
|
28
|
+
if not isinstance(data, dict) or not isinstance(data.get("output"), str):
|
|
29
|
+
raise ValueError("PAW inference returned an invalid output.")
|
|
30
|
+
return data["output"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RemotePawFunction:
|
|
34
|
+
"""A hosted program that owns its HTTP client."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, client: httpx.Client, program_id: str):
|
|
37
|
+
self._client = client
|
|
38
|
+
self._program_id = program_id
|
|
39
|
+
|
|
40
|
+
def __call__(
|
|
41
|
+
self,
|
|
42
|
+
input_text: str,
|
|
43
|
+
max_tokens: int | None = None,
|
|
44
|
+
temperature: float | None = None,
|
|
45
|
+
logits_processor=None,
|
|
46
|
+
) -> str:
|
|
47
|
+
if self._client.is_closed:
|
|
48
|
+
raise RuntimeError("This RemotePawFunction has been closed.")
|
|
49
|
+
if logits_processor is not None:
|
|
50
|
+
raise ValueError("logits_processor is only supported for local inference.")
|
|
51
|
+
return _infer(
|
|
52
|
+
self._client,
|
|
53
|
+
self._program_id,
|
|
54
|
+
input_text,
|
|
55
|
+
max_tokens=max_tokens,
|
|
56
|
+
temperature=temperature,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def close(self) -> None:
|
|
60
|
+
self._client.close()
|
|
61
|
+
|
|
62
|
+
def __enter__(self) -> RemotePawFunction:
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
def __exit__(self, exc_type, exc, traceback) -> None:
|
|
66
|
+
self.close()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def load_remote_function(reference: str) -> RemotePawFunction:
|
|
70
|
+
from urllib.parse import quote
|
|
71
|
+
|
|
72
|
+
from .cache import is_program_id
|
|
73
|
+
from .config import get_api_key, get_api_url
|
|
74
|
+
|
|
75
|
+
headers = {}
|
|
76
|
+
api_key = get_api_key()
|
|
77
|
+
if api_key:
|
|
78
|
+
headers["X-API-Key"] = api_key
|
|
79
|
+
client = httpx.Client(
|
|
80
|
+
base_url=get_api_url().rstrip("/") + "/",
|
|
81
|
+
headers=headers,
|
|
82
|
+
timeout=60.0,
|
|
83
|
+
)
|
|
84
|
+
try:
|
|
85
|
+
program_id = reference
|
|
86
|
+
if not is_program_id(reference):
|
|
87
|
+
response = client.get(
|
|
88
|
+
"api/v1/programs/resolve/" + quote(reference, safe=""),
|
|
89
|
+
timeout=10.0,
|
|
90
|
+
)
|
|
91
|
+
raise_for_api_status(response)
|
|
92
|
+
data = response.json()
|
|
93
|
+
program_id = data.get("program_id") if isinstance(data, dict) else None
|
|
94
|
+
if not isinstance(program_id, str) or not is_program_id(program_id):
|
|
95
|
+
raise ValueError("PAW returned an invalid program ID.")
|
|
96
|
+
return RemotePawFunction(client, program_id)
|
|
97
|
+
except BaseException:
|
|
98
|
+
client.close()
|
|
99
|
+
raise
|
|
@@ -4,7 +4,7 @@ ProgramAsWeights CLI.
|
|
|
4
4
|
|
|
5
5
|
Usage:
|
|
6
6
|
paw compile --spec "..." Compile a spec on the server
|
|
7
|
-
paw run --program <id> --input "..." Run
|
|
7
|
+
paw run --program <id> --input "..." Run locally (or add --remote)
|
|
8
8
|
paw rename <program> <slug> Set or change a program's slug
|
|
9
9
|
paw info <program> Show program metadata
|
|
10
10
|
paw login [key] Save API key for authentication
|
|
@@ -85,6 +85,9 @@ def cmd_run(args):
|
|
|
85
85
|
program = getattr(args, "program", None)
|
|
86
86
|
interpreter = getattr(args, "interpreter", None)
|
|
87
87
|
offline = bool(getattr(args, "offline", False))
|
|
88
|
+
remote = bool(getattr(args, "remote", False))
|
|
89
|
+
if remote and (base_mode or offline):
|
|
90
|
+
raise ValueError("--remote cannot be combined with --base or --offline.")
|
|
88
91
|
if base_mode:
|
|
89
92
|
if program is not None:
|
|
90
93
|
raise ValueError("--base cannot be combined with --program.")
|
|
@@ -98,13 +101,24 @@ def cmd_run(args):
|
|
|
98
101
|
"--interpreter is only valid together with --base."
|
|
99
102
|
)
|
|
100
103
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
verbose=args.verbose,
|
|
104
|
-
offline=offline,
|
|
105
|
-
interpreter=interpreter,
|
|
104
|
+
load_kwargs = dict(
|
|
105
|
+
verbose=args.verbose, offline=offline, interpreter=interpreter,
|
|
106
106
|
)
|
|
107
|
-
|
|
107
|
+
if remote:
|
|
108
|
+
load_kwargs["remote"] = True
|
|
109
|
+
max_tokens = args.max_tokens
|
|
110
|
+
temperature = args.temperature
|
|
111
|
+
if not remote:
|
|
112
|
+
if max_tokens is None:
|
|
113
|
+
max_tokens = 512
|
|
114
|
+
if temperature is None:
|
|
115
|
+
temperature = 0.0
|
|
116
|
+
fn = paw.function(None if base_mode else program, **load_kwargs)
|
|
117
|
+
try:
|
|
118
|
+
result = fn(args.input, max_tokens=max_tokens, temperature=temperature)
|
|
119
|
+
finally:
|
|
120
|
+
if remote:
|
|
121
|
+
fn.close()
|
|
108
122
|
|
|
109
123
|
if args.json:
|
|
110
124
|
print(
|
|
@@ -204,7 +218,7 @@ def main():
|
|
|
204
218
|
p.add_argument("--private", action="store_true", help="Make program private (not listed on hub)")
|
|
205
219
|
p.add_argument("--json", action="store_true", help="JSON output")
|
|
206
220
|
|
|
207
|
-
p = sub.add_parser("run", help="Run a program locally
|
|
221
|
+
p = sub.add_parser("run", help="Run a program locally or remotely")
|
|
208
222
|
run_mode = p.add_mutually_exclusive_group(required=True)
|
|
209
223
|
run_mode.add_argument(
|
|
210
224
|
"--program",
|
|
@@ -222,8 +236,9 @@ def main():
|
|
|
222
236
|
help="Base interpreter; only valid with --base",
|
|
223
237
|
)
|
|
224
238
|
p.add_argument("--input", required=True, help="Input text")
|
|
225
|
-
p.add_argument("--max-tokens", type=int, default=
|
|
226
|
-
p.add_argument("--temperature", type=float, default=
|
|
239
|
+
p.add_argument("--max-tokens", type=int, default=None)
|
|
240
|
+
p.add_argument("--temperature", type=float, default=None)
|
|
241
|
+
p.add_argument("--remote", action="store_true", help="Use hosted inference")
|
|
227
242
|
p.add_argument("--verbose", action="store_true")
|
|
228
243
|
p.add_argument(
|
|
229
244
|
"--offline",
|
|
@@ -250,6 +265,8 @@ def main():
|
|
|
250
265
|
parser.print_help()
|
|
251
266
|
return 0
|
|
252
267
|
if args.command == "run":
|
|
268
|
+
if args.remote and (args.base or args.offline):
|
|
269
|
+
parser.error("--remote cannot be combined with --base or --offline")
|
|
253
270
|
if args.base and args.interpreter is None:
|
|
254
271
|
parser.error("paw run --base requires --interpreter")
|
|
255
272
|
if args.program is not None and args.interpreter is not None:
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "programasweights"
|
|
7
|
-
version = "0.4.
|
|
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,82 +0,0 @@
|
|
|
1
|
-
name: tests
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
test:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
strategy:
|
|
12
|
-
fail-fast: false
|
|
13
|
-
matrix:
|
|
14
|
-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
15
|
-
steps:
|
|
16
|
-
- uses: actions/checkout@v4
|
|
17
|
-
|
|
18
|
-
- name: Set up Python ${{ matrix.python-version }}
|
|
19
|
-
uses: actions/setup-python@v5
|
|
20
|
-
with:
|
|
21
|
-
python-version: ${{ matrix.python-version }}
|
|
22
|
-
|
|
23
|
-
- name: Install (hermetic deps only)
|
|
24
|
-
# Install httpx + pytest and the package itself without pulling the heavy
|
|
25
|
-
# llama-cpp-python build. Runtime tests inject a fake llama_cpp module,
|
|
26
|
-
# so CI needs neither the native extension nor a model download.
|
|
27
|
-
run: |
|
|
28
|
-
python -m pip install --upgrade pip
|
|
29
|
-
python -m pip install httpx pytest
|
|
30
|
-
python -m pip install -e . --no-deps
|
|
31
|
-
|
|
32
|
-
- name: Run hermetic tests
|
|
33
|
-
# Scoped to tests that need no network, no model download, and no
|
|
34
|
-
# PAW_API_KEY. Auth tests (@needs_auth) auto-skip without a key; the
|
|
35
|
-
# network/model-download tests in test_sdk.py are excluded here and can
|
|
36
|
-
# be run separately against a live server.
|
|
37
|
-
run: |
|
|
38
|
-
pytest \
|
|
39
|
-
tests/test_api_errors.py \
|
|
40
|
-
tests/test_compile_timeouts.py \
|
|
41
|
-
tests/test_local_program.py \
|
|
42
|
-
tests/test_base_interpreter.py \
|
|
43
|
-
tests/test_cli_auth.py \
|
|
44
|
-
tests/test_desktop_sdk.py \
|
|
45
|
-
tests/test_runtime_registry_sdk.py \
|
|
46
|
-
tests/test_sdk.py::TestInstallAndImport
|
|
47
|
-
|
|
48
|
-
local-files-windows:
|
|
49
|
-
runs-on: windows-latest
|
|
50
|
-
strategy:
|
|
51
|
-
fail-fast: false
|
|
52
|
-
matrix:
|
|
53
|
-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
54
|
-
steps:
|
|
55
|
-
- uses: actions/checkout@v4
|
|
56
|
-
- uses: actions/setup-python@v5
|
|
57
|
-
with:
|
|
58
|
-
python-version: ${{ matrix.python-version }}
|
|
59
|
-
- name: Install hermetic test dependencies
|
|
60
|
-
run: |
|
|
61
|
-
python -m pip install httpx pytest
|
|
62
|
-
python -m pip install -e . --no-deps
|
|
63
|
-
- name: Test Windows local paths, cache locks, and compile errors
|
|
64
|
-
run: python -m pytest tests/test_api_errors.py tests/test_compile_timeouts.py tests/test_local_program.py --junitxml=test-results.xml
|
|
65
|
-
- name: Annotate Windows test failures
|
|
66
|
-
if: failure()
|
|
67
|
-
shell: python
|
|
68
|
-
run: |
|
|
69
|
-
from pathlib import Path
|
|
70
|
-
import xml.etree.ElementTree as ET
|
|
71
|
-
|
|
72
|
-
report = Path("test-results.xml")
|
|
73
|
-
if report.exists():
|
|
74
|
-
for case in ET.parse(report).iter("testcase"):
|
|
75
|
-
for result in case:
|
|
76
|
-
if result.tag in {"failure", "error"}:
|
|
77
|
-
# GitHub truncates annotations, so retain the actual
|
|
78
|
-
# exception at the end of long pytest tracebacks.
|
|
79
|
-
detail = result.text or result.get("message", "")
|
|
80
|
-
message = f"{case.get('name')}: {detail[-3500:]}"
|
|
81
|
-
message = message.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
|
|
82
|
-
print(f"::error::{message}")
|