wafer-cli 0.2.28__tar.gz → 0.2.30__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/PKG-INFO +1 -1
  2. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/pyproject.toml +1 -1
  3. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/evaluate.py +54 -137
  4. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/global_config.py +13 -0
  5. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer_cli.egg-info/PKG-INFO +1 -1
  6. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/README.md +0 -0
  7. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/setup.cfg +0 -0
  8. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_analytics.py +0 -0
  9. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_auth.py +0 -0
  10. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_billing.py +0 -0
  11. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_cli_coverage.py +0 -0
  12. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_cli_parity_integration.py +0 -0
  13. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_config_integration.py +0 -0
  14. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_file_operations_integration.py +0 -0
  15. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_kernel_scope_cli.py +0 -0
  16. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_nsys_analyze.py +0 -0
  17. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_nsys_profile.py +0 -0
  18. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_output.py +0 -0
  19. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_rocprof_compute_integration.py +0 -0
  20. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_skill_commands.py +0 -0
  21. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_ssh_integration.py +0 -0
  22. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_targets_ops.py +0 -0
  23. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_wevin_cli.py +0 -0
  24. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/tests/test_workflow_integration.py +0 -0
  25. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/GUIDE.md +0 -0
  26. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/__init__.py +0 -0
  27. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/agent_defaults.py +0 -0
  28. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/analytics.py +0 -0
  29. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/api_client.py +0 -0
  30. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/auth.py +0 -0
  31. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/autotuner.py +0 -0
  32. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/billing.py +0 -0
  33. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/cli.py +0 -0
  34. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/cli_instructions.py +0 -0
  35. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/config.py +0 -0
  36. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/corpus.py +0 -0
  37. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/gpu_run.py +0 -0
  38. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/inference.py +0 -0
  39. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/kernel_scope.py +0 -0
  40. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/ncu_analyze.py +0 -0
  41. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/nsys_analyze.py +0 -0
  42. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/nsys_profile.py +0 -0
  43. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/output.py +0 -0
  44. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/problems.py +0 -0
  45. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/rocprof_compute.py +0 -0
  46. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/rocprof_sdk.py +0 -0
  47. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/rocprof_systems.py +0 -0
  48. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/skills/wafer-guide/SKILL.md +0 -0
  49. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/specs_cli.py +0 -0
  50. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/ssh_keys.py +0 -0
  51. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/target_lock.py +0 -0
  52. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/targets.py +0 -0
  53. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/targets_cli.py +0 -0
  54. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/targets_ops.py +0 -0
  55. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/templates/__init__.py +0 -0
  56. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/templates/ask_docs.py +0 -0
  57. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/templates/optimize_kernel.py +0 -0
  58. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/templates/optimize_kernelbench.py +0 -0
  59. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/templates/trace_analyze.py +0 -0
  60. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/tests/test_eval_cli_parity.py +0 -0
  61. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/trace_compare.py +0 -0
  62. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/tracelens.py +0 -0
  63. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/wevin_cli.py +0 -0
  64. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer/workspaces.py +0 -0
  65. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer_cli.egg-info/SOURCES.txt +0 -0
  66. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer_cli.egg-info/dependency_links.txt +0 -0
  67. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer_cli.egg-info/entry_points.txt +0 -0
  68. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer_cli.egg-info/requires.txt +0 -0
  69. {wafer_cli-0.2.28 → wafer_cli-0.2.30}/wafer_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wafer-cli
3
- Version: 0.2.28
3
+ Version: 0.2.30
4
4
  Summary: CLI for running GPU workloads, managing remote workspaces, and evaluating/optimizing kernels
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "wafer-cli"
3
- version = "0.2.28"
3
+ version = "0.2.30"
4
4
  description = "CLI for running GPU workloads, managing remote workspaces, and evaluating/optimizing kernels"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -379,18 +379,6 @@ def _build_docker_pip_install_cmd(target: BaremetalTarget | VMTarget) -> str:
379
379
  return " && ".join(commands)
380
380
 
381
381
 
