quarterbit 0.1.0__tar.gz → 0.1.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quarterbit
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Precision optimizer for PyTorch - 1,000,000x more accurate than FP32
5
5
  Author-email: Kyle Clouthier <info@quarterbit.dev>
6
6
  License: Proprietary - Free tier available, commercial use requires license
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quarterbit"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "Precision optimizer for PyTorch - 1,000,000x more accurate than FP32"
9
9
  readme = "README.md"
10
10
  license = {text = "Proprietary - Free tier available, commercial use requires license"}
@@ -40,6 +40,9 @@ Documentation = "https://quarterbit.dev/docs"
40
40
  [project.optional-dependencies]
41
41
  dev = ["pytest", "build", "twine"]
42
42
 
43
+ [project.scripts]
44
+ quarterbit = "quarterbit.license:main"
45
+
43
46
  [tool.setuptools.packages.find]
44
47
  where = ["."]
45
48
  include = ["quarterbit*"]
@@ -0,0 +1,73 @@
1
+ """
2
+ QuarterBit
3
+ ==========
4
+
5
+ Precision optimizer for PyTorch. 1,000,000x more accurate than FP32.
6
+
7
+ Usage:
8
+ from quarterbit.torch import CompactEFTAdam
9
+ optimizer = CompactEFTAdam(model.parameters(), lr=1e-3)
10
+
11
+ Activate license:
12
+ quarterbit activate <YOUR_LICENSE_KEY>
13
+
14
+ Copyright (c) 2026 Clouthier Simulation Labs. All rights reserved.
15
+ """
16
+
17
+ __version__ = "0.1.1"
18
+ __author__ = "Kyle Clouthier"
19
+ __license__ = "Proprietary"
20
+
21
+ from .license import (
22
+ get_license_info,
23
+ activate,
24
+ deactivate,
25
+ check_license,
26
+ track_usage,
27
+ LicenseError,
28
+ UsageLimitError,
29
+ )
30
+
31
+ # Get current license tier on import
32
+ _license_info = get_license_info()
33
+ _tier = _license_info["tier"]
34
+
35
+ # Show activation message for free tier (only once)
36
+ if not _license_info["activated"]:
37
+ import sys
38
+ if sys.stderr.isatty():
39
+ print(
40
+ f"QuarterBit v{__version__} (Free tier - {_license_info['usage']['gpu_hours_limit']} GPU-hrs/month)\n"
41
+ f"Activate: quarterbit activate <YOUR_KEY> | Get key: quarterbit.dev",
42
+ file=sys.stderr
43
+ )
44
+
45
+
46
+ def get_version():
47
+ """Get QuarterBit version."""
48
+ return __version__
49
+
50
+
51
+ def get_tier():
52
+ """Get current license tier."""
53
+ return _tier
54
+
55
+
56
+ def is_available():
57
+ """Check if QuarterBit GPU backend is available."""
58
+ try:
59
+ from .torch.utils import is_available as _gpu_available
60
+ return _gpu_available()
61
+ except ImportError:
62
+ return False
63
+
64
+
65
+ # Lazy imports for submodules
66
+ def __getattr__(name):
67
+ if name == "torch":
68
+ from . import torch
69
+ return torch
70
+ if name == "license":
71
+ from . import license
72
+ return license
73
+ raise AttributeError(f"module 'quarterbit' has no attribute '{name}'")
@@ -0,0 +1,348 @@
1
+ """
2
+ QuarterBit License Management
3
+ =============================
4
+
5
+ Handles license activation, validation, and usage tracking.
6
+
7
+ Usage:
8
+ # Activate license (one-time)
9
+ quarterbit activate <LICENSE_KEY>
10
+
11
+ # Or in Python
12
+ from quarterbit.license import activate
13
+ activate("QB-XXXX-XXXX-XXXX")
14
+ """
15
+
16
+ import os
17
+ import json
18
+ import hashlib
19
+ import platform
20
+ import urllib.request
21
+ import urllib.error
22
+ from pathlib import Path
23
+ from datetime import datetime, timedelta
24
+ from typing import Optional, Dict, Any
25
+
26
+ # License server endpoint
27
+ LICENSE_API = "https://us-central1-quarterbit.cloudfunctions.net"
28
+
29
+ # Local config directory
30
+ CONFIG_DIR = Path.home() / ".quarterbit"
31
+ LICENSE_FILE = CONFIG_DIR / "license.json"
32
+ USAGE_FILE = CONFIG_DIR / "usage.json"
33
+
34
+ # License tiers and limits
35
+ TIER_LIMITS = {
36
+ "FREE": {"gpu_hours_month": 10, "commercial": False},
37
+ "PRO": {"gpu_hours_month": 1000, "commercial": True},
38
+ "TEAM": {"gpu_hours_month": 10000, "commercial": True},
39
+ "ENTERPRISE": {"gpu_hours_month": float("inf"), "commercial": True},
40
+ }
41
+
42
+
43
+ def _get_machine_id() -> str:
44
+ """Generate a unique machine identifier."""
45
+ data = f"{platform.node()}-{platform.machine()}-{platform.processor()}"
46
+ return hashlib.sha256(data.encode()).hexdigest()[:16]
47
+
48
+
49
+ def _ensure_config_dir():
50
+ """Create config directory if it doesn't exist."""
51
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
52
+
53
+
54
+ def _load_license() -> Optional[Dict[str, Any]]:
55
+ """Load license from local file."""
56
+ if not LICENSE_FILE.exists():
57
+ return None
58
+ try:
59
+ with open(LICENSE_FILE) as f:
60
+ return json.load(f)
61
+ except:
62
+ return None
63
+
64
+
65
+ def _save_license(data: Dict[str, Any]):
66
+ """Save license to local file."""
67
+ _ensure_config_dir()
68
+ with open(LICENSE_FILE, "w") as f:
69
+ json.dump(data, f, indent=2)
70
+
71
+
72
+ def _load_usage() -> Dict[str, Any]:
73
+ """Load usage tracking data."""
74
+ if not USAGE_FILE.exists():
75
+ return {"sessions": [], "gpu_seconds": 0, "month": datetime.now().strftime("%Y-%m")}
76
+ try:
77
+ with open(USAGE_FILE) as f:
78
+ data = json.load(f)
79
+ # Reset if new month
80
+ current_month = datetime.now().strftime("%Y-%m")
81
+ if data.get("month") != current_month:
82
+ data = {"sessions": [], "gpu_seconds": 0, "month": current_month}
83
+ return data
84
+ except:
85
+ return {"sessions": [], "gpu_seconds": 0, "month": datetime.now().strftime("%Y-%m")}
86
+
87
+
88
+ def _save_usage(data: Dict[str, Any]):
89
+ """Save usage tracking data."""
90
+ _ensure_config_dir()
91
+ with open(USAGE_FILE, "w") as f:
92
+ json.dump(data, f, indent=2)
93
+
94
+
95
+ def activate(license_key: str) -> Dict[str, Any]:
96
+ """
97
+ Activate a license key.
98
+
99
+ Args:
100
+ license_key: License key from quarterbit.dev dashboard
101
+
102
+ Returns:
103
+ dict with activation status and tier info
104
+
105
+ Raises:
106
+ ValueError: If license key is invalid
107
+ """
108
+ if not license_key or not license_key.startswith("QB-"):
109
+ raise ValueError("Invalid license key format. Expected QB-XXXX-XXXX-XXXX")
110
+
111
+ machine_id = _get_machine_id()
112
+
113
+ # Validate with server
114
+ try:
115
+ req_data = json.dumps({
116
+ "licenseKey": license_key,
117
+ "machineId": machine_id,
118
+ "platform": platform.system(),
119
+ "pythonVersion": platform.python_version(),
120
+ }).encode()
121
+
122
+ req = urllib.request.Request(
123
+ f"{LICENSE_API}/activateLicense",
124
+ data=req_data,
125
+ headers={"Content-Type": "application/json"},
126
+ method="POST"
127
+ )
128
+
129
+ with urllib.request.urlopen(req, timeout=10) as resp:
130
+ result = json.loads(resp.read().decode())
131
+
132
+ except urllib.error.HTTPError as e:
133
+ error_body = e.read().decode() if e.fp else str(e)
134
+ raise ValueError(f"Activation failed: {error_body}")
135
+ except urllib.error.URLError:
136
+ raise ValueError("Cannot reach license server. Check your internet connection.")
137
+
138
+ if not result.get("valid"):
139
+ raise ValueError(result.get("error", "Invalid license key"))
140
+
141
+ # Save locally
142
+ license_data = {
143
+ "key": license_key,
144
+ "tier": result.get("tier", "FREE"),
145
+ "email": result.get("email"),
146
+ "activated_at": datetime.now().isoformat(),
147
+ "machine_id": machine_id,
148
+ "expires_at": result.get("expiresAt"),
149
+ }
150
+ _save_license(license_data)
151
+
152
+ return {
153
+ "status": "activated",
154
+ "tier": license_data["tier"],
155
+ "email": license_data["email"],
156
+ "message": f"License activated! Tier: {license_data['tier']}"
157
+ }
158
+
159
+
160
+ def deactivate():
161
+ """Remove license from this machine."""
162
+ if LICENSE_FILE.exists():
163
+ LICENSE_FILE.unlink()
164
+ if USAGE_FILE.exists():
165
+ USAGE_FILE.unlink()
166
+
167
+
168
+ def get_license_info() -> Dict[str, Any]:
169
+ """Get current license information."""
170
+ license_data = _load_license()
171
+
172
+ if not license_data:
173
+ return {
174
+ "tier": "FREE",
175
+ "activated": False,
176
+ "limits": TIER_LIMITS["FREE"],
177
+ }
178
+
179
+ usage = _load_usage()
180
+ gpu_hours_used = usage.get("gpu_seconds", 0) / 3600
181
+ tier = license_data.get("tier", "FREE")
182
+ limits = TIER_LIMITS.get(tier, TIER_LIMITS["FREE"])
183
+
184
+ return {
185
+ "tier": tier,
186
+ "activated": True,
187
+ "email": license_data.get("email"),
188
+ "key": license_data.get("key", "")[:10] + "...",
189
+ "activated_at": license_data.get("activated_at"),
190
+ "limits": limits,
191
+ "usage": {
192
+ "gpu_hours_used": round(gpu_hours_used, 2),
193
+ "gpu_hours_limit": limits["gpu_hours_month"],
194
+ "month": usage.get("month"),
195
+ }
196
+ }
197
+
198
+
199
+ def check_license() -> bool:
200
+ """
201
+ Check if license is valid for current usage.
202
+
203
+ Returns:
204
+ True if valid, raises exception if not
205
+ """
206
+ info = get_license_info()
207
+ tier = info["tier"]
208
+
209
+ # Free tier always allowed (with limits enforced at usage)
210
+ if tier == "FREE":
211
+ return True
212
+
213
+ # Check if license expired (for paid tiers)
214
+ license_data = _load_license()
215
+ if license_data and license_data.get("expires_at"):
216
+ expires = datetime.fromisoformat(license_data["expires_at"].replace("Z", "+00:00"))
217
+ if datetime.now(expires.tzinfo) > expires:
218
+ raise LicenseError("License expired. Please renew at quarterbit.dev")
219
+
220
+ return True
221
+
222
+
223
+ def track_usage(gpu_seconds: float):
224
+ """
225
+ Track GPU usage for this session.
226
+
227
+ Args:
228
+ gpu_seconds: Number of GPU seconds used
229
+ """
230
+ usage = _load_usage()
231
+ usage["gpu_seconds"] = usage.get("gpu_seconds", 0) + gpu_seconds
232
+ usage["sessions"].append({
233
+ "timestamp": datetime.now().isoformat(),
234
+ "gpu_seconds": gpu_seconds,
235
+ })
236
+ # Keep last 100 sessions only
237
+ usage["sessions"] = usage["sessions"][-100:]
238
+ _save_usage(usage)
239
+
240
+ # Check limits
241
+ info = get_license_info()
242
+ gpu_hours_used = usage["gpu_seconds"] / 3600
243
+ gpu_hours_limit = info["limits"]["gpu_hours_month"]
244
+
245
+ if gpu_hours_used > gpu_hours_limit:
246
+ tier = info["tier"]
247
+ if tier == "FREE":
248
+ raise UsageLimitError(
249
+ f"Free tier limit exceeded ({gpu_hours_limit} GPU-hours/month). "
250
+ f"Upgrade at quarterbit.dev for unlimited usage."
251
+ )
252
+
253
+
254
+ def report_usage():
255
+ """Report usage to server (called periodically)."""
256
+ license_data = _load_license()
257
+ if not license_data:
258
+ return
259
+
260
+ usage = _load_usage()
261
+
262
+ try:
263
+ req_data = json.dumps({
264
+ "licenseKey": license_data.get("key"),
265
+ "machineId": _get_machine_id(),
266
+ "gpuSeconds": usage.get("gpu_seconds", 0),
267
+ "sessionCount": len(usage.get("sessions", [])),
268
+ "month": usage.get("month"),
269
+ }).encode()
270
+
271
+ req = urllib.request.Request(
272
+ f"{LICENSE_API}/reportUsage",
273
+ data=req_data,
274
+ headers={"Content-Type": "application/json"},
275
+ method="POST"
276
+ )
277
+
278
+ with urllib.request.urlopen(req, timeout=5) as resp:
279
+ pass # Fire and forget
280
+ except:
281
+ pass # Silent fail - usage reporting is best-effort
282
+
283
+
284
+ class LicenseError(Exception):
285
+ """License validation error."""
286
+ pass
287
+
288
+
289
+ class UsageLimitError(Exception):
290
+ """Usage limit exceeded."""
291
+ pass
292
+
293
+
294
+ # CLI interface
295
+ def main():
296
+ """CLI entry point for license management."""
297
+ import sys
298
+
299
+ if len(sys.argv) < 2:
300
+ print("QuarterBit License Manager")
301
+ print("=" * 40)
302
+ info = get_license_info()
303
+ print(f"Tier: {info['tier']}")
304
+ print(f"Activated: {info['activated']}")
305
+ if info['activated']:
306
+ print(f"Email: {info.get('email', 'N/A')}")
307
+ print(f"Key: {info.get('key', 'N/A')}")
308
+ print(f"\nUsage this month:")
309
+ print(f" GPU hours: {info['usage']['gpu_hours_used']:.2f} / {info['usage']['gpu_hours_limit']}")
310
+ print(f"\nCommands:")
311
+ print(" quarterbit activate <KEY> - Activate license")
312
+ print(" quarterbit deactivate - Remove license")
313
+ print(" quarterbit status - Show this info")
314
+ return
315
+
316
+ cmd = sys.argv[1].lower()
317
+
318
+ if cmd == "activate":
319
+ if len(sys.argv) < 3:
320
+ print("Usage: quarterbit activate <LICENSE_KEY>")
321
+ sys.exit(1)
322
+ key = sys.argv[2]
323
+ try:
324
+ result = activate(key)
325
+ print(f"Success! {result['message']}")
326
+ except ValueError as e:
327
+ print(f"Error: {e}")
328
+ sys.exit(1)
329
+
330
+ elif cmd == "deactivate":
331
+ deactivate()
332
+ print("License deactivated.")
333
+
334
+ elif cmd == "status":
335
+ info = get_license_info()
336
+ print(f"Tier: {info['tier']}")
337
+ print(f"Activated: {info['activated']}")
338
+ if info['activated']:
339
+ print(f"Email: {info.get('email', 'N/A')}")
340
+ print(f"GPU hours used: {info['usage']['gpu_hours_used']:.2f} / {info['usage']['gpu_hours_limit']}")
341
+
342
+ else:
343
+ print(f"Unknown command: {cmd}")
344
+ sys.exit(1)
345
+
346
+
347
+ if __name__ == "__main__":
348
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quarterbit
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Precision optimizer for PyTorch - 1,000,000x more accurate than FP32
5
5
  Author-email: Kyle Clouthier <info@quarterbit.dev>
6
6
  License: Proprietary - Free tier available, commercial use requires license
@@ -2,9 +2,11 @@ LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
4
  quarterbit/__init__.py
5
+ quarterbit/license.py
5
6
  quarterbit.egg-info/PKG-INFO
6
7
  quarterbit.egg-info/SOURCES.txt
7
8
  quarterbit.egg-info/dependency_links.txt
9
+ quarterbit.egg-info/entry_points.txt
8
10
  quarterbit.egg-info/requires.txt
9
11
  quarterbit.egg-info/top_level.txt
10
12
  quarterbit/torch/__init__.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ quarterbit = quarterbit.license:main
@@ -1,94 +0,0 @@
1
- """
2
- QuarterBit
3
- ==========
4
-
5
- Precision computing library.
6
-
7
- Usage:
8
- from quarterbit.torch import Adam
9
-
10
- Copyright (c) 2026 Clouthier Simulation Labs. All rights reserved.
11
- """
12
-
13
- __version__ = "1.0.0"
14
- __author__ = "Kyle Clouthier"
15
- __license__ = "Proprietary"
16
-
17
- import os as _os
18
- import hashlib as _hashlib
19
- import platform as _platform
20
-
21
- # ============================================================================
22
- # License Validation (stub - implement full validation later)
23
- # ============================================================================
24
-
25
- _LICENSE_KEY = _os.environ.get("QUARTERBIT_LICENSE_KEY", "")
26
- _VALIDATED = False
27
-
28
- def _validate_license():
29
- """Validate license key. Returns tier or raises."""
30
- global _VALIDATED
31
-
32
- if _VALIDATED:
33
- return True
34
-
35
- # Free tier: limited functionality
36
- if not _LICENSE_KEY:
37
- _VALIDATED = True
38
- return "free"
39
-
40
- # TODO: Implement full license validation against server
41
- # For now, any key = pro tier
42
- _VALIDATED = True
43
- return "pro"
44
-
45
- def _check_environment():
46
- """Check for debugging/reverse engineering attempts."""
47
- # Basic anti-debug checks
48
- suspicious = []
49
-
50
- # Check for common debuggers
51
- if _os.environ.get("PYTHONDEBUG"):
52
- suspicious.append("debug_mode")
53
-
54
- # Check for trace
55
- import sys
56
- if sys.gettrace() is not None:
57
- suspicious.append("trace_active")
58
-
59
- return suspicious
60
-
61
- # Run checks on import
62
- _tier = _validate_license()
63
- _suspicious = _check_environment()
64
-
65
- if _suspicious and _tier == "free":
66
- import warnings
67
- warnings.warn("QuarterBit: Debug mode detected. Some features disabled.")
68
-
69
- # ============================================================================
70
- # Public API
71
- # ============================================================================
72
-
73
- def get_version():
74
- """Get QuarterBit version."""
75
- return __version__
76
-
77
- def get_license_tier():
78
- """Get current license tier."""
79
- return _tier
80
-
81
- def is_available():
82
- """Check if QuarterBit GPU backend is available."""
83
- try:
84
- from .torch.utils import is_available as _gpu_available
85
- return _gpu_available()
86
- except ImportError:
87
- return False
88
-
89
- # Lazy imports for submodules
90
- def __getattr__(name):
91
- if name == "torch":
92
- from . import torch
93
- return torch
94
- raise AttributeError(f"module 'quarterbit' has no attribute '{name}'")
File without changes
File without changes
File without changes