File size: 5,879 Bytes
0e3d4b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
"""Configuration and hardware detection for SplitBit LLM."""

from __future__ import annotations

import logging
import os
import platform
from dataclasses import dataclass, field
from enum import Enum
from typing import Any

logger = logging.getLogger(__name__)

try:
    import psutil
except ImportError:
    psutil = None  # type: ignore[assignment]


class HardwareTier(str, Enum):
    MOBILE = "mobile"
    MINIMAL = "minimal"
    LIGHT = "light"
    STANDARD = "standard"
    FULL = "full"
    MAXIMUM = "maximum"
    DATACENTER = "datacenter"
    SUPERCOMPUTER = "supercomputer"


@dataclass
class ModelConfig:
    """Model architecture config — auto-adjusted per hardware tier."""
    n_layers: int = 3
    n_heads: int = 4
    d_model: int = 256
    d_ff: int = 512
    vocab_size: int = 4096
    max_seq_len: int = 512
    dropout: float = 0.0

    @property
    def d_head(self) -> int:
        return self.d_model // self.n_heads

    @property
    def param_count(self) -> int:
        """Rough parameter count estimate."""
        emb = self.vocab_size * self.d_model
        attn = 4 * self.d_model * self.d_model * self.n_layers
        ffn = 2 * self.d_model * self.d_ff * self.n_layers
        ln = 2 * self.d_model * self.n_layers
        return emb + attn + ffn + ln


@dataclass
class QuantConfig:
    """SplitBit weight quantization config."""
    format: str = "q4_k_m"
    bpw: float = 4.0

    @staticmethod
    def for_tier(tier: HardwareTier) -> "QuantConfig":
        formats = {
            HardwareTier.MOBILE: ("ternary", 1.585),
            HardwareTier.MINIMAL: ("q2_k", 2.0),
            HardwareTier.LIGHT: ("q3_k_s", 3.0),
            HardwareTier.STANDARD: ("q4_k_m", 4.0),
            HardwareTier.FULL: ("q5_k_m", 5.0),
            HardwareTier.MAXIMUM: ("q8_0", 8.0),
            HardwareTier.DATACENTER: ("fp8_e4m3", 8.0),
            HardwareTier.SUPERCOMPUTER: ("fp16", 16.0),
        }
        fmt, bpw = formats.get(tier, ("q4_k_m", 4.0))
        return QuantConfig(format=fmt, bpw=bpw)


@dataclass
class StorageConfig:
    """Storage paths and limits."""
    data_dir: str = os.path.expanduser("~/.splitbit-llm")
    skill_storage_mb: int = 200
    max_skills: int = 2000

    @staticmethod
    def for_tier(tier: HardwareTier) -> "StorageConfig":
        limits = {
            HardwareTier.MOBILE: (50, 500),
            HardwareTier.MINIMAL: (200, 2000),
            HardwareTier.LIGHT: (1000, 10000),
            HardwareTier.STANDARD: (5000, 50000),
            HardwareTier.FULL: (20000, 200000),
            HardwareTier.MAXIMUM: (50000, 500000),
            HardwareTier.DATACENTER: (200000, 2000000),
            HardwareTier.SUPERCOMPUTER: (1000000, 10000000),
        }
        mb, skills = limits.get(tier, (200, 2000))
        return StorageConfig(skill_storage_mb=mb, max_skills=skills)


def detect_hardware() -> HardwareTier:
    """Detect hardware tier based on available RAM, CPU, and GPU."""
    ram_mb = 0
    cpu_cores = os.cpu_count() or 1

    if psutil:
        ram_mb = psutil.virtual_memory().available // (1024 * 1024)
        cpu_cores = psutil.cpu_count() or cpu_cores

    gpu_vram_mb = 0
    try:
        import torch
        if torch.cuda.is_available():
            gpu_vram_mb = torch.cuda.get_device_properties(0).total_memory // (1024 * 1024)
    except ImportError:
        pass

    is_mobile = platform.machine().startswith(("arm", "aarch")) and ram_mb < 2048

    if is_mobile or ram_mb < 2048:
        return HardwareTier.MOBILE
    if ram_mb < 4096:
        return HardwareTier.MINIMAL
    if ram_mb < 8192:
        return HardwareTier.LIGHT
    if ram_mb < 16384 or gpu_vram_mb < 6144:
        return HardwareTier.STANDARD
    if ram_mb < 32768 or gpu_vram_mb < 12288:
        return HardwareTier.FULL
    if ram_mb < 65536:
        return HardwareTier.MAXIMUM
    if ram_mb < 262144:
        return HardwareTier.DATACENTER
    return HardwareTier.SUPERCOMPUTER


def get_model_config(tier: HardwareTier) -> ModelConfig:
    """Get optimal model config for a hardware tier."""
    configs = {
        HardwareTier.MOBILE: ModelConfig(n_layers=2, n_heads=4, d_model=128, d_ff=256, vocab_size=2048, max_seq_len=256),
        HardwareTier.MINIMAL: ModelConfig(n_layers=3, n_heads=4, d_model=256, d_ff=512, vocab_size=4096, max_seq_len=512),
        HardwareTier.LIGHT: ModelConfig(n_layers=4, n_heads=8, d_model=384, d_ff=768, vocab_size=8192, max_seq_len=1024),
        HardwareTier.STANDARD: ModelConfig(n_layers=6, n_heads=8, d_model=512, d_ff=1024, vocab_size=16384, max_seq_len=2048),
        HardwareTier.FULL: ModelConfig(n_layers=8, n_heads=16, d_model=768, d_ff=2048, vocab_size=32000, max_seq_len=4096),
        HardwareTier.MAXIMUM: ModelConfig(n_layers=12, n_heads=16, d_model=1024, d_ff=3072, vocab_size=64000, max_seq_len=8192),
        HardwareTier.DATACENTER: ModelConfig(n_layers=24, n_heads=32, d_model=2048, d_ff=6144, vocab_size=128000, max_seq_len=16384),
        HardwareTier.SUPERCOMPUTER: ModelConfig(n_layers=48, n_heads=64, d_model=4096, d_ff=12288, vocab_size=128000, max_seq_len=32768),
    }
    return configs.get(tier, configs[HardwareTier.MINIMAL])


@dataclass
class Settings:
    """Global settings for SplitBit LLM."""
    tier: HardwareTier = field(default_factory=detect_hardware)
    model: ModelConfig = field(default_factory=lambda: get_model_config(detect_hardware()))
    quant: QuantConfig = field(default_factory=lambda: QuantConfig.for_tier(detect_hardware()))
    storage: StorageConfig = field(default_factory=lambda: StorageConfig.for_tier(detect_hardware()))

    @staticmethod
    def from_env() -> "Settings":
        tier = detect_hardware()
        return Settings(
            tier=tier,
            model=get_model_config(tier),
            quant=QuantConfig.for_tier(tier),
            storage=StorageConfig.for_tier(tier),
        )