382
- def _get_wafer_root() -> Path:
383
- """Get wafer monorepo root directory.
384
-
385
- Walks up from this file to find the wafer repo root (contains apps/, packages/).
386
- """
387
- current = Path(__file__).resolve()
388
- for parent in [current] + list(current.parents):
389
- if (parent / "apps").is_dir() and (parent / "packages").is_dir():
390
- return parent
391
- raise RuntimeError(f"Could not find wafer root from {__file__}")
392
-
393
-
394
382
  async def run_evaluate_docker(
395
383
  args: EvaluateArgs,
396
384
  target: BaremetalTarget | VMTarget,
@@ -2033,54 +2021,13 @@ async def run_evaluate_runpod(
2033
2021
  error_message=f"Failed to setup Python environment: {e}",
2034
2022
  )
2035
2023
 
2036
- # Upload wafer-core to remote
2037
- try:
2038
- wafer_root = _get_wafer_root()
2039
- wafer_core_path = wafer_root / "packages" / "wafer-core"
2040
- print(f"Uploading wafer-core from {wafer_core_path}...")
2041
-
2042
- wafer_core_remote = f"{REMOTE_WORKSPACE}/wafer-core"
2043
- await client.exec(f"mkdir -p {wafer_core_remote}")
2044
- wafer_core_workspace = await client.expand_path(wafer_core_remote)
2045
-
2046
- upload_result = await client.upload_files(
2047
- str(wafer_core_path), wafer_core_workspace, recursive=True
2048
- )
2049
-
2050
- # Wide event logging for upload result
2051
- upload_event = {
2052
- "event": "wafer_core_upload",
2053
- "target": target.name,
2054
- "target_type": "runpod",
2055
- "ssh_host": f"{client.user}@{client.host}:{client.port}",
2056
- "local_path": str(wafer_core_path),
2057
- "remote_path": wafer_core_workspace,
2058
- "success": upload_result.success,
2059
- "files_copied": upload_result.files_copied,
2060
- "duration_seconds": upload_result.duration_seconds,
2061
- "error_message": upload_result.error_message,
2062
- }
2063
- if upload_result.debug_info:
2064
- upload_event["debug_info"] = upload_result.debug_info
2065
- logger.info(json.dumps(upload_event))
2066
-
2067
- # Fail fast if upload failed
2068
- if not upload_result.success:
2069
- print(f"ERROR: Upload failed: {upload_result.error_message}")
2070
- if upload_result.debug_info:
2071
- print(f"Debug info: {json.dumps(upload_result.debug_info, indent=2)}")
2072
- return EvaluateResult(
2073
- success=False,
2074
- all_correct=False,
2075
- correctness_score=0.0,
2076
- geomean_speedup=0.0,
2077
- passed_tests=0,
2078
- total_tests=0,
2079
- error_message=f"Failed to upload wafer-core: {upload_result.error_message}",
2080
- )
2081
-
2082
- print(f"Uploaded {upload_result.files_copied} files")
2083
- except Exception as e:
2024
+ # Install wafer-core in remote venv
2025
+ print("Installing wafer-core...")
2026
+ install_result = await client.exec(
2027
+ f'export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH" && '
2028
+ f"uv pip install --python {python_exe} wafer-core"
2029
+ )
2030
+ if install_result.exit_code != 0:
2084
2031
  return EvaluateResult(
2085
2032
  success=False,
2086
2033
  all_correct=False,
@@ -2088,7 +2035,7 @@ async def run_evaluate_runpod(
2088
2035
  geomean_speedup=0.0,
2089
2036
  passed_tests=0,
2090
2037
  total_tests=0,
2091
- error_message=f"Failed to upload wafer-core: {e}",
2038
+ error_message=f"Failed to install wafer-core: {install_result.stderr}",
2092
2039
  )
2093
2040
 
2094
2041
  # Select GPU (RunPod pods typically have GPU 0)
@@ -2229,11 +2176,33 @@ async def run_evaluate_runpod(
2229
2176
  error_message=f"Evaluation timed out after {target.eval_timeout}s",
2230
2177
  )
2231
2178
 
2232
- # Parse output
2179
+ # Show output to user
2233
2180
  stdout = result.stdout
2234
2181
  stderr = result.stderr
2182
+ if stdout:
2183
+ print(stdout)
2235
2184
 
2236
2185
  if result.exit_code != 0:
2186
+ error_parts = [f"Evaluation failed (exit code {result.exit_code}):"]
2187
+ if stdout:
2188
+ error_parts.append(f"stdout: {stdout}")
2189
+ if stderr:
2190
+ error_parts.append(f"stderr: {stderr}")
2191
+ return EvaluateResult(
2192
+ success=False,
2193
+ all_correct=False,
2194
+ correctness_score=0.0,
2195
+ geomean_speedup=0.0,
2196
+ passed_tests=0,
2197
+ total_tests=0,
2198
+ error_message="\n".join(error_parts),
2199
+ )
2200
+
2201
+ # Read results from results.json file written by evaluate module
2202
+ results_path = f"{run_path}/results.json"
2203
+ cat_result = await client.exec(f"cat {results_path}")
2204
+
2205
+ if cat_result.exit_code != 0:
2237
2206
  return EvaluateResult(
2238
2207
  success=False,
2239
2208
  all_correct=False,
@@ -2241,20 +2210,12 @@ async def run_evaluate_runpod(
2241
2210
  geomean_speedup=0.0,
2242
2211
  passed_tests=0,
2243
2212
  total_tests=0,
2244
- error_message=f"Evaluation failed:\nstdout: {stdout}\nstderr: {stderr}",
2213
+ error_message=f"Failed to read results: {cat_result.stderr}",
2245
2214
  )
2246
2215
 
2247
- # Find JSON result in output
2248
- result_json = None
2249
- for line in reversed(stdout.strip().split("\n")):
2250
- if line.startswith("{"):
2251
- try:
2252
- result_json = json.loads(line)
2253
- break
2254
- except json.JSONDecodeError:
2255
- continue
2256
-
2257
- if result_json is None:
2216
+ try:
2217
+ results_data = json.loads(cat_result.stdout)
2218
+ except json.JSONDecodeError as e:
2258
2219
  return EvaluateResult(
2259
2220
  success=False,
2260
2221
  all_correct=False,
@@ -2262,10 +2223,12 @@ async def run_evaluate_runpod(
2262
2223
  geomean_speedup=0.0,
2263
2224
  passed_tests=0,
2264
2225
  total_tests=0,
2265
- error_message=f"No JSON result in output:\n{stdout}",
2226
+ error_message=f"Invalid JSON in results: {e}",
2266
2227
  )
2267
2228
 
2268
- if "error" in result_json:
2229
+ # Extract backend results (same format as DigitalOcean/SSH path)
2230
+ backends = results_data.get("backends", [])
2231
+ if not backends:
2269
2232
  return EvaluateResult(
2270
2233
  success=False,
2271
2234
  all_correct=False,
@@ -2273,18 +2236,20 @@ async def run_evaluate_runpod(
2273
2236
  geomean_speedup=0.0,
2274
2237
  passed_tests=0,
2275
2238
  total_tests=0,
2276
- error_message=result_json["error"],
2239
+ error_message="No backend results found",
2277
2240
  )
2278
2241
 
2279
- passed = result_json.get("passed", 0)
2280
- total = result_json.get("total", 0)
2242
+ backend = backends[0]
2243
+ correctness_tests = backend.get("correctness_tests", [])
2244
+ passed = sum(1 for t in correctness_tests if t.get("is_correct", False))
2245
+ total = len(correctness_tests)
2281
2246
  correctness = passed / total if total > 0 else 0.0
2282
2247
 
2283
2248
  return EvaluateResult(
2284
2249
  success=True,
2285
- all_correct=result_json.get("all_correct", False),
2250
+ all_correct=backend.get("all_correct", False),
2286
2251
  correctness_score=correctness,
2287
- geomean_speedup=result_json.get("speedup", 0.0),
2252
+ geomean_speedup=backend.get("geomean_speedup", 0.0),
2288
2253
  passed_tests=passed,
2289
2254
  total_tests=total,
2290
2255
  )
@@ -2385,61 +2350,13 @@ async def run_evaluate_digitalocean(
2385
2350
  error_message=f"Failed to setup Python environment: {e}",
2386
2351
  )
2387
2352
 
2388
- # Upload wafer-core to remote
2389
- try:
2390
- wafer_root = _get_wafer_root()
2391
- wafer_core_path = wafer_root / "packages" / "wafer-core"
2392
- print(f"Uploading wafer-core from {wafer_core_path}...")
2393
-
2394
- wafer_core_remote = f"{REMOTE_WORKSPACE}/wafer-core"
2395
- await client.exec(f"mkdir -p {wafer_core_remote}")
2396
- wafer_core_workspace = await client.expand_path(wafer_core_remote)
2397
-
2398
- # Use SFTP instead of rsync to avoid SSH subprocess timeout issues
2399
- # (DigitalOcean may rate-limit new SSH connections)
2400
- upload_result = await client.upload_files(
2401
- str(wafer_core_path),
2402
- wafer_core_workspace,
2403
- recursive=True,
2404
- use_sftp=True,
2405
- )
2406
-
2407
- # Wide event logging for upload result
2408
- upload_event = {
2409
- "event": "wafer_core_upload",
2410
- "target": target.name,
2411
- "target_type": "digitalocean",
2412
- "ssh_host": f"{client.user}@{client.host}:{client.port}",
2413
- "local_path": str(wafer_core_path),
2414
- "remote_path": wafer_core_workspace,
2415
- "success": upload_result.success,
2416
- "files_copied": upload_result.files_copied,
2417
- "duration_seconds": upload_result.duration_seconds,
2418
- "error_message": upload_result.error_message,
2419
- }
2420
- if upload_result.debug_info:
2421
- upload_event["debug_info"] = upload_result.debug_info
2422
- logger.info(json.dumps(upload_event))
2423
-
2424
- # Fail fast if upload failed
2425
- if not upload_result.success:
2426
- print(f"ERROR: Upload failed: {upload_result.error_message}")
2427
- if upload_result.debug_info:
2428
- print(
2429
- f"Debug info: {json.dumps(upload_result.debug_info, indent=2)}"
2430
- )
2431
- return EvaluateResult(
2432
- success=False,
2433
- all_correct=False,
2434
- correctness_score=0.0,
2435
- geomean_speedup=0.0,
2436
- passed_tests=0,
2437
- total_tests=0,
2438
- error_message=f"Failed to upload wafer-core: {upload_result.error_message}",
2439
- )
2440
-
2441
- print(f"Uploaded {upload_result.files_copied} files")
2442
- except Exception as e:
2353
+ # Install wafer-core in remote venv
2354
+ print("Installing wafer-core...")
2355
+ install_result = await client.exec(
2356
+ f'export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH" && '
2357
+ f"uv pip install --python {python_exe} wafer-core"
2358
+ )
2359
+ if install_result.exit_code != 0:
2443
2360
  return EvaluateResult(
2444
2361
  success=False,
2445
2362
  all_correct=False,
@@ -2447,7 +2364,7 @@ async def run_evaluate_digitalocean(
2447
2364
  geomean_speedup=0.0,
2448
2365
  passed_tests=0,
2449
2366
  total_tests=0,
2450
- error_message=f"Failed to upload wafer-core: {e}",
2367
+ error_message=f"Failed to install wafer-core: {install_result.stderr}",
2451
2368
  )
2452
2369
 
2453
2370
  # Select GPU (DigitalOcean droplets typically have GPU 0)
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wafer-cli
3
- Version: 0.2.28
3
+ Version: 0.2.30
4
4
  Summary: CLI for running GPU workloads, managing remote workspaces, and evaluating/optimizing kernels
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes