wafer-cli 0.2.14__py3-none-any.whl → 0.2.30__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.
- wafer/GUIDE.md +1 -1
- wafer/agent_defaults.py +42 -0
- wafer/auth.py +7 -0
- wafer/billing.py +6 -6
- wafer/cli.py +905 -131
- wafer/cli_instructions.py +143 -0
- wafer/corpus.py +313 -15
- wafer/evaluate.py +480 -146
- wafer/global_config.py +13 -0
- wafer/kernel_scope.py +1 -1
- wafer/ncu_analyze.py +1 -1
- wafer/nsys_analyze.py +1 -1
- wafer/skills/wafer-guide/SKILL.md +22 -6
- wafer/specs_cli.py +157 -0
- wafer/ssh_keys.py +6 -6
- wafer/targets_cli.py +472 -0
- wafer/targets_ops.py +29 -2
- wafer/templates/ask_docs.py +1 -1
- wafer/templates/optimize_kernel.py +3 -1
- wafer/templates/optimize_kernelbench.py +17 -62
- wafer/templates/trace_analyze.py +1 -1
- wafer/tests/test_eval_cli_parity.py +199 -0
- wafer/trace_compare.py +274 -0
- wafer/wevin_cli.py +125 -26
- wafer/workspaces.py +163 -16
- wafer_cli-0.2.30.dist-info/METADATA +107 -0
- wafer_cli-0.2.30.dist-info/RECORD +47 -0
- wafer_cli-0.2.14.dist-info/METADATA +0 -16
- wafer_cli-0.2.14.dist-info/RECORD +0 -41
- {wafer_cli-0.2.14.dist-info → wafer_cli-0.2.30.dist-info}/WHEEL +0 -0
- {wafer_cli-0.2.14.dist-info → wafer_cli-0.2.30.dist-info}/entry_points.txt +0 -0
- {wafer_cli-0.2.14.dist-info → wafer_cli-0.2.30.dist-info}/top_level.txt +0 -0
wafer/global_config.py
CHANGED
|
@@ -234,7 +234,20 @@ def get_supabase_anon_key() -> str:
|
|
|
234
234
|
|
|
235
235
|
The anon key is public and used for client-side auth operations
|
|
236
236
|
like token refresh.
|
|
237
|
+
|
|
238
|
+
If SUPABASE_URL is set via env var, infer the matching anon key
|
|
239
|
+
from the built-in environments. Otherwise, use the config file's environment.
|
|
237
240
|
"""
|
|
241
|
+
supabase_url = get_supabase_url()
|
|
242
|
+
|
|
243
|
+
# If SUPABASE_URL was set via env var, find matching environment
|
|
244
|
+
if os.environ.get("SUPABASE_URL"):
|
|
245
|
+
# Check built-in environments to find matching Supabase URL
|
|
246
|
+
for env_name, env in BUILTIN_ENVIRONMENTS.items():
|
|
247
|
+
if env.supabase_url == supabase_url:
|
|
248
|
+
return env.supabase_anon_key
|
|
249
|
+
|
|
250
|
+
# Otherwise, use config file's environment
|
|
238
251
|
return load_global_config().get_api_environment().supabase_anon_key
|
|
239
252
|
|
|
240
253
|
|
wafer/kernel_scope.py
CHANGED
|
@@ -95,7 +95,7 @@ def analyze_command(
|
|
|
95
95
|
if not api_url or not auth_headers:
|
|
96
96
|
raise RuntimeError(
|
|
97
97
|
"API authentication required for .co file analysis. "
|
|
98
|
-
"Run 'wafer login' first."
|
|
98
|
+
"Run 'wafer auth login' first."
|
|
99
99
|
)
|
|
100
100
|
result = analyze_code_object(target_path, api_url, auth_headers)
|
|
101
101
|
# ISA files - use kernel_index parameter
|
wafer/ncu_analyze.py
CHANGED
|
@@ -520,7 +520,7 @@ def _analyze_remote_api(
|
|
|
520
520
|
|
|
521
521
|
except httpx.HTTPStatusError as e:
|
|
522
522
|
if e.response.status_code == 401:
|
|
523
|
-
raise RuntimeError("Not authenticated. Run: wafer login") from e
|
|
523
|
+
raise RuntimeError("Not authenticated. Run: wafer auth login") from e
|
|
524
524
|
raise RuntimeError(f"API error: {e.response.status_code} - {e.response.text}") from e
|
|
525
525
|
except httpx.RequestError as e:
|
|
526
526
|
raise RuntimeError(f"Could not reach API: {e}") from e
|
wafer/nsys_analyze.py
CHANGED
|
@@ -844,7 +844,7 @@ def _analyze_remote_api(
|
|
|
844
844
|
|
|
845
845
|
except httpx.HTTPStatusError as e:
|
|
846
846
|
if e.response.status_code == 401:
|
|
847
|
-
raise RuntimeError("Not authenticated. Run: wafer login") from e
|
|
847
|
+
raise RuntimeError("Not authenticated. Run: wafer auth login") from e
|
|
848
848
|
raise RuntimeError(f"API error: {e.response.status_code} - {e.response.text}") from e
|
|
849
849
|
except httpx.RequestError as e:
|
|
850
850
|
raise RuntimeError(f"Could not reach API: {e}") from e
|
|
@@ -16,7 +16,7 @@ Before using Wafer CLI commands, install the tool:
|
|
|
16
16
|
uv tool install wafer-cli
|
|
17
17
|
|
|
18
18
|
# Authenticate (one-time setup)
|
|
19
|
-
wafer login
|
|
19
|
+
wafer auth login
|
|
20
20
|
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -71,15 +71,31 @@ Test correctness and measure speedup against a reference:
|
|
|
71
71
|
wafer evaluate make-template ./my-kernel
|
|
72
72
|
# Creates: kernel.py, reference.py, test_cases.json
|
|
73
73
|
|
|
74
|
-
#
|
|
75
|
-
|
|
74
|
+
# test_cases.json format:
|
|
75
|
+
# [{"name": "small", "n": 1024, "seed": 42}, {"name": "large", "n": 1048576, "seed": 42}]
|
|
76
|
+
# Each dict is passed as **kwargs to generate_input() in reference.py
|
|
77
|
+
|
|
78
|
+
# Run correctness check (GPUMode functional format)
|
|
79
|
+
wafer evaluate gpumode \
|
|
76
80
|
--impl ./my-kernel/kernel.py \
|
|
77
81
|
--reference ./my-kernel/reference.py \
|
|
78
82
|
--test-cases ./my-kernel/test_cases.json \
|
|
79
83
|
--target <target-name>
|
|
80
84
|
|
|
81
|
-
#
|
|
82
|
-
wafer evaluate
|
|
85
|
+
# Run correctness + benchmark (measures speedup vs reference)
|
|
86
|
+
wafer evaluate gpumode \
|
|
87
|
+
--impl ./my-kernel/kernel.py \
|
|
88
|
+
--reference ./my-kernel/reference.py \
|
|
89
|
+
--test-cases ./my-kernel/test_cases.json \
|
|
90
|
+
--target <target-name> --benchmark
|
|
91
|
+
|
|
92
|
+
# Run with defensive timing (detects evaluation hacking)
|
|
93
|
+
wafer evaluate gpumode ... --benchmark --defensive
|
|
94
|
+
|
|
95
|
+
# KernelBench format (ModelNew class)
|
|
96
|
+
wafer evaluate kernelbench \
|
|
97
|
+
--impl my_kernel.py --reference problem.py \
|
|
98
|
+
--target <target-name> --stages all
|
|
83
99
|
```
|
|
84
100
|
|
|
85
101
|
### 4. AI-Assisted Optimization
|
|
@@ -126,4 +142,4 @@ wafer config targets init runpod # RunPod cloud GPUs
|
|
|
126
142
|
wafer config targets init digitalocean # DigitalOcean AMD GPUs
|
|
127
143
|
```
|
|
128
144
|
|
|
129
|
-
Then use: `wafer evaluate --target <name> ...`
|
|
145
|
+
Then use: `wafer evaluate gpumode --target <name> ...`
|
wafer/specs_cli.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""CLI commands for wafer specs — TargetSpec TOML management.
|
|
2
|
+
|
|
3
|
+
These are the local config commands (no API calls).
|
|
4
|
+
Registered as: wafer specs list|show|add|remove|default|init
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
specs_app = typer.Typer(
|
|
14
|
+
help="""Manage GPU target specs (provisioning blueprints).
|
|
15
|
+
|
|
16
|
+
Specs define how to access or provision GPUs. They are TOML files in ~/.wafer/specs/.
|
|
17
|
+
|
|
18
|
+
wafer specs list # List all specs
|
|
19
|
+
wafer specs show runpod-mi300x # Show one spec
|
|
20
|
+
wafer specs add /path/to/spec.toml # Add from file
|
|
21
|
+
wafer specs remove old-target # Remove a spec
|
|
22
|
+
wafer specs default runpod-mi300x # Set default
|
|
23
|
+
|
|
24
|
+
To create a new spec interactively:
|
|
25
|
+
wafer config targets init ssh # (legacy, still works)
|
|
26
|
+
wafer config targets init runpod
|
|
27
|
+
"""
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@specs_app.command("list")
|
|
32
|
+
def specs_list() -> None:
|
|
33
|
+
"""List all configured specs.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
wafer specs list
|
|
37
|
+
"""
|
|
38
|
+
from wafer_core.targets.spec_store import list_spec_names, load_spec
|
|
39
|
+
|
|
40
|
+
from .targets import get_default_target
|
|
41
|
+
|
|
42
|
+
names = list_spec_names()
|
|
43
|
+
default = get_default_target()
|
|
44
|
+
|
|
45
|
+
if not names:
|
|
46
|
+
typer.echo("No specs configured.")
|
|
47
|
+
typer.echo("Add one with: wafer specs add <path/to/spec.toml>")
|
|
48
|
+
typer.echo("Or interactively: wafer config targets init ssh")
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
typer.echo("Configured specs:")
|
|
52
|
+
for name in names:
|
|
53
|
+
marker = " (default)" if name == default else ""
|
|
54
|
+
try:
|
|
55
|
+
spec = load_spec(name)
|
|
56
|
+
type_name = type(spec).__name__.replace("Target", "")
|
|
57
|
+
typer.echo(f" {name}{marker} [{type_name}] gpu={spec.gpu_type}")
|
|
58
|
+
except Exception as e:
|
|
59
|
+
typer.echo(f" {name}{marker} [error: {e}]")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@specs_app.command("show")
|
|
63
|
+
def specs_show(
|
|
64
|
+
name: str = typer.Argument(..., help="Spec name"),
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Show details for a spec.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
wafer specs show runpod-mi300x
|
|
70
|
+
"""
|
|
71
|
+
from wafer_core.targets.spec_store import load_spec
|
|
72
|
+
|
|
73
|
+
from .targets import get_target_info
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
spec = load_spec(name)
|
|
77
|
+
except FileNotFoundError:
|
|
78
|
+
typer.echo(f"Spec not found: {name}", err=True)
|
|
79
|
+
raise typer.Exit(1) from None
|
|
80
|
+
|
|
81
|
+
typer.echo(f"Spec: {name}")
|
|
82
|
+
for key, value in get_target_info(spec).items():
|
|
83
|
+
typer.echo(f" {key}: {value}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@specs_app.command("add")
|
|
87
|
+
def specs_add(
|
|
88
|
+
file_path: Path = typer.Argument(..., help="Path to TOML spec file"),
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Add a spec from a TOML file.
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
wafer specs add ./my-target.toml
|
|
94
|
+
"""
|
|
95
|
+
import tomllib
|
|
96
|
+
|
|
97
|
+
from wafer_core.targets.spec_store import parse_spec, save_spec
|
|
98
|
+
|
|
99
|
+
if not file_path.exists():
|
|
100
|
+
typer.echo(f"File not found: {file_path}", err=True)
|
|
101
|
+
raise typer.Exit(1) from None
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
with open(file_path, "rb") as f:
|
|
105
|
+
data = tomllib.load(f)
|
|
106
|
+
spec = parse_spec(data)
|
|
107
|
+
save_spec(spec)
|
|
108
|
+
typer.echo(f"Added spec: {spec.name}")
|
|
109
|
+
except Exception as e:
|
|
110
|
+
typer.echo(f"Error: {e}", err=True)
|
|
111
|
+
raise typer.Exit(1) from None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@specs_app.command("remove")
|
|
115
|
+
def specs_remove(
|
|
116
|
+
name: str = typer.Argument(..., help="Spec name to remove"),
|
|
117
|
+
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
118
|
+
) -> None:
|
|
119
|
+
"""Remove a spec.
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
wafer specs remove old-target
|
|
123
|
+
"""
|
|
124
|
+
from wafer_core.targets.spec_store import remove_spec
|
|
125
|
+
|
|
126
|
+
if not force:
|
|
127
|
+
confirm = typer.confirm(f"Remove spec '{name}'?")
|
|
128
|
+
if not confirm:
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
remove_spec(name)
|
|
133
|
+
typer.echo(f"Removed spec: {name}")
|
|
134
|
+
except FileNotFoundError:
|
|
135
|
+
typer.echo(f"Spec not found: {name}", err=True)
|
|
136
|
+
raise typer.Exit(1) from None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@specs_app.command("default")
|
|
140
|
+
def specs_default(
|
|
141
|
+
name: str = typer.Argument(..., help="Spec name to set as default"),
|
|
142
|
+
) -> None:
|
|
143
|
+
"""Set the default spec.
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
wafer specs default runpod-mi300x
|
|
147
|
+
"""
|
|
148
|
+
from wafer_core.targets.spec_store import list_spec_names
|
|
149
|
+
|
|
150
|
+
from .targets import set_default_target
|
|
151
|
+
|
|
152
|
+
if name not in list_spec_names():
|
|
153
|
+
typer.echo(f"Spec not found: {name}", err=True)
|
|
154
|
+
raise typer.Exit(1) from None
|
|
155
|
+
|
|
156
|
+
set_default_target(name)
|
|
157
|
+
typer.echo(f"Default spec set to: {name}")
|
wafer/ssh_keys.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""SSH Keys CLI - Manage SSH public keys for workspace access.
|
|
2
2
|
|
|
3
|
-
This module provides the implementation for the `wafer ssh-keys` subcommand.
|
|
3
|
+
This module provides the implementation for the `wafer config ssh-keys` subcommand.
|
|
4
4
|
Users register their SSH public keys here, which are then installed in all
|
|
5
5
|
workspaces they attach to (BYOK - Bring Your Own Key model).
|
|
6
6
|
"""
|
|
@@ -94,7 +94,7 @@ def list_ssh_keys(json_output: bool = False) -> str:
|
|
|
94
94
|
keys = response.json()
|
|
95
95
|
except httpx.HTTPStatusError as e:
|
|
96
96
|
if e.response.status_code == 401:
|
|
97
|
-
raise RuntimeError("Not authenticated. Run: wafer login") from e
|
|
97
|
+
raise RuntimeError("Not authenticated. Run: wafer auth login") from e
|
|
98
98
|
raise RuntimeError(f"API error: {e.response.status_code} - {e.response.text}") from e
|
|
99
99
|
except httpx.RequestError as e:
|
|
100
100
|
raise RuntimeError(f"Could not reach API: {e}") from e
|
|
@@ -107,7 +107,7 @@ def list_ssh_keys(json_output: bool = False) -> str:
|
|
|
107
107
|
"No SSH keys registered.\n"
|
|
108
108
|
"\n"
|
|
109
109
|
"Add your SSH key:\n"
|
|
110
|
-
" wafer ssh-keys add\n"
|
|
110
|
+
" wafer config ssh-keys add\n"
|
|
111
111
|
"\n"
|
|
112
112
|
"This will auto-detect your key from ~/.ssh/"
|
|
113
113
|
)
|
|
@@ -149,7 +149,7 @@ def add_ssh_key(
|
|
|
149
149
|
" ssh-keygen -t ed25519\n"
|
|
150
150
|
"\n"
|
|
151
151
|
"Or specify a path:\n"
|
|
152
|
-
" wafer ssh-keys add /path/to/key.pub"
|
|
152
|
+
" wafer config ssh-keys add /path/to/key.pub"
|
|
153
153
|
)
|
|
154
154
|
pubkey_path = detected[0]
|
|
155
155
|
|
|
@@ -202,7 +202,7 @@ def add_ssh_key(
|
|
|
202
202
|
key_data = response.json()
|
|
203
203
|
except httpx.HTTPStatusError as e:
|
|
204
204
|
if e.response.status_code == 401:
|
|
205
|
-
raise RuntimeError("Not authenticated. Run: wafer login") from e
|
|
205
|
+
raise RuntimeError("Not authenticated. Run: wafer auth login") from e
|
|
206
206
|
if e.response.status_code == 400:
|
|
207
207
|
# Parse error detail
|
|
208
208
|
try:
|
|
@@ -248,7 +248,7 @@ def remove_ssh_key(key_id: str, json_output: bool = False) -> str:
|
|
|
248
248
|
response.raise_for_status()
|
|
249
249
|
except httpx.HTTPStatusError as e:
|
|
250
250
|
if e.response.status_code == 401:
|
|
251
|
-
raise RuntimeError("Not authenticated. Run: wafer login") from e
|
|
251
|
+
raise RuntimeError("Not authenticated. Run: wafer auth login") from e
|
|
252
252
|
if e.response.status_code == 404:
|
|
253
253
|
raise RuntimeError(f"SSH key not found: {key_id}") from e
|
|
254
254
|
raise RuntimeError(f"API error: {e.response.status_code} - {e.response.text}") from e
|