PyIntell 0.1.0__py3-none-any.whl

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.
pyintell/__init__.py ADDED
@@ -0,0 +1,31 @@
1
+ """pyintell: a modular NumPy-based framework for building AI models."""
2
+
3
+ from .tokenization import *
4
+ from .embeddings import *
5
+ from .attention import *
6
+ from .layers import *
7
+ from .transformer import *
8
+ from .loss import *
9
+ from .optim import *
10
+ from .training import *
11
+ from .generation import *
12
+ from .system import *
13
+ from .autograd import *
14
+ from .utilities import *
15
+ from .serialization import *
16
+ from .quantization import *
17
+ from .scheduling import *
18
+ from .finetuning import *
19
+ from .focus import SUPPORTED_FOCUSES, FOCUS_PROFILES, normalize_focus, build_focus_config, focus_description
20
+ from .model import Model
21
+ from .builder import build
22
+
23
+ __version__ = "0.1.0"
24
+
25
+ # Explicitly expose the high-level model-management API.
26
+ save_model = save_model
27
+ load_model = load_model
28
+ generate = generate
29
+ model_run = model_run
30
+
31
+ __all__ = [name for name in globals() if not name.startswith("_")]
pyintell/attention.py ADDED
@@ -0,0 +1,126 @@
1
+ """Numerically stable attention primitives used by pyintell."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ def _softmax(x):
7
+ x = np.asarray(x, dtype=np.float32)
8
+ x = x - np.max(x, axis=-1, keepdims=True)
9
+ e = np.exp(np.clip(x, -80.0, 80.0))
10
+ return e / np.maximum(np.sum(e, axis=-1, keepdims=True), 1e-12)
11
+
12
+
13
+ def attention(query, key, value, mask=None, dropout=0.0):
14
+ """Scaled dot-product attention with stable masking."""
15
+ q, k, v = map(lambda a: np.asarray(a, dtype=np.float32), (query, key, value))
16
+ depth = q.shape[-1]
17
+ scores = q @ np.swapaxes(k, -1, -2) / np.sqrt(max(depth, 1))
18
+ if mask is not None:
19
+ m = np.asarray(mask, dtype=bool)
20
+ scores = np.where(m, scores, -1e9)
21
+ weights = _softmax(scores)
22
+ if dropout:
23
+ raise NotImplementedError("dropout is intentionally disabled in the NumPy inference core")
24
+ return weights @ v
25
+
26
+
27
+ def scaled_dot_product_attention(query, key, value, mask=None):
28
+ return attention(query, key, value, mask)
29
+
30
+
31
+ def self_attention(x, mask=None):
32
+ return attention(x, x, x, mask)
33
+
34
+
35
+ def cross_attention(query, context, mask=None):
36
+ return attention(query, context, context, mask)
37
+
38
+
39
+ def causal_mask(length):
40
+ return np.tril(np.ones((int(length), int(length)), dtype=bool))
41
+
42
+
43
+ def padding_mask(ids, pad_id=0):
44
+ return np.asarray(ids) != pad_id
45
+
46
+
47
+ def attention_mask(length, causal=False):
48
+ return causal_mask(length) if causal else np.ones((length, length), dtype=bool)
49
+
50
+
51
+ def causal_attention(x):
52
+ return attention(x, x, x, causal_mask(x.shape[-2]))
53
+
54
+
55
+ def multihead_attention(x, heads=8, mask=None, weights=None):
56
+ """Multi-head attention.
57
+
58
+ When ``weights`` is supplied it must contain q/k/v/o projection matrices.
59
+ This makes attention deterministic and trainable instead of regenerating
60
+ random projections on every forward pass.
61
+ """
62
+ x = np.asarray(x, dtype=np.float32)
63
+ heads = int(heads)
64
+ if heads < 1 or x.shape[-1] % heads:
65
+ raise ValueError("embedding dimension must be divisible by heads")
66
+ d_model = x.shape[-1]
67
+ if weights is None:
68
+ q = k = v = x
69
+ output = np.concatenate(
70
+ [attention(a, a, a, mask) for a in np.split(x, heads, axis=-1)], axis=-1
71
+ )
72
+ return output
73
+ q = x @ weights["q"] + weights.get("q_bias", 0.0)
74
+ k = x @ weights["k"] + weights.get("k_bias", 0.0)
75
+ v = x @ weights["v"] + weights.get("v_bias", 0.0)
76
+ d_head = d_model // heads
77
+ q = q.reshape(x.shape[0], heads, d_head).transpose(1, 0, 2)
78
+ k = k.reshape(x.shape[0], heads, d_head).transpose(1, 0, 2)
79
+ v = v.reshape(x.shape[0], heads, d_head).transpose(1, 0, 2)
80
+ attended = attention(q, k, v, mask)
81
+ attended = attended.transpose(1, 0, 2).reshape(x.shape[0], d_model)
82
+ return attended @ weights["o"] + weights.get("o_bias", 0.0)
83
+
84
+
85
+ def multi_query_attention(x, heads=8, mask=None):
86
+ return multihead_attention(x, heads, mask)
87
+
88
+
89
+ def grouped_query_attention(x, heads=8, mask=None):
90
+ return multihead_attention(x, heads, mask)
91
+
92
+
93
+ def local_attention(x, window=128):
94
+ length = x.shape[-2]
95
+ mask = np.zeros((length, length), dtype=bool)
96
+ for i in range(length):
97
+ mask[i, max(0, i - int(window) + 1):i + 1] = True
98
+ return attention(x, x, x, mask)
99
+
100
+
101
+ def sliding_window_attention(x, window=128):
102
+ return local_attention(x, window)
103
+
104
+
105
+ def global_attention(x, mask=None):
106
+ return attention(x, x, x, mask)
107
+
108
+
109
+ def sparse_attention(x, mask):
110
+ return attention(x, x, x, mask)
111
+
112
+
113
+ def block_attention(x, block_size=64):
114
+ return local_attention(x, block_size)
115
+
116
+
117
+ def rotary_attention(x, mask=None):
118
+ return self_attention(x, mask)
119
+
120
+
121
+ def alibi_attention(x, mask=None):
122
+ return self_attention(x, mask)
123
+
124
+
125
+ def flash_attention(query, key, value, mask=None):
126
+ return attention(query, key, value, mask)
pyintell/autograd.py ADDED
@@ -0,0 +1,50 @@
1
+ """Small numerical automatic-differentiation helpers.
2
+
3
+ This module intentionally stays lightweight; it provides finite-difference
4
+ helpers for experiments rather than a production reverse-mode engine.
5
+ """
6
+
7
+ import numpy as np
8
+
9
+
10
+ def gradient(function, x, epsilon=1e-5):
11
+ x = np.asarray(x, dtype=float)
12
+ result = np.zeros_like(x)
13
+ for index in np.ndindex(x.shape):
14
+ plus = x.copy(); minus = x.copy()
15
+ plus[index] += epsilon; minus[index] -= epsilon
16
+ result[index] = (function(plus) - function(minus)) / (2 * epsilon)
17
+ return result
18
+
19
+
20
+ def compute_gradients(function, x, epsilon=1e-5):
21
+ return gradient(function, x, epsilon)
22
+
23
+
24
+ def numerical_gradient(function, x, epsilon=1e-5):
25
+ return gradient(function, x, epsilon)
26
+
27
+
28
+ def backward(function, x, epsilon=1e-5):
29
+ return gradient(function, x, epsilon)
30
+
31
+
32
+ def requires_grad(x, value=True):
33
+ array = np.asarray(x)
34
+ setattr(array, "requires_grad", value) if hasattr(array, "__dict__") else None
35
+ return array
36
+
37
+
38
+ def detach(x):
39
+ return np.array(x, copy=True)
40
+
41
+
42
+ def no_grad(function):
43
+ return function
44
+
45
+
46
+ def zero_grad(gradients):
47
+ if isinstance(gradients, dict):
48
+ for key, value in gradients.items(): gradients[key] = np.zeros_like(value)
49
+ return gradients
50
+ return np.zeros_like(gradients)
pyintell/builder.py ADDED
@@ -0,0 +1,211 @@
1
+ """High-level model builder."""
2
+
3
+ import math
4
+
5
+ from .model import Model
6
+ from .system import ram, storage_info
7
+ from .serialization import set_current_model
8
+
9
+ _SUPPORTED_FOCUS = {
10
+ "intelligence", "natural", "coding", "reasoning", "math", "knowledge",
11
+ "creativity", "conversation", "instruction", "accuracy", "speed", "memory",
12
+ "context", "language", "translation", "summarization", "classification", "roleplay",
13
+ }
14
+ _SUPPORTED_DTYPES = {"float64", "float32", "float16", "bfloat16", "int8", "int4"}
15
+ _SUPPORTED_PLATFORMS = {"auto", "cpu", "gpu", "cuda", "mps", "rocm", "tpu", "android", "mobile"}
16
+
17
+ # Focus is a model specialization, not merely a label. These profiles describe
18
+ # the capabilities the builder should prioritize when choosing defaults.
19
+ _FOCUS_PROFILES = {
20
+ "intelligence": {"reasoning": 1.0, "knowledge": 0.9, "accuracy": 0.9},
21
+ "natural": {"language": 1.0, "conversation": 0.8, "creativity": 0.6},
22
+ "coding": {"reasoning": 1.0, "instruction": 0.9, "accuracy": 0.9},
23
+ "reasoning": {"reasoning": 1.0, "accuracy": 0.9, "math": 0.8},
24
+ "math": {"math": 1.0, "reasoning": 0.9, "accuracy": 0.9},
25
+ "knowledge": {"knowledge": 1.0, "memory": 0.8, "accuracy": 0.8},
26
+ "creativity": {"creativity": 1.0, "language": 0.8, "natural": 0.7},
27
+ "conversation": {"conversation": 1.0, "natural": 0.9, "instruction": 0.7},
28
+ "instruction": {"instruction": 1.0, "accuracy": 0.9, "conversation": 0.6},
29
+ "accuracy": {"accuracy": 1.0, "reasoning": 0.8, "knowledge": 0.7},
30
+ "speed": {"speed": 1.0, "efficiency": 0.9},
31
+ "memory": {"memory": 1.0, "knowledge": 0.8, "context": 0.7},
32
+ "context": {"context": 1.0, "memory": 0.8, "language": 0.6},
33
+ "language": {"language": 1.0, "natural": 0.8, "translation": 0.7},
34
+ "translation": {"translation": 1.0, "language": 0.9, "accuracy": 0.8},
35
+ "summarization": {"summarization": 1.0, "language": 0.8, "accuracy": 0.7},
36
+ "classification": {"classification": 1.0, "accuracy": 0.9, "instruction": 0.6},
37
+ "roleplay": {"roleplay": 1.0, "conversation": 0.9, "creativity": 0.8},
38
+ }
39
+
40
+
41
+ def _dtype_bytes(dtype):
42
+ return {"float64": 8, "float32": 4, "float16": 2, "bfloat16": 2, "int8": 1, "int4": 0.5}[dtype]
43
+
44
+
45
+ def _normalize_focus(focus):
46
+ focuses = [focus] if isinstance(focus, str) else list(focus)
47
+ if not focuses:
48
+ raise ValueError("focus must contain at least one focus")
49
+ normalized = []
50
+ for item in focuses:
51
+ if not isinstance(item, str):
52
+ raise TypeError("focus values must be strings")
53
+ value = item.strip().lower()
54
+ if value not in _SUPPORTED_FOCUS:
55
+ raise ValueError(f"unsupported focus values: {[value]}")
56
+ if value not in normalized:
57
+ normalized.append(value)
58
+ return normalized
59
+
60
+
61
+ def _build_focus_config(focuses):
62
+ priorities = {}
63
+ for focus in focuses:
64
+ for capability, weight in _FOCUS_PROFILES[focus].items():
65
+ priorities[capability] = priorities.get(capability, 0.0) + weight
66
+ scale = max(priorities.values(), default=1.0)
67
+ priorities = {key: round(value / scale, 4) for key, value in priorities.items()}
68
+ return {"focuses": list(focuses), "priorities": priorities}
69
+
70
+
71
+ def _estimate_architecture(target, vocab_size, settings, focus_config):
72
+ # Focus influences defaults only; explicit architecture settings always win.
73
+ priorities = focus_config["priorities"]
74
+ reasoning_boost = max(priorities.get("reasoning", 0.0), priorities.get("math", 0.0))
75
+ context_boost = priorities.get("context", 0.0)
76
+ speed_boost = priorities.get("speed", 0.0)
77
+ default_layers = round((target / 2.0e6) ** 0.45)
78
+ default_layers += int(round(reasoning_boost * 2))
79
+ default_layers -= int(round(speed_boost))
80
+ layers = max(1, int(settings.get("layers", default_layers)))
81
+
82
+ heads_default = 4 if target < 10_000_000 else 8
83
+ heads_default += int(round(reasoning_boost))
84
+ heads = max(1, int(settings.get("heads", heads_default)))
85
+
86
+ embedding_default = round((target / max(vocab_size, 1)) ** 0.5 * 8)
87
+ if context_boost:
88
+ embedding_default += int(round(embedding_default * 0.05 * context_boost))
89
+ embedding_size = max(32, int(settings.get("embedding_size", embedding_default)))
90
+ embedding_size = max(heads, (embedding_size // heads) * heads)
91
+
92
+ hidden_multiplier = 4
93
+ if reasoning_boost:
94
+ hidden_multiplier += int(round(reasoning_boost))
95
+ hidden_size = max(embedding_size, int(settings.get("hidden_size", embedding_size * hidden_multiplier)))
96
+ estimated = vocab_size * embedding_size + layers * (4 * embedding_size**2 + 2 * embedding_size * hidden_size) + embedding_size * vocab_size
97
+ return {"layers": layers, "heads": heads, "embedding_size": embedding_size, "hidden_size": hidden_size, "estimated_parameters": estimated}
98
+
99
+
100
+ def _validate_positive_int(settings, key, default, minimum=1):
101
+ value = int(settings.get(key, default))
102
+ if value < minimum:
103
+ raise ValueError(f"settings['{key}'] must be at least {minimum}")
104
+ return value
105
+
106
+
107
+ def _validate_positive_float(settings, key, default):
108
+ value = float(settings.get(key, default))
109
+ if value <= 0:
110
+ raise ValueError(f"settings['{key}'] must be positive")
111
+ return value
112
+
113
+
114
+ def build(vocab, reverse_vocab, dataset, parameters, focus, dtype=None, settings=None, model_name=None):
115
+ """Build a configurable, focus-specialized Transformer model.
116
+
117
+ ``focus`` selects the capabilities the model should prioritize. Multiple
118
+ focuses can be supplied and are merged into a normalized priority map.
119
+ Focus changes architecture defaults when explicit architecture settings are
120
+ omitted and is persisted in the model for training/inference tooling.
121
+ """
122
+ if not isinstance(vocab, dict) or not isinstance(reverse_vocab, dict):
123
+ raise TypeError("vocab and reverse_vocab must be dictionaries")
124
+ if not isinstance(parameters, int) or isinstance(parameters, bool) or parameters <= 0:
125
+ raise ValueError("parameters must be a positive integer")
126
+ if settings is None:
127
+ settings = {}
128
+ elif not isinstance(settings, dict):
129
+ raise TypeError("settings must be a dictionary or None")
130
+ else:
131
+ settings = dict(settings)
132
+ dtype = "float32" if dtype is None else str(dtype).lower()
133
+ if dtype not in _SUPPORTED_DTYPES:
134
+ raise ValueError(f"unsupported dtype: {dtype}")
135
+
136
+ focuses = _normalize_focus(focus)
137
+ focus_config = _build_focus_config(focuses)
138
+
139
+ if model_name is not None:
140
+ if not isinstance(model_name, str):
141
+ raise TypeError("model_name must be a string or None")
142
+ model_name = model_name.strip()
143
+ if not model_name:
144
+ raise ValueError("model_name must not be empty")
145
+
146
+ platform = str(settings.get("platform", "auto")).lower()
147
+ if platform not in _SUPPORTED_PLATFORMS:
148
+ raise ValueError(f"unsupported platform: {platform}")
149
+ device = settings.get("device")
150
+ seed = settings.get("seed")
151
+ if seed is not None:
152
+ if isinstance(seed, bool):
153
+ raise TypeError("settings['seed'] must be an integer or None")
154
+ seed = int(seed)
155
+ batch_size = _validate_positive_int(settings, "batch_size", 1)
156
+ context_length = _validate_positive_int(settings, "context_length", 512)
157
+ epochs = _validate_positive_int(settings, "epochs", 1)
158
+ gradient_accumulation = _validate_positive_int(settings, "gradient_accumulation", 1)
159
+ learning_rate = _validate_positive_float(settings, "learning_rate", 3e-4)
160
+ weight_decay = float(settings.get("weight_decay", 0.0))
161
+ gradient_clip = float(settings.get("gradient_clip", 1.0))
162
+ dropout = float(settings.get("dropout", 0.0))
163
+ if weight_decay < 0:
164
+ raise ValueError("settings['weight_decay'] must not be negative")
165
+ if gradient_clip < 0:
166
+ raise ValueError("settings['gradient_clip'] must not be negative")
167
+ if not 0 <= dropout < 1:
168
+ raise ValueError("settings['dropout'] must be in [0, 1)")
169
+ optimizer = str(settings.get("optimizer", "adamw")).lower()
170
+ if optimizer not in {"sgd", "adam", "adamw", "rmsprop", "adagrad", "adadelta"}:
171
+ raise ValueError(f"unsupported optimizer: {optimizer}")
172
+
173
+ architecture = _estimate_architecture(parameters, len(vocab), settings, focus_config)
174
+ weight_bytes = parameters * _dtype_bytes(dtype)
175
+ required_storage = int(math.ceil(weight_bytes * 1.25))
176
+ required_ram = int(math.ceil(weight_bytes * (2.0 if dtype in {"int4", "int8"} else 4.0)))
177
+ available_ram = ram().get("available")
178
+ available_storage = storage_info().get("free")
179
+ if available_ram is not None and required_ram > available_ram:
180
+ raise MemoryError(f"requested model may require about {required_ram / 2**30:.2f} GiB RAM, but only {available_ram / 2**30:.2f} GiB is available")
181
+ if available_storage is not None and required_storage > available_storage:
182
+ raise OSError(f"requested model may require about {required_storage / 2**30:.2f} GiB storage, but only {available_storage / 2**30:.2f} GiB is free")
183
+
184
+ final_settings = dict(settings)
185
+ final_settings.update({
186
+ "model_name": model_name or settings.get("model_name", "pyintell_model"),
187
+ "platform": platform,
188
+ "device": device,
189
+ "seed": seed,
190
+ "layers": architecture["layers"],
191
+ "heads": architecture["heads"],
192
+ "embedding_size": architecture["embedding_size"],
193
+ "hidden_size": architecture["hidden_size"],
194
+ "context_length": context_length,
195
+ "batch_size": batch_size,
196
+ "learning_rate": learning_rate,
197
+ "epochs": epochs,
198
+ "optimizer": optimizer,
199
+ "weight_decay": weight_decay,
200
+ "gradient_clip": gradient_clip,
201
+ "gradient_accumulation": gradient_accumulation,
202
+ "dropout": dropout,
203
+ "focus_config": focus_config,
204
+ "estimated_parameters": architecture["estimated_parameters"],
205
+ "estimated_weight_bytes": int(weight_bytes),
206
+ "estimated_ram_bytes": required_ram,
207
+ "estimated_storage_bytes": required_storage,
208
+ })
209
+ model = Model(vocab, reverse_vocab, dataset, parameters, focuses, dtype, final_settings)
210
+ set_current_model(model)
211
+ return model
pyintell/embeddings.py ADDED
@@ -0,0 +1,33 @@
1
+ """Embedding layers implemented with NumPy."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ def embedding(tokens, dimensions=128, weights=None, vocab_size=None):
7
+ ids = np.asarray(tokens, dtype=np.int64)
8
+ if weights is None:
9
+ if vocab_size is None: vocab_size = int(ids.max()) + 1 if ids.size else 1
10
+ weights = np.random.randn(vocab_size, dimensions).astype(np.float32) * 0.02
11
+ return weights[ids]
12
+
13
+
14
+ def positional_embedding(sequence_length, dimensions, weights=None):
15
+ if weights is None: weights = np.random.randn(sequence_length, dimensions).astype(np.float32) * 0.02
16
+ return weights[:sequence_length]
17
+
18
+
19
+ def sinusoidal_embedding(sequence_length, dimensions):
20
+ positions = np.arange(sequence_length)[:, None]
21
+ div = np.exp(np.arange(0, dimensions, 2) * (-np.log(10000.0) / dimensions))
22
+ result = np.zeros((sequence_length, dimensions), dtype=np.float32)
23
+ result[:, 0::2] = np.sin(positions * div)
24
+ result[:, 1::2] = np.cos(positions * div[:result[:, 1::2].shape[1]])
25
+ return result
26
+
27
+
28
+ def rotary_embedding(sequence_length, dimensions): return sinusoidal_embedding(sequence_length, dimensions)
29
+ def position_embedding(sequence_length, dimensions): return positional_embedding(sequence_length, dimensions)
30
+
31
+ def embedding_similarity(a, b):
32
+ a = np.asarray(a); b = np.asarray(b)
33
+ return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-12))
pyintell/finetuning.py ADDED
@@ -0,0 +1,29 @@
1
+ """Fine-tuning helpers and configuration primitives."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ def freeze(model): setattr(model, "frozen", True); return model
7
+ def unfreeze(model): setattr(model, "frozen", False); return model
8
+ def freeze_layers(model, layers=None): setattr(model, "frozen_layers", layers); return model
9
+ def unfreeze_layers(model, layers=None): setattr(model, "frozen_layers", []); return model
10
+ def trainable(model): return not getattr(model, "frozen", False)
11
+ def finetune(model, dataset, **settings): setattr(model, "finetune_settings", dict(settings)); return model
12
+
13
+ def lora(model, rank=8, alpha=16): setattr(model, "lora", {"rank": rank, "alpha": alpha}); return model
14
+ def qlora(model, rank=8, alpha=16): setattr(model, "qlora", {"rank": rank, "alpha": alpha}); return model
15
+ def adapter(model, size=64): setattr(model, "adapter_size", size); return model
16
+ def prompt_tuning(model, tokens=20): setattr(model, "prompt_tuning_tokens", tokens); return model
17
+ def prefix_tuning(model, tokens=20): setattr(model, "prefix_tuning_tokens", tokens); return model
18
+
19
+ def parameter_efficient_finetuning(model, method="lora", **kwargs):
20
+ methods = {"lora": lora, "qlora": qlora, "adapter": adapter, "prompt_tuning": prompt_tuning, "prefix_tuning": prefix_tuning}
21
+ if method not in methods: raise ValueError(f"unknown fine-tuning method: {method}")
22
+ return methods[method](model, **kwargs)
23
+
24
+ def gradient_clipping(gradients, max_norm=1.0):
25
+ norm = np.sqrt(sum(np.sum(np.asarray(g) ** 2) for g in gradients.values())) if isinstance(gradients, dict) else np.linalg.norm(gradients)
26
+ scale = min(1.0, max_norm / (norm + 1e-12)); return {k: v*scale for k,v in gradients.items()} if isinstance(gradients, dict) else gradients*scale
27
+
28
+ def weight_decay(value, rate=0.01): return value * (1-rate)
29
+ def ema(previous, current, decay=0.99): return decay * previous + (1-decay) * current
pyintell/focus.py ADDED
@@ -0,0 +1,81 @@
1
+ """Alpha focus/capability utilities for pyintell.
2
+
3
+ Focus is intentionally lightweight in 0.1.x. It describes intended model
4
+ specialization and provides metadata/priorities for future training systems.
5
+ It does not claim that a focus alone makes a model capable.
6
+ """
7
+
8
+ SUPPORTED_FOCUSES = (
9
+ "intelligence", "natural", "coding", "reasoning", "math", "knowledge",
10
+ "creativity", "conversation", "instruction", "accuracy", "speed", "memory",
11
+ "context", "language", "translation", "summarization", "classification", "roleplay",
12
+ )
13
+
14
+ FOCUS_PROFILES = {
15
+ "intelligence": {"reasoning": 1.0, "knowledge": 0.9, "accuracy": 0.9},
16
+ "natural": {"language": 1.0, "conversation": 0.8, "creativity": 0.6},
17
+ "coding": {"reasoning": 1.0, "instruction": 0.9, "accuracy": 0.9},
18
+ "reasoning": {"reasoning": 1.0, "accuracy": 0.9, "math": 0.8},
19
+ "math": {"math": 1.0, "reasoning": 0.9, "accuracy": 0.9},
20
+ "knowledge": {"knowledge": 1.0, "memory": 0.8, "accuracy": 0.8},
21
+ "creativity": {"creativity": 1.0, "language": 0.8, "natural": 0.7},
22
+ "conversation": {"conversation": 1.0, "natural": 0.9, "instruction": 0.7},
23
+ "instruction": {"instruction": 1.0, "accuracy": 0.9, "conversation": 0.6},
24
+ "accuracy": {"accuracy": 1.0, "reasoning": 0.8, "knowledge": 0.7},
25
+ "speed": {"speed": 1.0, "efficiency": 0.9},
26
+ "memory": {"memory": 1.0, "knowledge": 0.8, "context": 0.7},
27
+ "context": {"context": 1.0, "memory": 0.8, "language": 0.6},
28
+ "language": {"language": 1.0, "natural": 0.8, "translation": 0.7},
29
+ "translation": {"translation": 1.0, "language": 0.9, "accuracy": 0.8},
30
+ "summarization": {"summarization": 1.0, "language": 0.8, "accuracy": 0.7},
31
+ "classification": {"classification": 1.0, "accuracy": 0.9, "instruction": 0.6},
32
+ "roleplay": {"roleplay": 1.0, "conversation": 0.9, "creativity": 0.8},
33
+ }
34
+
35
+
36
+ def normalize_focus(focus):
37
+ """Normalize and validate one or more focus names."""
38
+ if isinstance(focus, str):
39
+ values = [focus]
40
+ else:
41
+ try:
42
+ values = list(focus)
43
+ except TypeError as exc:
44
+ raise TypeError("focus must be a string or iterable of strings") from exc
45
+ if not values:
46
+ raise ValueError("focus must contain at least one focus")
47
+
48
+ result = []
49
+ for value in values:
50
+ if not isinstance(value, str):
51
+ raise TypeError("focus values must be strings")
52
+ value = value.strip().lower()
53
+ if value not in SUPPORTED_FOCUSES:
54
+ raise ValueError(f"unsupported focus: {value}")
55
+ if value not in result:
56
+ result.append(value)
57
+ return result
58
+
59
+
60
+ def build_focus_config(focus):
61
+ """Build a serializable alpha focus configuration."""
62
+ focuses = normalize_focus(focus)
63
+ priorities = {}
64
+ for name in focuses:
65
+ for capability, weight in FOCUS_PROFILES[name].items():
66
+ priorities[capability] = priorities.get(capability, 0.0) + weight
67
+
68
+ maximum = max(priorities.values(), default=1.0)
69
+ priorities = {name: round(value / maximum, 4) for name, value in priorities.items()}
70
+ return {"focuses": focuses, "priorities": priorities}
71
+
72
+
73
+ def focus_description(focus):
74
+ """Return a small human-readable description of a focus selection."""
75
+ config = build_focus_config(focus)
76
+ ranked = sorted(config["priorities"].items(), key=lambda item: (-item[1], item[0]))
77
+ return {
78
+ "focuses": config["focuses"],
79
+ "top_capabilities": [name for name, _ in ranked[:5]],
80
+ "priorities": config["priorities"],
81
+ }
pyintell/generation.py ADDED
@@ -0,0 +1,73 @@
1
+ """Generation and sampling utilities."""
2
+
3
+ import numpy as np
4
+
5
+ from .serialization import get_model
6
+
7
+
8
+ def sample(logits, temperature=1.0, top_k=None, rng=None):
9
+ """Sample one token ID from a logits vector."""
10
+ logits = np.asarray(logits, dtype=np.float64)
11
+ if logits.ndim != 1 or logits.size == 0:
12
+ raise ValueError("logits must be a non-empty one-dimensional array")
13
+ if temperature <= 0:
14
+ raise ValueError("temperature must be greater than zero")
15
+ scaled = logits / temperature
16
+ if top_k is not None:
17
+ if isinstance(top_k, bool):
18
+ raise TypeError("top_k must be an integer")
19
+ top_k = int(top_k)
20
+ if top_k <= 0: raise ValueError("top_k must be greater than zero")
21
+ top_k = min(top_k, scaled.size)
22
+ indices = np.argpartition(scaled, -top_k)[-top_k:]
23
+ filtered = np.full_like(scaled, -np.inf)
24
+ filtered[indices] = scaled[indices]
25
+ scaled = filtered
26
+ scaled -= np.max(scaled)
27
+ probabilities = np.exp(scaled)
28
+ total = probabilities.sum()
29
+ if not np.isfinite(total) or total <= 0:
30
+ raise ValueError("logits produced invalid sampling probabilities")
31
+ probabilities /= total
32
+ generator = rng or np.random.default_rng()
33
+ return int(generator.choice(len(probabilities), p=probabilities))
34
+
35
+
36
+ def generate(model_name, prompt, max_tokens=50, temperature=1.0, top_k=None, **kwargs):
37
+ """Generate a response using a saved model name."""
38
+ model = get_model(model_name)
39
+ if not hasattr(model, "generate"):
40
+ raise TypeError("saved object is not a valid pyintell model")
41
+ return model.generate(prompt, max_tokens=max_tokens, temperature=temperature, top_k=top_k, **kwargs)
42
+
43
+
44
+ def model_run(model_name, max_tokens=50, temperature=1.0, top_k=None, **kwargs):
45
+ """Run an interactive terminal chat session with a saved model.
46
+
47
+ Invalid prompts or generation errors are reported without terminating the
48
+ entire chat session. Enter ``exit``, ``quit``, ``/exit``, or ``/quit`` to
49
+ stop the session.
50
+ """
51
+ # Validate/load once before opening the interactive session.
52
+ get_model(model_name)
53
+ print(f"pyintell model '{model_name}' is running.")
54
+ print("Type 'exit' or 'quit' to stop.")
55
+
56
+ while True:
57
+ try:
58
+ prompt = input("You: ")
59
+ except (EOFError, KeyboardInterrupt):
60
+ print()
61
+ break
62
+ if prompt.strip().lower() in {"exit", "quit", "/exit", "/quit"}:
63
+ break
64
+ if not prompt.strip():
65
+ continue
66
+ try:
67
+ response = generate(model_name, prompt, max_tokens=max_tokens,
68
+ temperature=temperature, top_k=top_k, **kwargs)
69
+ print(f"AI: {response}")
70
+ except (TypeError, ValueError, RuntimeError, FileNotFoundError) as error:
71
+ print(f"AI error: {error}")
72
+
73
+ return None