Run the same checks on Linux that the Windows scripts run
The product could only reach a GPU through one hard-coded route: `wsl.exe -d Ubuntu`, a device named "RTX 4090", and launch scripts that existed only as PowerShell. On this machine `WSL_DISTRO_NAME` is already set, so the local shell is Linux and the wsl.exe hop was unnecessary; on any other Linux host it is impossible. Every SGLang, vLLM and KTransformers profile was reported "missing" here even with a working 4090. profiles.py now asks the host what it is instead of assuming: `uses_wsl()` is false inside WSL and only true when wsl.exe is actually on PATH, `host_shell()` runs `bash -lc` directly or through `wsl.exe -d settings.wsl_distro`, `gpu_available()` replaced `wsl_gpu_available()`, and `launch_script()` picks the .sh twin on a Linux host and the .ps1 on a Windows one. The schema field `wsl_gpu` became `gpu_available` because the old name lied on Linux. Training no longer fails on a card name. `cuda_bf16_training()` gates on cuda_available plus bf16_supported, which the training environment already records, so an RTX 5090 or a 4080 passes and a non-BF16 4090 still fails. Fourteen .sh twins cover the six launch profiles plus install, verify, serve, download and finetune; the six WSL-hopping .ps1 files read ELY_EYE_WSL_DISTRO instead of Ubuntu. PowerShell parsing is not verified here because no pwsh exists on this host. ci.yml runs pytest, ruff, go build/vet/test/gofmt, the Vite build and a docker build-plus-curl on every push. To make that lint gate mean anything, the 17 pre-existing ruff findings across backend/src are cleared: 14 mechanical fixes plus three probe handlers that now name the failure families they absorb (OSError, SubprocessError, ValueError) instead of catching Exception. Verification: pytest 239 passed; ruff check backend/src bench clean (17 at HEAD); mypy on the 12 touched files 14 errors, identical at HEAD; pyright on the same files 8 errors, identical at HEAD; bash -n clean on all 15 .sh.
This commit is contained in:
1 parent
36f40c4c59
commit
922d01202f
34 files changed
+482
-88
No files matched your search
@@ -0,0 +1,67 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
- run: pip install -e ./backend
|
||||
- run: python -m pytest -q
|
||||
- run: pip install ruff==0.16.10 && ruff check backend/src bench
|
||||
|
||||
mcp:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.25"
|
||||
cache-dependency-path: ely-eye-mcp/go.sum
|
||||
- name: Build, vet and test
|
||||
working-directory: ely-eye-mcp
|
||||
run: go build ./... && go vet ./... && go test ./...
|
||||
- name: gofmt is clean
|
||||
working-directory: ely-eye-mcp
|
||||
run: test -z "$(gofmt -l .)"
|
||||
|
||||
interface:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
- run: npm ci && npm run build
|
||||
working-directory: frontend
|
||||
|
||||
image:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build the image from a clean clone
|
||||
run: docker build -t ely-eye:ci .
|
||||
- name: The container answers on both faces
|
||||
run: |
|
||||
docker run -d --name elyeye -p 8765:8765 ely-eye:ci
|
||||
url=http://127.0.0.1:8765/api/status
|
||||
for attempt in $(seq 1 30); do
|
||||
curl -fsS "$url" -o /tmp/status.json && break
|
||||
sleep 2
|
||||
done
|
||||
cat /tmp/status.json
|
||||
curl -fsS http://127.0.0.1:8765/ | grep -q '<div id="root">'
|
||||
docker logs elyeye
|
||||
@@ -277,8 +277,10 @@ def create_app() -> FastAPI:
|
||||
return benchmark_prediction_report(settings, settings.data_dir / "benchmark_predictions")
|
||||
|
||||
@app.get("/api/runtime/profiles", response_model=RuntimeProfileReport)
|
||||
def runtime_profiles() -> RuntimeProfileReport:
|
||||
return runtime_profile_report()
|
||||
def runtime_profiles(
|
||||
settings: Annotated[Settings, Depends(settings_dep)],
|
||||
) -> RuntimeProfileReport:
|
||||
return runtime_profile_report(settings)
|
||||
|
||||
@app.get("/api/runtime/adapters", response_model=AdapterLoadReport)
|
||||
def runtime_adapters(settings: Annotated[Settings, Depends(settings_dep)]) -> AdapterLoadReport:
|
||||
|
||||
@@ -325,7 +325,7 @@ def run_benchmark(
|
||||
|
||||
@app.command("runtime-profiles")
|
||||
def runtime_profiles() -> None:
|
||||
typer.echo(runtime_profile_report().model_dump_json(indent=2))
|
||||
typer.echo(runtime_profile_report(get_settings()).model_dump_json(indent=2))
|
||||
|
||||
|
||||
@app.command("research-modules")
|
||||
|
||||
@@ -27,6 +27,7 @@ class Settings(BaseSettings):
|
||||
sglang_base_url: str = "http://127.0.0.1:8000/v1"
|
||||
sglang_api_key: str = "ely-eye-local"
|
||||
ui_dir: Path | None = None
|
||||
wsl_distro: str = "Ubuntu"
|
||||
dense_embeddings_enabled: bool = False
|
||||
live_context_tokens: int = 262_144
|
||||
extreme_context_tokens: int = 1_010_000
|
||||
|
||||
@@ -2,7 +2,6 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -64,5 +64,5 @@ class EvidenceGraphService:
|
||||
|
||||
|
||||
def claim_node_id(question: str, answer: str) -> str:
|
||||
digest = hashlib.sha256(f"{question}\n{answer}".encode("utf-8")).hexdigest()[:20]
|
||||
digest = hashlib.sha256(f"{question}\n{answer}".encode()).hexdigest()[:20]
|
||||
return f"claim_{digest}"
|
||||
@@ -6,9 +6,9 @@ import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from .config import Settings
|
||||
from .schemas import RuntimeLaunchProfile, RuntimeProfileReport, RuntimeProfileValidation
|
||||
|
||||
|
||||
EXTREME_YARN_OVERRIDE = {
|
||||
"text_config": {
|
||||
"rope_parameters": {
|
||||
@@ -24,7 +24,7 @@ EXTREME_YARN_OVERRIDE = {
|
||||
}
|
||||
|
||||
|
||||
def runtime_profile_report(project_root: Path | None = None) -> RuntimeProfileReport:
|
||||
def runtime_profile_report(settings: Settings, project_root: Path | None = None) -> RuntimeProfileReport:
|
||||
root = project_root or Path.cwd()
|
||||
profiles = [
|
||||
RuntimeLaunchProfile(
|
||||
@@ -185,29 +185,55 @@ def runtime_profile_report(project_root: Path | None = None) -> RuntimeProfileRe
|
||||
required_features=["GGUF", "offline demo", "quantized KV"],
|
||||
),
|
||||
]
|
||||
validations = validate_runtime_profiles(root, profiles)
|
||||
validations = validate_runtime_profiles(root, profiles, settings)
|
||||
ready = all(validation.status == "ready" for validation in validations)
|
||||
return RuntimeProfileReport(
|
||||
status="ready" if ready else "incomplete",
|
||||
profiles=profiles,
|
||||
profiles=[profile.model_copy(update={"script": launch_script(profile.script)}) for profile in profiles],
|
||||
validations=validations,
|
||||
)
|
||||
|
||||
|
||||
def uses_wsl() -> bool:
|
||||
"""A Windows process reaches Linux through wsl.exe; inside WSL the local shell already is Linux."""
|
||||
if os.environ.get("WSL_DISTRO_NAME"):
|
||||
return False
|
||||
return shutil.which("wsl.exe") is not None
|
||||
|
||||
|
||||
def launch_script(script: str) -> str:
|
||||
"""A Windows host runs the .ps1; a Linux host, WSL included, runs the .sh twin."""
|
||||
return script if uses_wsl() else str(Path(script).with_suffix(".sh"))
|
||||
|
||||
|
||||
def host_shell(command: str, settings: Settings) -> list[str]:
|
||||
"""Run a shell command where the launch environments live."""
|
||||
if uses_wsl():
|
||||
return ["wsl.exe", "-d", settings.wsl_distro, "bash", "-lc", command]
|
||||
return ["bash", "-lc", command]
|
||||
|
||||
|
||||
def host_path(root: Path) -> str:
|
||||
"""The path the host shell can cd into; a Windows path is reached through the WSL mount."""
|
||||
resolved = root.resolve()
|
||||
return windows_path_to_wsl(resolved) if uses_wsl() else str(resolved)
|
||||
|
||||
|
||||
def validate_runtime_profiles(
|
||||
root: Path,
|
||||
profiles: list[RuntimeLaunchProfile],
|
||||
settings: Settings,
|
||||
) -> list[RuntimeProfileValidation]:
|
||||
wsl_gpu = wsl_gpu_available()
|
||||
gpu = gpu_available(settings)
|
||||
package_cache = {
|
||||
".venv-linux": linux_package_versions(root, ".venv-linux"),
|
||||
".venv-linux-vllm": linux_package_versions(root, ".venv-linux-vllm"),
|
||||
".venv-linux": linux_package_versions(root, ".venv-linux", settings),
|
||||
".venv-linux-vllm": linux_package_versions(root, ".venv-linux-vllm", settings),
|
||||
}
|
||||
probe_cache: dict[tuple[str, str, str | None], dict[str, object]] = {}
|
||||
llama_server = resolve_llama_server()
|
||||
validations: list[RuntimeProfileValidation] = []
|
||||
for profile in profiles:
|
||||
script_exists = (root / profile.script).exists()
|
||||
script_exists = (root / launch_script(profile.script)).exists()
|
||||
package = package_for_runtime(profile.runtime)
|
||||
env_name = linux_env_for_runtime(profile.runtime)
|
||||
linux_packages = package_cache.get(env_name, {})
|
||||
@@ -216,24 +242,24 @@ def validate_runtime_profiles(
|
||||
executable = llama_server if profile.runtime == "llama.cpp" else None
|
||||
probe_key = (profile.runtime, env_name, executable)
|
||||
if probe_key not in probe_cache:
|
||||
probe_cache[probe_key] = runtime_launcher_probe(root, profile.runtime, env_name, executable)
|
||||
probe_cache[probe_key] = runtime_launcher_probe(root, profile.runtime, env_name, executable, settings)
|
||||
probe = probe_cache[probe_key]
|
||||
probe_status = str(probe.get("status", "skipped"))
|
||||
probe_passed = probe_status == "passed"
|
||||
if profile.runtime in {"SGLang", "vLLM", "KTransformers"}:
|
||||
ready = script_exists and bool(package_version) and wsl_gpu and probe_passed
|
||||
ready = script_exists and bool(package_version) and gpu and probe_passed
|
||||
if profile.runtime == "KTransformers":
|
||||
ready = ready and bool(kt_sglang_version)
|
||||
detail = (
|
||||
(
|
||||
f"WSL GPU ready; {package} {package_version}; "
|
||||
f"GPU ready; {package} {package_version}; "
|
||||
f"sglang-kt entry {kt_sglang_version}"
|
||||
)
|
||||
if profile.runtime == "KTransformers" and ready
|
||||
else f"WSL GPU ready; {package} {package_version}; env {env_name}"
|
||||
else f"GPU ready; {package} {package_version}; env {env_name}"
|
||||
if ready
|
||||
else (
|
||||
f"script={script_exists}, wsl_gpu={wsl_gpu}, env={env_name}, "
|
||||
f"script={script_exists}, gpu={gpu}, env={env_name}, "
|
||||
f"{package}={package_version}, sglang={kt_sglang_version}"
|
||||
)
|
||||
)
|
||||
@@ -253,7 +279,7 @@ def validate_runtime_profiles(
|
||||
package=package,
|
||||
package_version=package_version,
|
||||
executable=executable,
|
||||
wsl_gpu=wsl_gpu,
|
||||
gpu_available=gpu,
|
||||
probe_command=[str(item) for item in probe.get("command", [])],
|
||||
probe_status=probe_status,
|
||||
probe_exit_code=probe.get("exit_code") if isinstance(probe.get("exit_code"), int) else None,
|
||||
@@ -278,7 +304,7 @@ def linux_env_for_runtime(runtime: str) -> str:
|
||||
return ".venv-linux"
|
||||
|
||||
|
||||
def linux_package_versions(root: Path, env_name: str) -> dict[str, str]:
|
||||
def linux_package_versions(root: Path, env_name: str, settings: Settings) -> dict[str, str]:
|
||||
script = (
|
||||
"import importlib.metadata as md, json;"
|
||||
"names=['sglang','vllm','ktransformers','torch','torchvision','qwen-vl-utils'];"
|
||||
@@ -290,25 +316,21 @@ def linux_package_versions(root: Path, env_name: str) -> dict[str, str]:
|
||||
)
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[
|
||||
"wsl.exe",
|
||||
"-d",
|
||||
"Ubuntu",
|
||||
"bash",
|
||||
"-lc",
|
||||
(
|
||||
f"cd {wsl_quote(windows_path_to_wsl(root))} "
|
||||
f"&& test -x {env_name}/bin/python "
|
||||
f"&& {env_name}/bin/python -c {wsl_quote(script)}"
|
||||
),
|
||||
],
|
||||
host_shell(
|
||||
f"cd {wsl_quote(host_path(root))} "
|
||||
f"&& test -x {env_name}/bin/python "
|
||||
f"&& {env_name}/bin/python -c {wsl_quote(script)}",
|
||||
settings,
|
||||
),
|
||||
text=True,
|
||||
stderr=subprocess.DEVNULL,
|
||||
timeout=20,
|
||||
)
|
||||
data = json.loads(output.strip())
|
||||
return {str(key): str(value) for key, value in data.items()}
|
||||
except Exception:
|
||||
payload = json.loads(output.strip())
|
||||
if not isinstance(payload, dict):
|
||||
return {}
|
||||
return {str(key): str(value) for key, value in payload.items()}
|
||||
except (OSError, subprocess.SubprocessError, ValueError):
|
||||
return {}
|
||||
|
||||
|
||||
@@ -317,6 +339,7 @@ def runtime_launcher_probe(
|
||||
runtime: str,
|
||||
env_name: str,
|
||||
executable: str | None,
|
||||
settings: Settings,
|
||||
) -> dict[str, object]:
|
||||
if runtime == "SGLang":
|
||||
probe_script = (
|
||||
@@ -325,7 +348,7 @@ def runtime_launcher_probe(
|
||||
"print(spec.origin if spec else 'missing'); "
|
||||
"sys.exit(0 if spec else 1)"
|
||||
)
|
||||
command = wsl_python_command(root, env_name, f"-c {wsl_quote(probe_script)}")
|
||||
command = host_python_command(root, env_name, f"-c {wsl_quote(probe_script)}", settings)
|
||||
elif runtime == "vLLM":
|
||||
probe_script = (
|
||||
"import importlib.util, sys; "
|
||||
@@ -333,10 +356,10 @@ def runtime_launcher_probe(
|
||||
"print(spec.origin if spec else 'missing'); "
|
||||
"sys.exit(0 if spec else 1)"
|
||||
)
|
||||
command = wsl_python_command(root, env_name, f"-c {wsl_quote(probe_script)}")
|
||||
command = host_python_command(root, env_name, f"-c {wsl_quote(probe_script)}", settings)
|
||||
elif runtime == "KTransformers":
|
||||
probe_script = "import ktransformers, sglang; print('ktransformers and sglang import ok')"
|
||||
command = wsl_python_command(root, env_name, f"-c {wsl_quote(probe_script)}")
|
||||
command = host_python_command(root, env_name, f"-c {wsl_quote(probe_script)}", settings)
|
||||
elif runtime == "llama.cpp" and executable:
|
||||
command = [executable, "--help"]
|
||||
else:
|
||||
@@ -349,7 +372,7 @@ def runtime_launcher_probe(
|
||||
timeout=30,
|
||||
check=False,
|
||||
)
|
||||
except Exception as exc:
|
||||
except (OSError, subprocess.SubprocessError) as exc:
|
||||
return {
|
||||
"status": "failed",
|
||||
"command": command,
|
||||
@@ -365,13 +388,13 @@ def runtime_launcher_probe(
|
||||
}
|
||||
|
||||
|
||||
def wsl_python_command(root: Path, env_name: str, python_args: str) -> list[str]:
|
||||
def host_python_command(root: Path, env_name: str, python_args: str, settings: Settings) -> list[str]:
|
||||
shell_command = (
|
||||
f"cd {wsl_quote(windows_path_to_wsl(root))} "
|
||||
f"cd {wsl_quote(host_path(root))} "
|
||||
f"&& test -x {env_name}/bin/python "
|
||||
f"&& {env_name}/bin/python {python_args}"
|
||||
)
|
||||
return ["wsl.exe", "-d", "Ubuntu", "bash", "-lc", shell_command]
|
||||
return host_shell(shell_command, settings)
|
||||
|
||||
|
||||
def compact_output(stdout: str, stderr: str, limit: int = 800) -> str:
|
||||
@@ -379,18 +402,17 @@ def compact_output(stdout: str, stderr: str, limit: int = 800) -> str:
|
||||
return text[:limit]
|
||||
|
||||
|
||||
def wsl_gpu_available() -> bool:
|
||||
if shutil.which("wsl.exe") is None:
|
||||
return False
|
||||
def gpu_available(settings: Settings) -> bool:
|
||||
"""Ask the host for a CUDA-capable GPU without naming a card: any GPU the driver lists will do."""
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
["wsl.exe", "-d", "Ubuntu", "nvidia-smi", "-L"],
|
||||
host_shell("nvidia-smi -L", settings),
|
||||
text=True,
|
||||
stderr=subprocess.DEVNULL,
|
||||
timeout=10,
|
||||
)
|
||||
return "GPU" in output
|
||||
except Exception:
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ class RuntimeChecks:
|
||||
runtime.unload()
|
||||
|
||||
def _runtime_profile_check(self, proof_root: Path) -> ProofCheck:
|
||||
report = runtime_profile_report()
|
||||
report = runtime_profile_report(self.settings)
|
||||
artifact = proof_root / "runtime_profiles.json"
|
||||
artifact.write_text(report.model_dump_json(indent=2), encoding="utf-8")
|
||||
required_runtimes = {"SGLang", "vLLM", "KTransformers", "llama.cpp"}
|
||||
|
||||
@@ -24,6 +24,11 @@ from ..training import (
|
||||
)
|
||||
|
||||
|
||||
def cuda_bf16_training(environment: dict[str, Any]) -> bool:
|
||||
"""The requirement is CUDA plus BF16 support, not one card name written down in 2025."""
|
||||
return bool(environment.get("cuda_available")) and bool(environment.get("bf16_supported"))
|
||||
|
||||
|
||||
def count_safetensor_keys(path: Path) -> int:
|
||||
return int(safetensor_weight_stats(path)["tensor_count"])
|
||||
|
||||
@@ -173,8 +178,7 @@ def validate_training_trace(manifest: AdapterManifest, adapter_root: Path) -> di
|
||||
}
|
||||
)
|
||||
environment = summary.get("environment") if isinstance(summary.get("environment"), dict) else {}
|
||||
device_name = str(environment.get("device_name") or "")
|
||||
report["cuda_training"] = bool(environment.get("cuda_available")) and "RTX 4090" in device_name
|
||||
report["cuda_training"] = cuda_bf16_training(environment)
|
||||
report["bf16_supported"] = bool(environment.get("bf16_supported"))
|
||||
report["valid"] = (
|
||||
report["trace_sha256_ok"]
|
||||
@@ -282,11 +286,7 @@ def validate_training_proof(manifest: AdapterManifest, adapter_root: Path) -> di
|
||||
and int(proof_weight_stats.get("nonzero_elements") or 0) > 0
|
||||
and bool(proof_weight_stats.get("all_finite"))
|
||||
),
|
||||
"proof_cuda_bf16": (
|
||||
bool(proof.get("bf16"))
|
||||
and bool(proof_environment.get("cuda_available"))
|
||||
and "RTX 4090" in str(proof_environment.get("device_name") or "")
|
||||
),
|
||||
"proof_cuda_bf16": bool(proof.get("bf16")) and cuda_bf16_training(proof_environment),
|
||||
"proof_bf16_supported": bool(proof_environment.get("bf16_supported")),
|
||||
"proof_contract_ok": training_payload_contract_ok(proof, manifest),
|
||||
"proof_optimizer_ok": proof.get("optimizer") == "paged_adamw_8bit",
|
||||
|
||||
@@ -155,7 +155,7 @@ class ResearchModuleService:
|
||||
"entries": layer.entries,
|
||||
"hit_rate": hit_rate,
|
||||
"evict": hit_rate < 0.25 and layer.entries > 0,
|
||||
"replay_token": hashlib.sha256(f"{layer.layer}:{layer.entries}".encode("utf-8")).hexdigest()[:16],
|
||||
"replay_token": hashlib.sha256(f"{layer.layer}:{layer.entries}".encode()).hexdigest()[:16],
|
||||
}
|
||||
)
|
||||
return {"policy": "hit-rate weighted replay buffer", "layers": rows}
|
||||
|
||||
@@ -439,7 +439,7 @@ class RuntimeProfileValidation(BaseModel):
|
||||
package: str | None = None
|
||||
package_version: str | None = None
|
||||
executable: str | None = None
|
||||
wsl_gpu: bool = False
|
||||
gpu_available: bool = False
|
||||
probe_command: list[str] = Field(default_factory=list)
|
||||
probe_status: Literal["passed", "failed", "skipped"] = "skipped"
|
||||
probe_exit_code: int | None = None
|
||||
|
||||
@@ -9,7 +9,7 @@ import shutil
|
||||
import subprocess
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
from pathlib import Path
|
||||
from typing import Any, TextIO
|
||||
@@ -20,7 +20,6 @@ from .config import Settings, get_settings
|
||||
from .db import Database
|
||||
from .schemas import AdapterManifest, EvidenceAtom, TrainingAdapterKind
|
||||
|
||||
|
||||
SYSTEM_PROMPT = (
|
||||
"You are Ely-Eye. Answer from supplied evidence, return compact JSON, "
|
||||
"and cite atom ids exactly."
|
||||
@@ -433,7 +432,7 @@ class TrainingService:
|
||||
gradient_accumulation_steps or self.settings.train_gradient_accumulation_steps
|
||||
)
|
||||
learning_rate = learning_rate or self.settings.train_learning_rate
|
||||
started_at = datetime.now(timezone.utc)
|
||||
started_at = datetime.now(UTC)
|
||||
wall_start = time.perf_counter()
|
||||
trace_path = output_dir / "training_trace.jsonl"
|
||||
|
||||
@@ -575,7 +574,7 @@ class TrainingService:
|
||||
processor.save_pretrained(output_dir)
|
||||
self.write_lora_config(output_dir, kind, rank, alpha, model_id)
|
||||
weight_stats = safetensor_weight_stats(output_dir / "adapter_model.safetensors")
|
||||
finished_at = datetime.now(timezone.utc)
|
||||
finished_at = datetime.now(UTC)
|
||||
wall_seconds = time.perf_counter() - wall_start
|
||||
summary_path = output_dir / "training_summary.json"
|
||||
proof_path = output_dir / "training_proof.json"
|
||||
@@ -671,7 +670,7 @@ class TrainingService:
|
||||
manifest = AdapterManifest(
|
||||
adapter_id=adapter_id(kind, dataset_path, model_id),
|
||||
kind=kind,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
created_at=datetime.now(UTC),
|
||||
proof_version=TRAINING_PROOF_VERSION,
|
||||
training_method=TRAINING_METHOD,
|
||||
precision=TRAINING_PRECISION,
|
||||
@@ -756,7 +755,7 @@ class TrainingService:
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
max_length = max_length or self.settings.train_max_length
|
||||
learning_rate = learning_rate or self.settings.train_learning_rate
|
||||
started_at = datetime.now(timezone.utc)
|
||||
started_at = datetime.now(UTC)
|
||||
wall_start = time.perf_counter()
|
||||
trace_path = output_dir / "training_trace.jsonl"
|
||||
|
||||
@@ -882,7 +881,7 @@ class TrainingService:
|
||||
processor.save_pretrained(output_dir)
|
||||
self.write_lora_config(output_dir, TrainingAdapterKind.hme_retrieval, rank, alpha, model_id)
|
||||
weight_stats = safetensor_weight_stats(output_dir / "adapter_model.safetensors")
|
||||
finished_at = datetime.now(timezone.utc)
|
||||
finished_at = datetime.now(UTC)
|
||||
wall_seconds = time.perf_counter() - wall_start
|
||||
summary_path = output_dir / "training_summary.json"
|
||||
proof_path = output_dir / "training_proof.json"
|
||||
@@ -977,7 +976,7 @@ class TrainingService:
|
||||
manifest = AdapterManifest(
|
||||
adapter_id=adapter_id(TrainingAdapterKind.hme_retrieval, dataset_path, model_id),
|
||||
kind=TrainingAdapterKind.hme_retrieval,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
created_at=datetime.now(UTC),
|
||||
proof_version=TRAINING_PROOF_VERSION,
|
||||
training_method=TRAINING_METHOD,
|
||||
precision=TRAINING_PRECISION,
|
||||
@@ -1269,7 +1268,7 @@ def trainable_update_l2(before_update: dict[str, Any], named_parameters: list[tu
|
||||
|
||||
def require_cuda_bf16(torch_module: Any) -> None:
|
||||
if not torch_module.cuda.is_available():
|
||||
raise RuntimeError("PRD 12.2 training requires CUDA BF16 on the local RTX 4090.")
|
||||
raise RuntimeError("training needs a CUDA device; none is available.")
|
||||
bf16_supported = getattr(torch_module.cuda, "is_bf16_supported", lambda: False)
|
||||
if not bool(bf16_supported()):
|
||||
raise RuntimeError("PRD 12.2 training requires BF16-capable CUDA hardware.")
|
||||
@@ -1598,7 +1597,7 @@ def slugify(value: str) -> str:
|
||||
|
||||
|
||||
def adapter_id(kind: TrainingAdapterKind, dataset_path: Path, model_id: str) -> str:
|
||||
raw = f"{kind.value}:{model_id}:{dataset_path.resolve()}:{datetime.now(timezone.utc).isoformat()}"
|
||||
raw = f"{kind.value}:{model_id}:{dataset_path.resolve()}:{datetime.now(UTC).isoformat()}"
|
||||
return f"{kind.value}_{sha256_bytes(raw.encode('utf-8'))[:12]}"
|
||||
|
||||
|
||||
@@ -1632,7 +1631,7 @@ def sha256_bytes(data: bytes) -> str:
|
||||
|
||||
|
||||
class _DigestWriter:
|
||||
def __init__(self, digest: "hashlib._Hash") -> None:
|
||||
def __init__(self, digest: hashlib._Hash) -> None:
|
||||
self.digest = digest
|
||||
|
||||
def write(self, data: bytes) -> int:
|
||||
|
||||
@@ -7,7 +7,6 @@ from typing import Any
|
||||
|
||||
from .schemas import CitationReport, CompiledContext
|
||||
|
||||
|
||||
ATOM_PATTERN = re.compile(r"[A-Za-z0-9_./\\:-]+:(?:text|image|page|frame|region)[A-Za-z0-9_./\\:#=-]*")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
"""The launch layer must describe the machine it runs on, not the one it was written on."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from ely_eye import profiles
|
||||
from ely_eye.config import Settings
|
||||
from ely_eye.proofs.training_trace import cuda_bf16_training
|
||||
from ely_eye.training import require_cuda_bf16
|
||||
|
||||
|
||||
def _stub_nvidia_smi(tmp_path: Path, monkeypatch, output: str, exit_code: int = 0) -> None:
|
||||
bin_dir = tmp_path / "bin"
|
||||
bin_dir.mkdir()
|
||||
stub = bin_dir / "nvidia-smi"
|
||||
stub.write_text(f"#!/bin/sh\nprintf '%s\\n' '{output}'\nexit {exit_code}\n", encoding="utf-8")
|
||||
stub.chmod(stub.stat().st_mode | stat.S_IEXEC)
|
||||
monkeypatch.setenv("PATH", f"{bin_dir}{os.pathsep}{os.environ['PATH']}")
|
||||
|
||||
|
||||
def test_a_gpu_that_is_not_the_written_down_card_still_counts(tmp_path: Path, monkeypatch) -> None:
|
||||
_stub_nvidia_smi(tmp_path, monkeypatch, "GPU 0: NVIDIA GeForce RTX 5090 (UUID: GPU-1234)")
|
||||
monkeypatch.setattr(profiles, "uses_wsl", lambda: False)
|
||||
assert profiles.gpu_available(Settings(home=tmp_path / "home")) is True
|
||||
|
||||
|
||||
def test_a_machine_the_driver_cannot_see_reports_no_gpu(tmp_path: Path, monkeypatch) -> None:
|
||||
_stub_nvidia_smi(tmp_path, monkeypatch, "nvidia-smi: command not found", exit_code=127)
|
||||
monkeypatch.setattr(profiles, "uses_wsl", lambda: False)
|
||||
assert profiles.gpu_available(Settings(home=tmp_path / "home")) is False
|
||||
|
||||
|
||||
def test_the_distro_name_comes_from_settings(monkeypatch) -> None:
|
||||
monkeypatch.setattr(profiles, "uses_wsl", lambda: True)
|
||||
command = profiles.host_shell("true", Settings(home=Path("/tmp"), wsl_distro="Ubuntu-24.04"))
|
||||
assert command == ["wsl.exe", "-d", "Ubuntu-24.04", "bash", "-lc", "true"]
|
||||
|
||||
|
||||
def test_inside_wsl_the_local_shell_runs(monkeypatch, tmp_path: Path) -> None:
|
||||
monkeypatch.setenv("WSL_DISTRO_NAME", "Ubuntu")
|
||||
monkeypatch.setattr(profiles.shutil, "which", lambda name: "/mnt/c/WINDOWS/system32/wsl.exe")
|
||||
assert profiles.uses_wsl() is False
|
||||
monkeypatch.delenv("WSL_DISTRO_NAME")
|
||||
assert profiles.uses_wsl() is True
|
||||
assert profiles.host_shell("true", Settings(home=tmp_path / "home", wsl_distro="Ubuntu"))[0] == "wsl.exe"
|
||||
|
||||
|
||||
def test_a_linux_host_is_pointed_at_the_shell_twin(monkeypatch) -> None:
|
||||
monkeypatch.setattr(profiles, "uses_wsl", lambda: False)
|
||||
assert profiles.launch_script("scripts/launch-sglang-live.ps1") == "scripts/launch-sglang-live.sh"
|
||||
monkeypatch.setattr(profiles, "uses_wsl", lambda: True)
|
||||
assert profiles.launch_script("scripts/launch-sglang-live.ps1") == "scripts/launch-sglang-live.ps1"
|
||||
|
||||
|
||||
def test_the_training_verdict_does_not_name_a_card() -> None:
|
||||
newer = {"cuda_available": True, "bf16_supported": True, "device_name": "NVIDIA GeForce RTX 5090"}
|
||||
assert cuda_bf16_training(newer) is True
|
||||
older = {"cuda_available": True, "bf16_supported": True, "device_name": "NVIDIA GeForce RTX 4080"}
|
||||
assert cuda_bf16_training(older) is True
|
||||
without_bf16 = {"cuda_available": True, "bf16_supported": False, "device_name": "NVIDIA GeForce RTX 4090"}
|
||||
assert cuda_bf16_training(without_bf16) is False
|
||||
|
||||
|
||||
class _FakeCuda:
|
||||
def __init__(self, available: bool, bf16: bool) -> None:
|
||||
self._available = available
|
||||
self._bf16 = bf16
|
||||
|
||||
def is_available(self) -> bool:
|
||||
return self._available
|
||||
|
||||
def is_bf16_supported(self) -> bool:
|
||||
return self._bf16
|
||||
|
||||
|
||||
class _FakeTorch:
|
||||
def __init__(self, available: bool, bf16: bool) -> None:
|
||||
self.cuda = _FakeCuda(available, bf16)
|
||||
|
||||
|
||||
def test_training_refuses_a_machine_without_cuda_by_capability_not_card() -> None:
|
||||
with pytest.raises(RuntimeError) as refused:
|
||||
require_cuda_bf16(_FakeTorch(available=False, bf16=False))
|
||||
assert "4090" not in str(refused.value)
|
||||
require_cuda_bf16(_FakeTorch(available=True, bf16=True))
|
||||
|
||||
|
||||
def test_every_profile_ships_a_script_for_this_host(tmp_path: Path, monkeypatch) -> None:
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
monkeypatch.setattr(profiles, "uses_wsl", lambda: False)
|
||||
linux = profiles.runtime_profile_report(Settings(home=tmp_path / "home"), root)
|
||||
assert [item.name for item in linux.validations if not item.script_exists] == []
|
||||
assert all(item.script.endswith(".sh") for item in linux.profiles), linux.profiles
|
||||
monkeypatch.setattr(profiles, "uses_wsl", lambda: True)
|
||||
windows = profiles.runtime_profile_report(Settings(home=tmp_path / "home"), root)
|
||||
assert [item.name for item in windows.validations if not item.script_exists] == []
|
||||
assert all(item.script.endswith(".ps1") for item in windows.profiles), windows.profiles
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
for model in Qwen/Qwen3.5-9B Qwen/Qwen3.5-0.8B Qwen/Qwen3-VL-Embedding-8B; do
|
||||
.venv/bin/python ./scripts/download_models.py --model "$model"
|
||||
done
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
.venv/bin/python -m pip install --upgrade --no-cache-dir --index-url https://download.pytorch.org/whl/cu130 torch==2.12.0+cu130 torchvision==0.27.0+cu130
|
||||
.venv/bin/python -c 'import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.version.cuda)'
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ ! -d .venv ]; then
|
||||
python3 -m venv .venv
|
||||
fi
|
||||
.venv/bin/python -m pip install --upgrade pip setuptools wheel
|
||||
.venv/bin/python -m pip install -e ./backend
|
||||
.venv/bin/python -m pip install --upgrade --no-cache-dir unsloth unsloth_zoo
|
||||
.venv/bin/python -m pip install --upgrade --no-cache-dir --index-url https://download.pytorch.org/whl/cu130 torch==2.12.0+cu130 torchvision==0.27.0+cu130
|
||||
.venv/bin/python -m pip install --upgrade --no-cache-dir transformers==5.9.0 datasets==4.8.5 dill==0.4.1 multiprocess==0.70.19 fsspec==2026.2.0 safetensors==0.8.0rc0
|
||||
(cd frontend && npm install)
|
||||
[ -f .env ] || cp .env.example .env
|
||||
.venv/bin/ely-eye status
|
||||
@@ -1,12 +1,13 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Set-Location (Split-Path -Parent $PSScriptRoot)
|
||||
$distro = if ($env:ELY_EYE_WSL_DISTRO) { $env:ELY_EYE_WSL_DISTRO } else { "Ubuntu" }
|
||||
|
||||
if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) {
|
||||
throw "WSL is required for the KTransformers Linux runtime."
|
||||
}
|
||||
|
||||
$wslRoot = (wsl.exe -d Ubuntu wslpath -a (Get-Location).Path).Trim()
|
||||
$wslRoot = (wsl.exe -d $distro wslpath -a (Get-Location).Path).Trim()
|
||||
$command = @"
|
||||
set -euo pipefail
|
||||
cd "$wslRoot"
|
||||
@@ -20,4 +21,4 @@ python -m sglang.launch_server \
|
||||
--cpu-offload-gb 48
|
||||
"@
|
||||
|
||||
wsl.exe -d Ubuntu -- bash -lc $command
|
||||
wsl.exe -d $distro -- bash -lc $command
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ ! -f .venv-linux/bin/activate ]; then
|
||||
echo "Run scripts/setup-linux-runtimes.sh before launching SGLang." >&2
|
||||
exit 1
|
||||
fi
|
||||
source .venv-linux/bin/activate
|
||||
export SGLANG_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1
|
||||
exec python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3.5-9B \
|
||||
--port 8002 \
|
||||
--context-length 1010000 \
|
||||
--kt-cpuinfer 16 \
|
||||
--cpu-offload-gb 48
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ -z "${ELY_EYE_GGUF_MODEL:-}" ]; then
|
||||
echo "Set ELY_EYE_GGUF_MODEL to the absolute GGUF model path." >&2
|
||||
exit 1
|
||||
fi
|
||||
server="${LLAMA_CPP_SERVER:-llama-server}"
|
||||
if ! command -v "$server" >/dev/null 2>&1; then
|
||||
echo "llama-server executable is missing. Build llama.cpp or set LLAMA_CPP_SERVER." >&2
|
||||
exit 1
|
||||
fi
|
||||
exec "$server" \
|
||||
--model "$ELY_EYE_GGUF_MODEL" \
|
||||
--host 127.0.0.1 \
|
||||
--port 8003 \
|
||||
--ctx-size 262144 \
|
||||
--flash-attn \
|
||||
--cache-type-k q4_0 \
|
||||
--cache-type-v q4_0
|
||||
@@ -1,13 +1,14 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Set-Location (Split-Path -Parent $PSScriptRoot)
|
||||
$distro = if ($env:ELY_EYE_WSL_DISTRO) { $env:ELY_EYE_WSL_DISTRO } else { "Ubuntu" }
|
||||
|
||||
if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) {
|
||||
throw "WSL is required for the SGLang Linux runtime."
|
||||
}
|
||||
|
||||
$override = '{"text_config":{"rope_parameters":{"mrope_interleaved":true,"mrope_section":[11,11,10],"rope_type":"yarn","rope_theta":10000000,"partial_rotary_factor":0.25,"factor":4.0,"original_max_position_embeddings":262144}}}'
|
||||
$wslRoot = (wsl.exe -d Ubuntu wslpath -a (Get-Location).Path).Trim()
|
||||
$wslRoot = (wsl.exe -d $distro wslpath -a (Get-Location).Path).Trim()
|
||||
$command = @"
|
||||
set -euo pipefail
|
||||
cd "$wslRoot"
|
||||
@@ -22,4 +23,4 @@ python -m sglang.launch_server \
|
||||
--json-model-override-args '$override'
|
||||
"@
|
||||
|
||||
wsl.exe -d Ubuntu -- bash -lc $command
|
||||
wsl.exe -d $distro -- bash -lc $command
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ ! -f .venv-linux/bin/activate ]; then
|
||||
echo "Run scripts/setup-linux-runtimes.sh before launching SGLang." >&2
|
||||
exit 1
|
||||
fi
|
||||
source .venv-linux/bin/activate
|
||||
export SGLANG_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1
|
||||
exec python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3.5-9B \
|
||||
--port 8000 \
|
||||
--tp-size 1 \
|
||||
--mem-fraction-static 0.88 \
|
||||
--context-length 1010000 \
|
||||
--json-model-override-args '{"text_config":{"rope_parameters":{"mrope_interleaved":true,"mrope_section":[11,11,10],"rope_type":"yarn","rope_theta":10000000,"partial_rotary_factor":0.25,"factor":4.0,"original_max_position_embeddings":262144}}}'
|
||||
@@ -1,12 +1,13 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Set-Location (Split-Path -Parent $PSScriptRoot)
|
||||
$distro = if ($env:ELY_EYE_WSL_DISTRO) { $env:ELY_EYE_WSL_DISTRO } else { "Ubuntu" }
|
||||
|
||||
if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) {
|
||||
throw "WSL is required for the SGLang Linux runtime."
|
||||
}
|
||||
|
||||
$wslRoot = (wsl.exe -d Ubuntu wslpath -a (Get-Location).Path).Trim()
|
||||
$wslRoot = (wsl.exe -d $distro wslpath -a (Get-Location).Path).Trim()
|
||||
$command = @"
|
||||
set -euo pipefail
|
||||
cd "$wslRoot"
|
||||
@@ -23,4 +24,4 @@ python -m sglang.launch_server \
|
||||
--speculative-num-draft-tokens 4
|
||||
"@
|
||||
|
||||
wsl.exe -d Ubuntu -- bash -lc $command
|
||||
wsl.exe -d $distro -- bash -lc $command
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ ! -f .venv-linux/bin/activate ]; then
|
||||
echo "Run scripts/setup-linux-runtimes.sh before launching SGLang." >&2
|
||||
exit 1
|
||||
fi
|
||||
source .venv-linux/bin/activate
|
||||
exec python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3.5-9B \
|
||||
--port 8000 \
|
||||
--tp-size 1 \
|
||||
--mem-fraction-static 0.82 \
|
||||
--context-length 262144 \
|
||||
--reasoning-parser qwen3 \
|
||||
--speculative-algo NEXTN \
|
||||
--speculative-num-steps 3 \
|
||||
--speculative-num-draft-tokens 4
|
||||
@@ -1,12 +1,13 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Set-Location (Split-Path -Parent $PSScriptRoot)
|
||||
$distro = if ($env:ELY_EYE_WSL_DISTRO) { $env:ELY_EYE_WSL_DISTRO } else { "Ubuntu" }
|
||||
|
||||
if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) {
|
||||
throw "WSL is required for the vLLM Linux runtime."
|
||||
}
|
||||
|
||||
$wslRoot = (wsl.exe -d Ubuntu wslpath -a (Get-Location).Path).Trim()
|
||||
$wslRoot = (wsl.exe -d $distro wslpath -a (Get-Location).Path).Trim()
|
||||
$ropeScaling = '{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":262144}'
|
||||
$command = @"
|
||||
set -euo pipefail
|
||||
@@ -23,4 +24,4 @@ python -m vllm.entrypoints.openai.api_server \
|
||||
--rope-scaling '$ropeScaling'
|
||||
"@
|
||||
|
||||
wsl.exe -d Ubuntu -- bash -lc $command
|
||||
wsl.exe -d $distro -- bash -lc $command
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ ! -f .venv-linux-vllm/bin/activate ]; then
|
||||
echo "Run scripts/setup-linux-runtimes.sh before launching vLLM." >&2
|
||||
exit 1
|
||||
fi
|
||||
source .venv-linux-vllm/bin/activate
|
||||
exec python -m vllm.entrypoints.openai.api_server \
|
||||
--model Qwen/Qwen3.5-9B \
|
||||
--host 127.0.0.1 \
|
||||
--port 8001 \
|
||||
--max-model-len 1010000 \
|
||||
--enable-prefix-caching \
|
||||
--gpu-memory-utilization 0.88 \
|
||||
--trust-remote-code \
|
||||
--rope-scaling '{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":262144}'
|
||||
@@ -1,12 +1,13 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Set-Location (Split-Path -Parent $PSScriptRoot)
|
||||
$distro = if ($env:ELY_EYE_WSL_DISTRO) { $env:ELY_EYE_WSL_DISTRO } else { "Ubuntu" }
|
||||
|
||||
if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) {
|
||||
throw "WSL is required for the vLLM Linux runtime."
|
||||
}
|
||||
|
||||
$wslRoot = (wsl.exe -d Ubuntu wslpath -a (Get-Location).Path).Trim()
|
||||
$wslRoot = (wsl.exe -d $distro wslpath -a (Get-Location).Path).Trim()
|
||||
$command = @"
|
||||
set -euo pipefail
|
||||
cd "$wslRoot"
|
||||
@@ -21,4 +22,4 @@ python -m vllm.entrypoints.openai.api_server \
|
||||
--trust-remote-code
|
||||
"@
|
||||
|
||||
wsl.exe -d Ubuntu -- bash -lc $command
|
||||
wsl.exe -d $distro -- bash -lc $command
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ ! -f .venv-linux-vllm/bin/activate ]; then
|
||||
echo "Run scripts/setup-linux-runtimes.sh before launching vLLM." >&2
|
||||
exit 1
|
||||
fi
|
||||
source .venv-linux-vllm/bin/activate
|
||||
exec python -m vllm.entrypoints.openai.api_server \
|
||||
--model Qwen/Qwen3.5-9B \
|
||||
--host 127.0.0.1 \
|
||||
--port 8001 \
|
||||
--max-model-len 262144 \
|
||||
--enable-prefix-caching \
|
||||
--gpu-memory-utilization 0.82 \
|
||||
--trust-remote-code
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
max_steps="${MAX_STEPS:-37}"
|
||||
rank="${RANK:-16}"
|
||||
alpha="${ALPHA:-32}"
|
||||
max_length="${MAX_LENGTH:-8192}"
|
||||
gradient_accumulation_steps="${GRADIENT_ACCUMULATION_STEPS:-4}"
|
||||
cartridge_id="${CARTRIDGE_ID:-}"
|
||||
|
||||
dataset=.ely_eye/training/hme_prd_sft.jsonl
|
||||
adapter=.ely_eye/adapters/hme_core_lora_qwen35_9b
|
||||
|
||||
.venv/bin/ely-eye build-prd-training-data \
|
||||
--prd-path ./PRD.md \
|
||||
--output-path "$dataset" \
|
||||
--max-sections 48
|
||||
|
||||
args=(train-lora "$dataset" "$adapter"
|
||||
--max-steps "$max_steps"
|
||||
--kind hme_core_lora
|
||||
--rank "$rank"
|
||||
--alpha "$alpha"
|
||||
--max-length "$max_length"
|
||||
--gradient-accumulation-steps "$gradient_accumulation_steps")
|
||||
|
||||
if [ -n "$cartridge_id" ]; then
|
||||
args+=(--cartridge-id "$cartridge_id")
|
||||
fi
|
||||
|
||||
.venv/bin/ely-eye "${args[@]}"
|
||||
|
||||
echo "Adapter ready: $adapter"
|
||||
echo "Use with: ELY_EYE_ADAPTER_DIR=$adapter"
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
exec .venv/bin/ely-eye serve --host 127.0.0.1 --port 8765
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
cd frontend
|
||||
exec npm run dev
|
||||
@@ -1,20 +1,21 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Set-Location (Split-Path -Parent $PSScriptRoot)
|
||||
$distro = if ($env:ELY_EYE_WSL_DISTRO) { $env:ELY_EYE_WSL_DISTRO } else { "Ubuntu" }
|
||||
|
||||
if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) {
|
||||
throw "WSL is required for SGLang, vLLM, and KTransformers runtime profiles."
|
||||
}
|
||||
|
||||
wsl.exe -d Ubuntu -- bash -lc "command -v nvidia-smi >/dev/null && nvidia-smi -L >/dev/null"
|
||||
wsl.exe -d Ubuntu -- bash -lc "command -v curl >/dev/null || (apt-get update && apt-get install -y curl)"
|
||||
wsl.exe -d Ubuntu -- bash -lc "command -v /root/.local/bin/uv >/dev/null || curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||
$wslRoot = (wsl.exe -d Ubuntu wslpath -a (Get-Location).Path).Trim()
|
||||
wsl.exe -d Ubuntu -- bash -lc "cd '$wslRoot' && /root/.local/bin/uv venv .venv-linux --python 3.12"
|
||||
wsl.exe -d Ubuntu -- bash -lc "cd '$wslRoot' && /root/.local/bin/uv venv .venv-linux-vllm --python 3.12"
|
||||
wsl.exe -d Ubuntu -- bash -lc "cd '$wslRoot' && source .venv-linux/bin/activate && /root/.local/bin/uv pip install --prerelease=allow -r backend/requirements-sglang-linux.txt"
|
||||
wsl.exe -d Ubuntu -- bash -lc "cd '$wslRoot' && source .venv-linux-vllm/bin/activate && /root/.local/bin/uv pip install --prerelease=allow -r backend/requirements-vllm-linux.txt"
|
||||
wsl.exe -d Ubuntu -- bash -lc "cd '$wslRoot' && source .venv-linux/bin/activate && /root/.local/bin/uv pip install --prerelease=allow -r backend/requirements-ktransformers-linux.txt"
|
||||
wsl.exe -d $distro -- bash -lc "command -v nvidia-smi >/dev/null && nvidia-smi -L >/dev/null"
|
||||
wsl.exe -d $distro -- bash -lc "command -v curl >/dev/null || (apt-get update && apt-get install -y curl)"
|
||||
wsl.exe -d $distro -- bash -lc "command -v /root/.local/bin/uv >/dev/null || curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||
$wslRoot = (wsl.exe -d $distro wslpath -a (Get-Location).Path).Trim()
|
||||
wsl.exe -d $distro -- bash -lc "cd '$wslRoot' && /root/.local/bin/uv venv .venv-linux --python 3.12"
|
||||
wsl.exe -d $distro -- bash -lc "cd '$wslRoot' && /root/.local/bin/uv venv .venv-linux-vllm --python 3.12"
|
||||
wsl.exe -d $distro -- bash -lc "cd '$wslRoot' && source .venv-linux/bin/activate && /root/.local/bin/uv pip install --prerelease=allow -r backend/requirements-sglang-linux.txt"
|
||||
wsl.exe -d $distro -- bash -lc "cd '$wslRoot' && source .venv-linux-vllm/bin/activate && /root/.local/bin/uv pip install --prerelease=allow -r backend/requirements-vllm-linux.txt"
|
||||
wsl.exe -d $distro -- bash -lc "cd '$wslRoot' && source .venv-linux/bin/activate && /root/.local/bin/uv pip install --prerelease=allow -r backend/requirements-ktransformers-linux.txt"
|
||||
|
||||
if (-not (Get-Command llama-server -ErrorAction SilentlyContinue)) {
|
||||
winget install --id ggml.llamacpp -e --accept-package-agreements --accept-source-agreements
|
||||
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if ! command -v nvidia-smi >/dev/null 2>&1; then
|
||||
echo "nvidia-smi is missing; these runtimes need a CUDA host." >&2
|
||||
exit 1
|
||||
fi
|
||||
nvidia-smi -L >/dev/null
|
||||
uv_bin="${ELY_EYE_UV:-$HOME/.local/bin/uv}"
|
||||
if ! command -v "$uv_bin" >/dev/null 2>&1; then
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
uv_bin="$HOME/.local/bin/uv"
|
||||
fi
|
||||
for venv in .venv-linux .venv-linux-vllm; do
|
||||
"$uv_bin" venv "$venv" --python 3.12
|
||||
done
|
||||
"$uv_bin" pip install --python .venv-linux/bin/python --prerelease=allow -r backend/requirements-sglang-linux.txt
|
||||
"$uv_bin" pip install --python .venv-linux-vllm/bin/python --prerelease=allow -r backend/requirements-vllm-linux.txt
|
||||
"$uv_bin" pip install --python .venv-linux/bin/python --prerelease=allow -r backend/requirements-ktransformers-linux.txt
|
||||
.venv/bin/ely-eye runtime-profiles
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
.venv/bin/python -m compileall ./backend/src
|
||||
.venv/bin/ely-eye status
|
||||
(cd frontend && npm run build)
|
||||
Reference in new issue
Block a user