drift-ml 0.1.4__tar.gz → 0.1.6__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.
- {drift_ml-0.1.4 → drift_ml-0.1.6}/PKG-INFO +1 -1
- drift_ml-0.1.6/drift/__main__.py +25 -0
- drift_ml-0.1.6/drift/engine_launcher.py +167 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift_ml.egg-info/PKG-INFO +1 -1
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift_ml.egg-info/SOURCES.txt +1 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift_ml.egg-info/top_level.txt +1 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/pyproject.toml +1 -1
- drift_ml-0.1.4/drift/__main__.py +0 -14
- {drift_ml-0.1.4 → drift_ml-0.1.6}/README.md +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/__init__.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/cli/__init__.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/cli/client.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/cli/repl.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/cli/session.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/llm_adapters/__init__.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/llm_adapters/base.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/llm_adapters/gemini_cli.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift/llm_adapters/local_llm.py +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift_ml.egg-info/dependency_links.txt +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift_ml.egg-info/entry_points.txt +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/drift_ml.egg-info/requires.txt +0 -0
- {drift_ml-0.1.4 → drift_ml-0.1.6}/setup.cfg +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entry point for `python -m drift` or `drift` command.
|
|
3
|
+
Runs the chat-based REPL. If no backend is running, downloads and starts the engine.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from drift.cli.repl import run_repl
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> None:
|
|
13
|
+
base_url = os.environ.get("DRIFT_BACKEND_URL")
|
|
14
|
+
if not base_url:
|
|
15
|
+
from drift.engine_launcher import ensure_engine
|
|
16
|
+
|
|
17
|
+
if not ensure_engine():
|
|
18
|
+
print("drift: Failed to start engine. Set DRIFT_BACKEND_URL or run: npm install -g drift-ml", file=sys.stderr)
|
|
19
|
+
sys.exit(1)
|
|
20
|
+
base_url = f"http://127.0.0.1:{os.environ.get('DRIFT_ENGINE_PORT', '8000')}"
|
|
21
|
+
run_repl(base_url=base_url)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
main()
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Download and start the drift engine binary when no backend is running.
|
|
3
|
+
Makes pipx install drift-ml work standalone (no npm needed).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional, Tuple
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
import requests
|
|
16
|
+
except ImportError:
|
|
17
|
+
requests = None
|
|
18
|
+
|
|
19
|
+
ENGINE_TAG = "v0.1.3"
|
|
20
|
+
GITHUB_REPO = "lakshitsachdeva/drift"
|
|
21
|
+
GITHUB_API = f"https://api.github.com/repos/{GITHUB_REPO}/releases/tags/{ENGINE_TAG}"
|
|
22
|
+
ENGINE_PORT = os.environ.get("DRIFT_ENGINE_PORT", "8000")
|
|
23
|
+
HEALTH_URL = f"http://127.0.0.1:{ENGINE_PORT}/health"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_platform_key() -> Tuple[str, str]:
|
|
27
|
+
"""Return (plat, arch) e.g. ('macos', 'arm64')."""
|
|
28
|
+
p = platform.system().lower()
|
|
29
|
+
a = platform.machine().lower()
|
|
30
|
+
plat = "macos" if p == "darwin" else "windows" if p == "windows" else "linux"
|
|
31
|
+
arch = "arm64" if a in ("arm64", "aarch64") else "x64"
|
|
32
|
+
if p == "windows" and "amd64" in a:
|
|
33
|
+
arch = "x64"
|
|
34
|
+
return plat, arch
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _get_engine_dir() -> Optional[Path]:
|
|
38
|
+
home = os.environ.get("HOME") or os.environ.get("USERPROFILE")
|
|
39
|
+
if not home:
|
|
40
|
+
return None
|
|
41
|
+
return Path(home) / ".drift" / "bin"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_engine_path() -> Optional[Path]:
|
|
45
|
+
d = _get_engine_dir()
|
|
46
|
+
if not d:
|
|
47
|
+
return None
|
|
48
|
+
plat, arch = _get_platform_key()
|
|
49
|
+
ext = ".exe" if platform.system() == "Windows" else ""
|
|
50
|
+
return d / f"drift-engine-{plat}-{arch}{ext}"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _engine_running() -> bool:
|
|
54
|
+
if not requests:
|
|
55
|
+
return False
|
|
56
|
+
try:
|
|
57
|
+
r = requests.get(HEALTH_URL, timeout=2)
|
|
58
|
+
return r.status_code == 200
|
|
59
|
+
except Exception:
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _get_asset_download_url(asset_name: str) -> str:
|
|
64
|
+
"""Resolve GitHub release asset to download URL."""
|
|
65
|
+
token = os.environ.get("DRIFT_GITHUB_TOKEN") or os.environ.get("GITHUB_TOKEN")
|
|
66
|
+
headers = {
|
|
67
|
+
"User-Agent": "Drift-Engine-Launcher/1.0",
|
|
68
|
+
"Accept": "application/vnd.github+json",
|
|
69
|
+
}
|
|
70
|
+
if token:
|
|
71
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
72
|
+
r = requests.get(GITHUB_API, headers=headers, timeout=15)
|
|
73
|
+
if r.status_code == 404:
|
|
74
|
+
raise RuntimeError(
|
|
75
|
+
f"Release {ENGINE_TAG} not found. "
|
|
76
|
+
"If the repo is private, set DRIFT_GITHUB_TOKEN with repo read access."
|
|
77
|
+
)
|
|
78
|
+
r.raise_for_status()
|
|
79
|
+
data = r.json()
|
|
80
|
+
for a in data.get("assets", []):
|
|
81
|
+
if a.get("name") == asset_name:
|
|
82
|
+
# Prefer browser_download_url for public repos (no auth needed)
|
|
83
|
+
url = a.get("browser_download_url")
|
|
84
|
+
if url:
|
|
85
|
+
return url
|
|
86
|
+
# API URL requires Accept: application/octet-stream
|
|
87
|
+
api_url = a.get("url")
|
|
88
|
+
if api_url:
|
|
89
|
+
return api_url
|
|
90
|
+
raise RuntimeError(f"Asset {asset_name} not found in release {ENGINE_TAG}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _download_file(url: str, dest: Path) -> None:
|
|
94
|
+
token = os.environ.get("DRIFT_GITHUB_TOKEN") or os.environ.get("GITHUB_TOKEN")
|
|
95
|
+
headers = {"User-Agent": "Drift-Engine-Launcher/1.0"}
|
|
96
|
+
# API URLs need Accept: application/octet-stream; browser_download_url works with default
|
|
97
|
+
if "api.github.com" in url:
|
|
98
|
+
headers["Accept"] = "application/octet-stream"
|
|
99
|
+
if token:
|
|
100
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
101
|
+
r = requests.get(url, headers=headers, stream=True, timeout=60)
|
|
102
|
+
r.raise_for_status()
|
|
103
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
104
|
+
with open(dest, "wb") as f:
|
|
105
|
+
for chunk in r.iter_content(chunk_size=65536):
|
|
106
|
+
f.write(chunk)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def ensure_engine() -> bool:
|
|
110
|
+
"""
|
|
111
|
+
If engine not running: download (if needed), start it, wait for health.
|
|
112
|
+
Returns True if engine is ready, False on failure.
|
|
113
|
+
"""
|
|
114
|
+
if _engine_running():
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
if not requests:
|
|
118
|
+
print("drift: 'requests' required for engine download. pip install requests", file=sys.stderr)
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
bin_path = _get_engine_path()
|
|
122
|
+
bin_dir = _get_engine_dir()
|
|
123
|
+
if not bin_path or not bin_dir:
|
|
124
|
+
print("drift: Could not resolve engine dir (~/.drift/bin). Set HOME or USERPROFILE.", file=sys.stderr)
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
bin_dir.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
|
|
129
|
+
if not bin_path.exists():
|
|
130
|
+
plat, arch = _get_platform_key()
|
|
131
|
+
ext = ".exe" if platform.system() == "Windows" else ""
|
|
132
|
+
asset = f"drift-engine-{plat}-{arch}{ext}"
|
|
133
|
+
print(f"drift: Downloading engine ({asset})...", file=sys.stderr)
|
|
134
|
+
try:
|
|
135
|
+
url = _get_asset_download_url(asset)
|
|
136
|
+
_download_file(url, bin_path)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
print(f"drift: Download failed: {e}", file=sys.stderr)
|
|
139
|
+
return False
|
|
140
|
+
if platform.system() != "Windows":
|
|
141
|
+
bin_path.chmod(0o755)
|
|
142
|
+
if platform.system() == "Darwin":
|
|
143
|
+
try:
|
|
144
|
+
subprocess.run(["xattr", "-dr", "com.apple.quarantine", str(bin_path)], check=False, capture_output=True)
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
# Start engine
|
|
149
|
+
env = {**os.environ, "DRIFT_ENGINE_PORT": ENGINE_PORT}
|
|
150
|
+
proc = subprocess.Popen(
|
|
151
|
+
[str(bin_path)],
|
|
152
|
+
cwd=str(bin_dir),
|
|
153
|
+
env=env,
|
|
154
|
+
stdout=subprocess.DEVNULL,
|
|
155
|
+
stderr=subprocess.DEVNULL,
|
|
156
|
+
start_new_session=True,
|
|
157
|
+
)
|
|
158
|
+
proc.wait() # wait briefly to avoid race
|
|
159
|
+
del proc
|
|
160
|
+
|
|
161
|
+
# Poll for health
|
|
162
|
+
for _ in range(60):
|
|
163
|
+
if _engine_running():
|
|
164
|
+
return True
|
|
165
|
+
import time
|
|
166
|
+
time.sleep(0.5)
|
|
167
|
+
return False
|
drift_ml-0.1.4/drift/__main__.py
DELETED
|
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
|