nsos-mamba 0.1.0__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.
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: nsos-mamba
3
+ Version: 0.1.0
4
+ Summary: Wrapper Python isolado para o módulo Mamba do NSOS com streaming incremental padrão.
5
+ Author: Oxta
6
+ Project-URL: Homepage, https://github.com/vitorGgC569/reimagined
7
+ Project-URL: Documentation, https://github.com/vitorGgC569/reimagined/tree/nsos-gpu-phases12/OXN/nsos/docs
8
+ Project-URL: Source, https://github.com/vitorGgC569/reimagined
9
+ Keywords: mamba,ssm,nsos,edge-ai,streaming
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: numpy>=1.23
21
+
22
+ # NSOS Mamba
23
+
24
+ `nsos-mamba` é um wrapper Python simples para usar apenas o módulo Mamba do
25
+ NSOS. Ele configura automaticamente o modelo em modo Mamba-2 faithful, ativa
26
+ streaming incremental por padrão e oferece utilitários pequenos para treinar
27
+ pares `prompt -> resposta`.
28
+
29
+ Este pacote não instala o runtime nativo `nsos_ext`. Antes de usar, compile ou
30
+ copie o `nsos_ext.so`/`nsos_ext.pyd` e deixe-o acessível via `PYTHONPATH` ou
31
+ pela variável `NSOS_EXT_PATH`.
32
+
33
+ ## Instalação
34
+
35
+ ```bash
36
+ pip install nsos-mamba
37
+ ```
38
+
39
+ Se o `nsos_ext` estiver em uma pasta separada:
40
+
41
+ ```bash
42
+ export NSOS_EXT_PATH=/caminho/para/nsos_ext
43
+ ```
44
+
45
+ No Colab:
46
+
47
+ ```python
48
+ import sys, os
49
+
50
+ sys.path.insert(0, "/content/nsos_ext_mambavs")
51
+ # ou:
52
+ os.environ["NSOS_EXT_PATH"] = "/content/nsos_ext_mambavs"
53
+ ```
54
+
55
+ ## Configurações principais
56
+
57
+ ```python
58
+ from nsos_mamba import MambaModuleConfig
59
+
60
+ cfg = MambaModuleConfig(
61
+ vocab_size=128,
62
+ num_layers=12,
63
+ d_model=128,
64
+ device="auto",
65
+ streaming=True,
66
+ )
67
+ ```
68
+
69
+ Campos mais usados:
70
+
71
+ - `vocab_size`: tamanho do vocabulário.
72
+ - `num_layers`: número de camadas Mamba.
73
+ - `d_model`: largura do modelo.
74
+ - `d_state`: tamanho do estado SSM. Padrão: `64`.
75
+ - `max_context_tokens`: limite de contexto. Padrão: `4096`.
76
+ - `device`: `"auto"`, `"gpu"`, `"cuda"` ou `"cpu"`.
77
+ - `streaming`: ativa decode incremental. Padrão: `True`.
78
+ - `allow_cpu_fallback`: se a GPU falhar, cai para CPU. Padrão: `True`.
79
+
80
+ Variáveis de ambiente equivalentes:
81
+
82
+ ```bash
83
+ NSOS_MAMBA_DEVICE=auto
84
+ NSOS_MAMBA_STREAMING=1
85
+ NSOS_MAMBA_LAYERS=12
86
+ NSOS_MAMBA_DMODEL=128
87
+ NSOS_MAMBA_DSTATE=64
88
+ NSOS_MAMBA_CONTEXT=4096
89
+ ```
90
+
91
+ ## Exemplo mínimo: "Quem é você?" -> "Oxta"
92
+
93
+ ```python
94
+ from nsos_mamba import CharTokenizer, MambaModuleConfig, NSOSMamba, TextPair
95
+
96
+ pairs = [
97
+ TextPair("Quem é você?", "Oxta"),
98
+ TextPair("quem é você?", "Oxta"),
99
+ TextPair("Quem e voce?", "Oxta"),
100
+ ]
101
+
102
+ tok = CharTokenizer.from_texts(
103
+ [p.prompt for p in pairs] + [p.answer for p in pairs]
104
+ )
105
+
106
+ cfg = MambaModuleConfig(
107
+ vocab_size=tok.vocab_size,
108
+ num_layers=12,
109
+ d_model=128,
110
+ device="auto",
111
+ streaming=True,
112
+ )
113
+
114
+ mamba = NSOSMamba(cfg)
115
+
116
+ mamba.fit_text_pairs(
117
+ pairs,
118
+ tok,
119
+ steps=1200,
120
+ batch_size=16,
121
+ learning_rate=3e-3,
122
+ callback=lambda step, loss: print(f"[train] step {step} loss~{loss:.4f}"),
123
+ )
124
+
125
+ result = mamba.generate_text("Quem é você?", tok, max_new_tokens=8)
126
+
127
+ print("resposta:", repr(result.text))
128
+ print("tokens/s:", result.decode_tokens_per_sec)
129
+ ```
130
+
131
+ Saída esperada:
132
+
133
+ ```text
134
+ resposta: 'Oxta'
135
+ ```
136
+
137
+ ## Streaming incremental
138
+
139
+ O streaming incremental é o padrão. O fluxo usado é:
140
+
141
+ 1. processa o prompt uma vez (`prefill`);
142
+ 2. gera cada token novo passando apenas o último token;
143
+ 3. reaproveita o estado interno do modelo.
144
+
145
+ Benchmark:
146
+
147
+ ```python
148
+ prompt_ids = tok.encode("Quem é você?", bos=True)
149
+ bench = mamba.benchmark_decode(prompt_ids, max_new_tokens=1024, repeats=5)
150
+ bench.print()
151
+ ```
152
+
153
+ Métricas:
154
+
155
+ - `prefill_ms`: tempo para processar o prompt.
156
+ - `first_incremental_ms`: primeiro passo incremental após o prefill.
157
+ - `decode_tokens_per_sec`: tokens/s do decode incremental.
158
+ - `end_to_end_tokens_per_sec`: tokens/s incluindo prefill.
159
+
160
+ ## Datasets
161
+
162
+ ### Lista em memória
163
+
164
+ ```python
165
+ from nsos_mamba import TextPair
166
+
167
+ pairs = [
168
+ TextPair(prompt="Quem é você?", answer="Oxta"),
169
+ TextPair(prompt="Qual seu nome?", answer="Oxta"),
170
+ ]
171
+ ```
172
+
173
+ ### JSON
174
+
175
+ Arquivo `dataset.json`:
176
+
177
+ ```json
178
+ [
179
+ {"prompt": "Quem é você?", "answer": "Oxta"},
180
+ {"prompt": "Qual seu nome?", "answer": "Oxta"}
181
+ ]
182
+ ```
183
+
184
+ Uso:
185
+
186
+ ```python
187
+ from nsos_mamba import load_text_pairs_json
188
+
189
+ pairs = load_text_pairs_json("dataset.json")
190
+ ```
191
+
192
+ Também aceita:
193
+
194
+ ```json
195
+ {"data": [{"prompt": "...", "answer": "..."}]}
196
+ ```
197
+
198
+ ou:
199
+
200
+ ```json
201
+ {"examples": [{"prompt": "...", "answer": "..."}]}
202
+ ```
203
+
204
+ ### JSONL
205
+
206
+ Arquivo `dataset.jsonl`:
207
+
208
+ ```jsonl
209
+ {"prompt": "Quem é você?", "answer": "Oxta"}
210
+ {"prompt": "Qual seu nome?", "answer": "Oxta"}
211
+ ```
212
+
213
+ Uso:
214
+
215
+ ```python
216
+ from nsos_mamba import load_text_pairs_jsonl
217
+
218
+ pairs = load_text_pairs_jsonl("dataset.jsonl")
219
+ ```
220
+
221
+ ## Observações
222
+
223
+ - `CharTokenizer` é para testes, demos e dados sintéticos pequenos.
224
+ - Para treino real, use um tokenizer próprio do projeto.
225
+ - O pacote expõe o Mamba isolado; não ativa Attention, MoE, KAN, TTT, CHRASS,
226
+ MCTS ou memória externa.
227
+ - O pacote é uma camada de uso sobre `nsos_ext`, não uma reimplementação em
228
+ Python.
@@ -0,0 +1,207 @@
1
+ # NSOS Mamba
2
+
3
+ `nsos-mamba` é um wrapper Python simples para usar apenas o módulo Mamba do
4
+ NSOS. Ele configura automaticamente o modelo em modo Mamba-2 faithful, ativa
5
+ streaming incremental por padrão e oferece utilitários pequenos para treinar
6
+ pares `prompt -> resposta`.
7
+
8
+ Este pacote não instala o runtime nativo `nsos_ext`. Antes de usar, compile ou
9
+ copie o `nsos_ext.so`/`nsos_ext.pyd` e deixe-o acessível via `PYTHONPATH` ou
10
+ pela variável `NSOS_EXT_PATH`.
11
+
12
+ ## Instalação
13
+
14
+ ```bash
15
+ pip install nsos-mamba
16
+ ```
17
+
18
+ Se o `nsos_ext` estiver em uma pasta separada:
19
+
20
+ ```bash
21
+ export NSOS_EXT_PATH=/caminho/para/nsos_ext
22
+ ```
23
+
24
+ No Colab:
25
+
26
+ ```python
27
+ import sys, os
28
+
29
+ sys.path.insert(0, "/content/nsos_ext_mambavs")
30
+ # ou:
31
+ os.environ["NSOS_EXT_PATH"] = "/content/nsos_ext_mambavs"
32
+ ```
33
+
34
+ ## Configurações principais
35
+
36
+ ```python
37
+ from nsos_mamba import MambaModuleConfig
38
+
39
+ cfg = MambaModuleConfig(
40
+ vocab_size=128,
41
+ num_layers=12,
42
+ d_model=128,
43
+ device="auto",
44
+ streaming=True,
45
+ )
46
+ ```
47
+
48
+ Campos mais usados:
49
+
50
+ - `vocab_size`: tamanho do vocabulário.
51
+ - `num_layers`: número de camadas Mamba.
52
+ - `d_model`: largura do modelo.
53
+ - `d_state`: tamanho do estado SSM. Padrão: `64`.
54
+ - `max_context_tokens`: limite de contexto. Padrão: `4096`.
55
+ - `device`: `"auto"`, `"gpu"`, `"cuda"` ou `"cpu"`.
56
+ - `streaming`: ativa decode incremental. Padrão: `True`.
57
+ - `allow_cpu_fallback`: se a GPU falhar, cai para CPU. Padrão: `True`.
58
+
59
+ Variáveis de ambiente equivalentes:
60
+
61
+ ```bash
62
+ NSOS_MAMBA_DEVICE=auto
63
+ NSOS_MAMBA_STREAMING=1
64
+ NSOS_MAMBA_LAYERS=12
65
+ NSOS_MAMBA_DMODEL=128
66
+ NSOS_MAMBA_DSTATE=64
67
+ NSOS_MAMBA_CONTEXT=4096
68
+ ```
69
+
70
+ ## Exemplo mínimo: "Quem é você?" -> "Oxta"
71
+
72
+ ```python
73
+ from nsos_mamba import CharTokenizer, MambaModuleConfig, NSOSMamba, TextPair
74
+
75
+ pairs = [
76
+ TextPair("Quem é você?", "Oxta"),
77
+ TextPair("quem é você?", "Oxta"),
78
+ TextPair("Quem e voce?", "Oxta"),
79
+ ]
80
+
81
+ tok = CharTokenizer.from_texts(
82
+ [p.prompt for p in pairs] + [p.answer for p in pairs]
83
+ )
84
+
85
+ cfg = MambaModuleConfig(
86
+ vocab_size=tok.vocab_size,
87
+ num_layers=12,
88
+ d_model=128,
89
+ device="auto",
90
+ streaming=True,
91
+ )
92
+
93
+ mamba = NSOSMamba(cfg)
94
+
95
+ mamba.fit_text_pairs(
96
+ pairs,
97
+ tok,
98
+ steps=1200,
99
+ batch_size=16,
100
+ learning_rate=3e-3,
101
+ callback=lambda step, loss: print(f"[train] step {step} loss~{loss:.4f}"),
102
+ )
103
+
104
+ result = mamba.generate_text("Quem é você?", tok, max_new_tokens=8)
105
+
106
+ print("resposta:", repr(result.text))
107
+ print("tokens/s:", result.decode_tokens_per_sec)
108
+ ```
109
+
110
+ Saída esperada:
111
+
112
+ ```text
113
+ resposta: 'Oxta'
114
+ ```
115
+
116
+ ## Streaming incremental
117
+
118
+ O streaming incremental é o padrão. O fluxo usado é:
119
+
120
+ 1. processa o prompt uma vez (`prefill`);
121
+ 2. gera cada token novo passando apenas o último token;
122
+ 3. reaproveita o estado interno do modelo.
123
+
124
+ Benchmark:
125
+
126
+ ```python
127
+ prompt_ids = tok.encode("Quem é você?", bos=True)
128
+ bench = mamba.benchmark_decode(prompt_ids, max_new_tokens=1024, repeats=5)
129
+ bench.print()
130
+ ```
131
+
132
+ Métricas:
133
+
134
+ - `prefill_ms`: tempo para processar o prompt.
135
+ - `first_incremental_ms`: primeiro passo incremental após o prefill.
136
+ - `decode_tokens_per_sec`: tokens/s do decode incremental.
137
+ - `end_to_end_tokens_per_sec`: tokens/s incluindo prefill.
138
+
139
+ ## Datasets
140
+
141
+ ### Lista em memória
142
+
143
+ ```python
144
+ from nsos_mamba import TextPair
145
+
146
+ pairs = [
147
+ TextPair(prompt="Quem é você?", answer="Oxta"),
148
+ TextPair(prompt="Qual seu nome?", answer="Oxta"),
149
+ ]
150
+ ```
151
+
152
+ ### JSON
153
+
154
+ Arquivo `dataset.json`:
155
+
156
+ ```json
157
+ [
158
+ {"prompt": "Quem é você?", "answer": "Oxta"},
159
+ {"prompt": "Qual seu nome?", "answer": "Oxta"}
160
+ ]
161
+ ```
162
+
163
+ Uso:
164
+
165
+ ```python
166
+ from nsos_mamba import load_text_pairs_json
167
+
168
+ pairs = load_text_pairs_json("dataset.json")
169
+ ```
170
+
171
+ Também aceita:
172
+
173
+ ```json
174
+ {"data": [{"prompt": "...", "answer": "..."}]}
175
+ ```
176
+
177
+ ou:
178
+
179
+ ```json
180
+ {"examples": [{"prompt": "...", "answer": "..."}]}
181
+ ```
182
+
183
+ ### JSONL
184
+
185
+ Arquivo `dataset.jsonl`:
186
+
187
+ ```jsonl
188
+ {"prompt": "Quem é você?", "answer": "Oxta"}
189
+ {"prompt": "Qual seu nome?", "answer": "Oxta"}
190
+ ```
191
+
192
+ Uso:
193
+
194
+ ```python
195
+ from nsos_mamba import load_text_pairs_jsonl
196
+
197
+ pairs = load_text_pairs_jsonl("dataset.jsonl")
198
+ ```
199
+
200
+ ## Observações
201
+
202
+ - `CharTokenizer` é para testes, demos e dados sintéticos pequenos.
203
+ - Para treino real, use um tokenizer próprio do projeto.
204
+ - O pacote expõe o Mamba isolado; não ativa Attention, MoE, KAN, TTT, CHRASS,
205
+ MCTS ou memória externa.
206
+ - O pacote é uma camada de uso sobre `nsos_ext`, não uma reimplementação em
207
+ Python.
@@ -0,0 +1,37 @@
1
+ """Standalone NSOS Mamba Python wrapper.
2
+
3
+ This package is intentionally thin: it does not implement a new architecture.
4
+ It builds a pure faithful-Mamba NSOS ``JambaModel`` on top of ``nsos_ext`` and
5
+ standardizes device selection, streaming incremental decoding and small
6
+ supervised datasets.
7
+ """
8
+
9
+ from .core import (
10
+ DecodeBenchmark,
11
+ GenerationResult,
12
+ MambaModuleConfig,
13
+ NSOSMamba,
14
+ detect_device,
15
+ )
16
+ from .datasets import (
17
+ CharTokenizer,
18
+ TextPair,
19
+ load_text_pairs,
20
+ load_text_pairs_json,
21
+ load_text_pairs_jsonl,
22
+ pairs_to_token_ids,
23
+ )
24
+
25
+ __all__ = [
26
+ "CharTokenizer",
27
+ "DecodeBenchmark",
28
+ "GenerationResult",
29
+ "MambaModuleConfig",
30
+ "NSOSMamba",
31
+ "TextPair",
32
+ "detect_device",
33
+ "load_text_pairs",
34
+ "load_text_pairs_json",
35
+ "load_text_pairs_jsonl",
36
+ "pairs_to_token_ids",
37
+ ]
@@ -0,0 +1,465 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import random
5
+ import time
6
+ from dataclasses import dataclass, field
7
+ from typing import Callable, Iterable, List, Optional, Sequence, Tuple
8
+
9
+ import numpy as np
10
+
11
+ from .datasets import CharTokenizer, TextPair, pairs_to_token_ids
12
+
13
+
14
+ def _load_nsos_ext():
15
+ ext_path = os.environ.get("NSOS_EXT_PATH")
16
+ if ext_path:
17
+ import sys
18
+
19
+ sys.path.insert(0, ext_path)
20
+ try:
21
+ import nsos_ext as nsos # type: ignore
22
+ except ImportError as exc:
23
+ raise ImportError(
24
+ "nsos_ext is not importable. Add the built nsos_ext directory to "
25
+ "PYTHONPATH/sys.path before importing nsos_mamba."
26
+ ) from exc
27
+ return nsos
28
+
29
+
30
+ def _torch_cuda_available() -> bool:
31
+ try:
32
+ import torch # type: ignore
33
+
34
+ return bool(torch.cuda.is_available())
35
+ except Exception:
36
+ return False
37
+
38
+
39
+ def detect_device(nsos_ext=None, requested: Optional[str] = None):
40
+ """Resolve CPU/GPU from a flag or ``NSOS_MAMBA_DEVICE``.
41
+
42
+ Accepted values: ``auto`` (default), ``gpu``/``cuda`` and ``cpu``.
43
+ ``auto`` selects GPU only when PyTorch can see CUDA; construction still has
44
+ a CPU fallback in ``NSOSMamba`` in case the native extension cannot allocate
45
+ on the selected device.
46
+ """
47
+
48
+ nsos = nsos_ext or _load_nsos_ext()
49
+ mode = (requested or os.environ.get("NSOS_MAMBA_DEVICE", "auto")).strip().lower()
50
+ if mode in ("cpu", "host"):
51
+ return nsos.Device.CPU
52
+ if mode in ("gpu", "cuda"):
53
+ return nsos.Device.GPU
54
+ if mode not in ("", "auto"):
55
+ raise ValueError("device must be one of: auto, gpu, cuda, cpu")
56
+ return nsos.Device.GPU if _torch_cuda_available() else nsos.Device.CPU
57
+
58
+
59
+ def _env_bool(name: str, default: bool) -> bool:
60
+ value = os.environ.get(name)
61
+ if value is None:
62
+ return default
63
+ return value.strip().lower() not in ("0", "false", "no", "off")
64
+
65
+
66
+ def _safe_set(obj, name: str, value) -> None:
67
+ try:
68
+ setattr(obj, name, value)
69
+ except Exception:
70
+ pass
71
+
72
+
73
+ @dataclass
74
+ class MambaModuleConfig:
75
+ """Configuration for the standalone faithful-Mamba wrapper."""
76
+
77
+ vocab_size: int
78
+ num_layers: int = 12
79
+ d_model: int = 128
80
+ d_state: int = 64
81
+ max_context_tokens: int = 4096
82
+ n_heads: int = 4
83
+ n_kv_heads: int = 2
84
+ mamba_expand: int = 2
85
+ mamba_head_dim: int = 64
86
+ mamba_n_groups: int = 1
87
+ tie_word_embeddings: bool = False
88
+ seed: Optional[int] = 42
89
+ device: str = "auto"
90
+ streaming: bool = True
91
+ allow_cpu_fallback: bool = True
92
+
93
+ @classmethod
94
+ def from_env(cls, vocab_size: int) -> "MambaModuleConfig":
95
+ return cls(
96
+ vocab_size=vocab_size,
97
+ num_layers=int(os.environ.get("NSOS_MAMBA_LAYERS", "12")),
98
+ d_model=int(os.environ.get("NSOS_MAMBA_DMODEL", "128")),
99
+ d_state=int(os.environ.get("NSOS_MAMBA_DSTATE", "64")),
100
+ max_context_tokens=int(os.environ.get("NSOS_MAMBA_CONTEXT", "4096")),
101
+ device=os.environ.get("NSOS_MAMBA_DEVICE", "auto"),
102
+ streaming=_env_bool("NSOS_MAMBA_STREAMING", True),
103
+ )
104
+
105
+
106
+ @dataclass
107
+ class GenerationResult:
108
+ prompt_ids: List[int]
109
+ generated_ids: List[int]
110
+ text: Optional[str]
111
+ prefill_ms: float
112
+ decode_ms: float
113
+ ttft_ms: float
114
+ first_incremental_ms: float
115
+ generated_tokens: int
116
+ decode_tokens_per_sec: float
117
+ end_to_end_tokens_per_sec: float
118
+ streaming: bool
119
+
120
+
121
+ @dataclass
122
+ class DecodeBenchmark:
123
+ runs: List[GenerationResult] = field(default_factory=list)
124
+
125
+ @property
126
+ def avg_decode_tokens_per_sec(self) -> float:
127
+ return float(np.mean([r.decode_tokens_per_sec for r in self.runs])) if self.runs else 0.0
128
+
129
+ @property
130
+ def avg_end_to_end_tokens_per_sec(self) -> float:
131
+ return float(np.mean([r.end_to_end_tokens_per_sec for r in self.runs])) if self.runs else 0.0
132
+
133
+ @property
134
+ def avg_prefill_ms(self) -> float:
135
+ return float(np.mean([r.prefill_ms for r in self.runs])) if self.runs else 0.0
136
+
137
+ @property
138
+ def avg_first_incremental_ms(self) -> float:
139
+ return float(np.mean([r.first_incremental_ms for r in self.runs])) if self.runs else 0.0
140
+
141
+ def print(self) -> None:
142
+ print("\n========== NSOS MAMBA DECODE BENCH ==========")
143
+ for idx, run in enumerate(self.runs):
144
+ print(
145
+ f"run {idx}: {run.generated_tokens} tok | "
146
+ f"decode={run.decode_ms / 1000.0:.4f}s | "
147
+ f"decode_tok/s={run.decode_tokens_per_sec:.2f} | "
148
+ f"e2e_tok/s={run.end_to_end_tokens_per_sec:.2f} | "
149
+ f"prefill={run.prefill_ms:.2f} ms | "
150
+ f"first_incremental={run.first_incremental_ms:.2f} ms"
151
+ )
152
+ print("---------------------------------------------")
153
+ print("avg decode tok/s:", self.avg_decode_tokens_per_sec)
154
+ print("avg e2e tok/s:", self.avg_end_to_end_tokens_per_sec)
155
+ print("avg prefill ms:", self.avg_prefill_ms)
156
+ print("avg first incremental ms:", self.avg_first_incremental_ms)
157
+
158
+
159
+ class NSOSMamba:
160
+ """Pure faithful-Mamba NSOS module with streaming incremental by default."""
161
+
162
+ def __init__(self, config: MambaModuleConfig, nsos_ext=None):
163
+ self.nsos = nsos_ext or _load_nsos_ext()
164
+ self.config = config
165
+ if config.seed is not None:
166
+ self.nsos.set_seed(int(config.seed))
167
+
168
+ requested_device = detect_device(self.nsos, config.device)
169
+ self.device = requested_device
170
+ try:
171
+ self.model = self.nsos.JambaModel(self._make_native_config(), requested_device)
172
+ self.model.to(requested_device)
173
+ except Exception:
174
+ if not config.allow_cpu_fallback or requested_device == self.nsos.Device.CPU:
175
+ raise
176
+ self.device = self.nsos.Device.CPU
177
+ self.model = self.nsos.JambaModel(self._make_native_config(), self.device)
178
+ self.model.to(self.device)
179
+
180
+ self.set_streaming(config.streaming)
181
+
182
+ def _make_native_config(self):
183
+ cfg = self.nsos.ModelConfig()
184
+ cfg.num_layers = int(self.config.num_layers)
185
+ cfg.d_model = int(self.config.d_model)
186
+ cfg.vocab_size = int(self.config.vocab_size)
187
+ cfg.n_heads = int(self.config.n_heads)
188
+ cfg.n_kv_heads = int(self.config.n_kv_heads)
189
+ cfg.max_context_tokens = int(self.config.max_context_tokens)
190
+
191
+ cfg.use_moe = False
192
+ cfg.use_kan = False
193
+ cfg.use_ttt = False
194
+ cfg.dropout = 0.0
195
+
196
+ cfg.mamba2_faithful = True
197
+ cfg.mamba_expand = int(self.config.mamba_expand)
198
+ cfg.mamba_head_dim = int(self.config.mamba_head_dim)
199
+ cfg.tie_word_embeddings = bool(self.config.tie_word_embeddings)
200
+
201
+ _safe_set(cfg, "mamba_state_expansion", int(self.config.d_state))
202
+ _safe_set(cfg, "mamba_n_groups", int(self.config.mamba_n_groups))
203
+
204
+ # Disable attention by putting the slot outside any real layer schedule.
205
+ cfg.attention_period = 1_000_000
206
+ cfg.attention_slot = 0
207
+ return cfg
208
+
209
+ @property
210
+ def vocab_size(self) -> int:
211
+ return int(self.config.vocab_size)
212
+
213
+ def set_streaming(self, enabled: bool = True) -> bool:
214
+ try:
215
+ if enabled and not self.model.supports_streaming_inference():
216
+ self.model.set_streaming_inference(False)
217
+ return False
218
+ self.model.set_streaming_inference(bool(enabled))
219
+ return bool(enabled)
220
+ except Exception:
221
+ return False
222
+
223
+ def _logits_numpy(self, tensor) -> np.ndarray:
224
+ return np.asarray(tensor.cpu().numpy()).reshape(-1, self.vocab_size)
225
+
226
+ def fit_token_pairs(
227
+ self,
228
+ pairs: Sequence[Tuple[Sequence[int], Sequence[int]]],
229
+ *,
230
+ steps: int = 1200,
231
+ batch_size: int = 16,
232
+ learning_rate: float = 3e-3,
233
+ qat: bool = False,
234
+ seed: int = 123,
235
+ progress_every: Optional[int] = None,
236
+ callback: Optional[Callable[[int, float], None]] = None,
237
+ ) -> List[float]:
238
+ if not pairs:
239
+ raise ValueError("fit_token_pairs requires at least one pair")
240
+
241
+ trainer = self.nsos.Trainer(self.model, float(learning_rate))
242
+ trainer.warmup_steps = max(1, min(50, steps // 10 if steps > 10 else 1))
243
+ trainer.total_training_steps = int(steps)
244
+ trainer.first_token_loss_scale = 1.0
245
+ trainer.eos_loss_scale = 1.0
246
+ trainer.phase_scheduler.progressive_qat_enabled = bool(qat)
247
+
248
+ rng = random.Random(seed)
249
+ self.model.set_training_mode(True)
250
+ self.set_streaming(False)
251
+
252
+ losses: List[float] = []
253
+ window = progress_every or max(1, steps // 6)
254
+ running = 0.0
255
+ for step in range(int(steps)):
256
+ prompts: List[List[int]] = []
257
+ answers: List[List[int]] = []
258
+ for _ in range(int(batch_size)):
259
+ prompt, answer = rng.choice(pairs)
260
+ prompts.append(list(prompt))
261
+ answers.append(list(answer))
262
+ loss = float(trainer.train_supervised_batch(prompts, answers))
263
+ losses.append(loss)
264
+ running += loss
265
+ if callback and (step + 1) % window == 0:
266
+ callback(step + 1, running / float(window))
267
+ running = 0.0
268
+
269
+ self.model.set_training_mode(False)
270
+ self.set_streaming(self.config.streaming)
271
+ return losses
272
+
273
+ def fit_text_pairs(
274
+ self,
275
+ pairs: Sequence[TextPair],
276
+ tokenizer: CharTokenizer,
277
+ **kwargs,
278
+ ) -> List[float]:
279
+ token_pairs = pairs_to_token_ids(pairs, tokenizer)
280
+ return self.fit_token_pairs(token_pairs, **kwargs)
281
+
282
+ def generate_ids(
283
+ self,
284
+ prompt_ids: Sequence[int],
285
+ *,
286
+ max_new_tokens: int = 128,
287
+ eos_token_id: Optional[int] = None,
288
+ stop_on_eos: bool = True,
289
+ streaming: Optional[bool] = None,
290
+ tokenizer: Optional[CharTokenizer] = None,
291
+ ) -> GenerationResult:
292
+ if max_new_tokens <= 0:
293
+ return GenerationResult(
294
+ prompt_ids=list(prompt_ids),
295
+ generated_ids=[],
296
+ text="",
297
+ prefill_ms=0.0,
298
+ decode_ms=0.0,
299
+ ttft_ms=0.0,
300
+ first_incremental_ms=0.0,
301
+ generated_tokens=0,
302
+ decode_tokens_per_sec=0.0,
303
+ end_to_end_tokens_per_sec=0.0,
304
+ streaming=bool(streaming),
305
+ )
306
+
307
+ use_streaming = self.config.streaming if streaming is None else bool(streaming)
308
+ self.model.set_training_mode(False)
309
+ self.set_streaming(use_streaming)
310
+
311
+ if use_streaming:
312
+ return self._generate_ids_streaming(
313
+ prompt_ids,
314
+ max_new_tokens=max_new_tokens,
315
+ eos_token_id=eos_token_id,
316
+ stop_on_eos=stop_on_eos,
317
+ tokenizer=tokenizer,
318
+ )
319
+ return self._generate_ids_eager(
320
+ prompt_ids,
321
+ max_new_tokens=max_new_tokens,
322
+ eos_token_id=eos_token_id,
323
+ stop_on_eos=stop_on_eos,
324
+ tokenizer=tokenizer,
325
+ )
326
+
327
+ def _generate_ids_streaming(
328
+ self,
329
+ prompt_ids: Sequence[int],
330
+ *,
331
+ max_new_tokens: int,
332
+ eos_token_id: Optional[int],
333
+ stop_on_eos: bool,
334
+ tokenizer: Optional[CharTokenizer],
335
+ ) -> GenerationResult:
336
+ self.model.reset_session()
337
+ prompt = list(prompt_ids)
338
+
339
+ prefill_t0 = time.perf_counter()
340
+ logits = self._logits_numpy(self.model.forward_ids(prompt))
341
+ prefill_s = time.perf_counter() - prefill_t0
342
+
343
+ next_id = int(np.argmax(logits[-1]))
344
+ generated = [next_id]
345
+ if stop_on_eos and eos_token_id is not None and next_id == eos_token_id:
346
+ text = tokenizer.decode(generated) if tokenizer else None
347
+ return self._result(prompt, generated, text, prefill_s, 0.0, 0.0, True)
348
+
349
+ decode_t0 = time.perf_counter()
350
+ first_incremental_s = 0.0
351
+ for idx in range(1, max_new_tokens):
352
+ step_t0 = time.perf_counter()
353
+ logits = self._logits_numpy(self.model.forward_ids([next_id]))
354
+ if idx == 1:
355
+ first_incremental_s = time.perf_counter() - step_t0
356
+ next_id = int(np.argmax(logits[-1]))
357
+ generated.append(next_id)
358
+ if stop_on_eos and eos_token_id is not None and next_id == eos_token_id:
359
+ break
360
+ decode_s = time.perf_counter() - decode_t0
361
+ text = tokenizer.decode(generated) if tokenizer else None
362
+ return self._result(prompt, generated, text, prefill_s, decode_s, first_incremental_s, True)
363
+
364
+ def _generate_ids_eager(
365
+ self,
366
+ prompt_ids: Sequence[int],
367
+ *,
368
+ max_new_tokens: int,
369
+ eos_token_id: Optional[int],
370
+ stop_on_eos: bool,
371
+ tokenizer: Optional[CharTokenizer],
372
+ ) -> GenerationResult:
373
+ prompt = list(prompt_ids)
374
+ seq = list(prompt)
375
+ generated: List[int] = []
376
+ t0 = time.perf_counter()
377
+ first_s = 0.0
378
+ for idx in range(max_new_tokens):
379
+ step_t0 = time.perf_counter()
380
+ logits = self._logits_numpy(self.model.forward_ids(seq))
381
+ if idx == 0:
382
+ first_s = time.perf_counter() - step_t0
383
+ next_id = int(np.argmax(logits[-1]))
384
+ generated.append(next_id)
385
+ seq.append(next_id)
386
+ if stop_on_eos and eos_token_id is not None and next_id == eos_token_id:
387
+ break
388
+ total_s = time.perf_counter() - t0
389
+ text = tokenizer.decode(generated) if tokenizer else None
390
+ return self._result(prompt, generated, text, first_s, max(total_s - first_s, 0.0), first_s, False)
391
+
392
+ def _result(
393
+ self,
394
+ prompt: List[int],
395
+ generated: List[int],
396
+ text: Optional[str],
397
+ prefill_s: float,
398
+ decode_s: float,
399
+ first_incremental_s: float,
400
+ streaming: bool,
401
+ ) -> GenerationResult:
402
+ # In streaming mode the first generated token comes from prefill; steady
403
+ # decode throughput counts only tokens produced after prefill.
404
+ steady_tokens = max(len(generated) - 1, 0) if streaming else len(generated)
405
+ decode_tps = steady_tokens / max(decode_s, 1e-9)
406
+ e2e_tps = len(generated) / max(prefill_s + decode_s, 1e-9)
407
+ return GenerationResult(
408
+ prompt_ids=prompt,
409
+ generated_ids=generated,
410
+ text=text,
411
+ prefill_ms=prefill_s * 1000.0,
412
+ decode_ms=decode_s * 1000.0,
413
+ ttft_ms=prefill_s * 1000.0,
414
+ first_incremental_ms=first_incremental_s * 1000.0,
415
+ generated_tokens=len(generated),
416
+ decode_tokens_per_sec=decode_tps,
417
+ end_to_end_tokens_per_sec=e2e_tps,
418
+ streaming=streaming,
419
+ )
420
+
421
+ def generate_text(
422
+ self,
423
+ prompt: str,
424
+ tokenizer: CharTokenizer,
425
+ *,
426
+ max_new_tokens: int = 128,
427
+ stop_on_eos: bool = True,
428
+ ) -> GenerationResult:
429
+ prompt_ids = tokenizer.encode(prompt, bos=True, eos=False)
430
+ return self.generate_ids(
431
+ prompt_ids,
432
+ max_new_tokens=max_new_tokens,
433
+ eos_token_id=tokenizer.eos_id,
434
+ stop_on_eos=stop_on_eos,
435
+ tokenizer=tokenizer,
436
+ )
437
+
438
+ def benchmark_decode(
439
+ self,
440
+ prompt_ids: Sequence[int],
441
+ *,
442
+ max_new_tokens: int = 256,
443
+ repeats: int = 5,
444
+ warmup_tokens: int = 8,
445
+ eos_token_id: Optional[int] = None,
446
+ ) -> DecodeBenchmark:
447
+ # Warmup: one short streaming run, no EOS stop.
448
+ self.generate_ids(
449
+ prompt_ids,
450
+ max_new_tokens=max(1, warmup_tokens),
451
+ eos_token_id=eos_token_id,
452
+ stop_on_eos=False,
453
+ streaming=True,
454
+ )
455
+ runs = [
456
+ self.generate_ids(
457
+ prompt_ids,
458
+ max_new_tokens=max_new_tokens,
459
+ eos_token_id=eos_token_id,
460
+ stop_on_eos=False,
461
+ streaming=True,
462
+ )
463
+ for _ in range(repeats)
464
+ ]
465
+ return DecodeBenchmark(runs)
@@ -0,0 +1,161 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Iterable, List, Mapping, Sequence, Tuple
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class TextPair:
11
+ """One supervised prompt -> answer example."""
12
+
13
+ prompt: str
14
+ answer: str
15
+
16
+
17
+ class CharTokenizer:
18
+ """Small deterministic char-level tokenizer for demos and probes.
19
+
20
+ It is deliberately simple. Production training should use the project's
21
+ tokenizer pack, but this class is enough for synthetic tasks such as:
22
+ "Quem é você?" -> "Oxta".
23
+ """
24
+
25
+ pad_token = "<PAD>"
26
+ bos_token = "<BOS>"
27
+ eos_token = "<EOS>"
28
+
29
+ def __init__(self, tokens: Sequence[str]):
30
+ unique = list(dict.fromkeys(tokens))
31
+ required = [self.pad_token, self.bos_token, self.eos_token]
32
+ merged = required + [t for t in unique if t not in required]
33
+ self.tokens: List[str] = merged
34
+ self.stoi = {tok: idx for idx, tok in enumerate(self.tokens)}
35
+ self.itos = {idx: tok for tok, idx in self.stoi.items()}
36
+
37
+ @classmethod
38
+ def from_texts(cls, texts: Iterable[str]) -> "CharTokenizer":
39
+ chars = sorted({ch for text in texts for ch in text})
40
+ return cls([cls.pad_token, cls.bos_token, cls.eos_token, *chars])
41
+
42
+ @property
43
+ def pad_id(self) -> int:
44
+ return self.stoi[self.pad_token]
45
+
46
+ @property
47
+ def bos_id(self) -> int:
48
+ return self.stoi[self.bos_token]
49
+
50
+ @property
51
+ def eos_id(self) -> int:
52
+ return self.stoi[self.eos_token]
53
+
54
+ @property
55
+ def vocab_size(self) -> int:
56
+ return len(self.tokens)
57
+
58
+ def encode(self, text: str, *, bos: bool = False, eos: bool = False) -> List[int]:
59
+ ids: List[int] = []
60
+ if bos:
61
+ ids.append(self.bos_id)
62
+ for ch in text:
63
+ if ch not in self.stoi:
64
+ raise KeyError(f"character not in tokenizer vocabulary: {ch!r}")
65
+ ids.append(self.stoi[ch])
66
+ if eos:
67
+ ids.append(self.eos_id)
68
+ return ids
69
+
70
+ def decode(self, ids: Iterable[int], *, stop_at_eos: bool = True) -> str:
71
+ out: List[str] = []
72
+ for token_id in ids:
73
+ token_id = int(token_id)
74
+ if token_id == self.eos_id and stop_at_eos:
75
+ break
76
+ if token_id in (self.pad_id, self.bos_id, self.eos_id):
77
+ continue
78
+ out.append(self.itos.get(token_id, ""))
79
+ return "".join(out)
80
+
81
+
82
+ def load_text_pairs(pairs: Iterable[Tuple[str, str] | Mapping[str, str] | TextPair]) -> List[TextPair]:
83
+ """Normalize in-memory examples into ``TextPair`` objects."""
84
+
85
+ normalized: List[TextPair] = []
86
+ for item in pairs:
87
+ if isinstance(item, TextPair):
88
+ normalized.append(item)
89
+ elif isinstance(item, Mapping):
90
+ normalized.append(TextPair(str(item["prompt"]), str(item["answer"])))
91
+ else:
92
+ prompt, answer = item
93
+ normalized.append(TextPair(str(prompt), str(answer)))
94
+ return normalized
95
+
96
+
97
+ def load_text_pairs_json(
98
+ path: str | Path,
99
+ *,
100
+ prompt_key: str = "prompt",
101
+ answer_key: str = "answer",
102
+ ) -> List[TextPair]:
103
+ """Load examples from a JSON file.
104
+
105
+ Accepted shapes:
106
+
107
+ - ``[{"prompt": "...", "answer": "..."}]``
108
+ - ``{"data": [{"prompt": "...", "answer": "..."}]}``
109
+ - ``{"examples": [{"prompt": "...", "answer": "..."}]}``
110
+ """
111
+
112
+ raw = json.loads(Path(path).read_text(encoding="utf-8"))
113
+ if isinstance(raw, Mapping):
114
+ if "data" in raw:
115
+ raw = raw["data"]
116
+ elif "examples" in raw:
117
+ raw = raw["examples"]
118
+ if not isinstance(raw, list):
119
+ raise ValueError("JSON dataset must be a list or an object with data/examples")
120
+ return [
121
+ TextPair(prompt=str(row[prompt_key]), answer=str(row[answer_key]))
122
+ for row in raw
123
+ ]
124
+
125
+
126
+ def load_text_pairs_jsonl(
127
+ path: str | Path,
128
+ *,
129
+ prompt_key: str = "prompt",
130
+ answer_key: str = "answer",
131
+ ) -> List[TextPair]:
132
+ """Load one ``{"prompt": "...", "answer": "..."}`` object per line."""
133
+
134
+ examples: List[TextPair] = []
135
+ for line_no, line in enumerate(Path(path).read_text(encoding="utf-8").splitlines(), 1):
136
+ stripped = line.strip()
137
+ if not stripped:
138
+ continue
139
+ row = json.loads(stripped)
140
+ if not isinstance(row, Mapping):
141
+ raise ValueError(f"JSONL line {line_no} is not an object")
142
+ examples.append(TextPair(prompt=str(row[prompt_key]), answer=str(row[answer_key])))
143
+ return examples
144
+
145
+
146
+ def pairs_to_token_ids(
147
+ pairs: Sequence[TextPair],
148
+ tokenizer: CharTokenizer,
149
+ *,
150
+ add_bos: bool = True,
151
+ add_eos: bool = True,
152
+ ) -> List[Tuple[List[int], List[int]]]:
153
+ """Convert text pairs to NSOS supervised prompt/answer token lists."""
154
+
155
+ return [
156
+ (
157
+ tokenizer.encode(pair.prompt, bos=add_bos, eos=False),
158
+ tokenizer.encode(pair.answer, bos=False, eos=add_eos),
159
+ )
160
+ for pair in pairs
161
+ ]
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: nsos-mamba
3
+ Version: 0.1.0
4
+ Summary: Wrapper Python isolado para o módulo Mamba do NSOS com streaming incremental padrão.
5
+ Author: Oxta
6
+ Project-URL: Homepage, https://github.com/vitorGgC569/reimagined
7
+ Project-URL: Documentation, https://github.com/vitorGgC569/reimagined/tree/nsos-gpu-phases12/OXN/nsos/docs
8
+ Project-URL: Source, https://github.com/vitorGgC569/reimagined
9
+ Keywords: mamba,ssm,nsos,edge-ai,streaming
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: numpy>=1.23
21
+
22
+ # NSOS Mamba
23
+
24
+ `nsos-mamba` é um wrapper Python simples para usar apenas o módulo Mamba do
25
+ NSOS. Ele configura automaticamente o modelo em modo Mamba-2 faithful, ativa
26
+ streaming incremental por padrão e oferece utilitários pequenos para treinar
27
+ pares `prompt -> resposta`.
28
+
29
+ Este pacote não instala o runtime nativo `nsos_ext`. Antes de usar, compile ou
30
+ copie o `nsos_ext.so`/`nsos_ext.pyd` e deixe-o acessível via `PYTHONPATH` ou
31
+ pela variável `NSOS_EXT_PATH`.
32
+
33
+ ## Instalação
34
+
35
+ ```bash
36
+ pip install nsos-mamba
37
+ ```
38
+
39
+ Se o `nsos_ext` estiver em uma pasta separada:
40
+
41
+ ```bash
42
+ export NSOS_EXT_PATH=/caminho/para/nsos_ext
43
+ ```
44
+
45
+ No Colab:
46
+
47
+ ```python
48
+ import sys, os
49
+
50
+ sys.path.insert(0, "/content/nsos_ext_mambavs")
51
+ # ou:
52
+ os.environ["NSOS_EXT_PATH"] = "/content/nsos_ext_mambavs"
53
+ ```
54
+
55
+ ## Configurações principais
56
+
57
+ ```python
58
+ from nsos_mamba import MambaModuleConfig
59
+
60
+ cfg = MambaModuleConfig(
61
+ vocab_size=128,
62
+ num_layers=12,
63
+ d_model=128,
64
+ device="auto",
65
+ streaming=True,
66
+ )
67
+ ```
68
+
69
+ Campos mais usados:
70
+
71
+ - `vocab_size`: tamanho do vocabulário.
72
+ - `num_layers`: número de camadas Mamba.
73
+ - `d_model`: largura do modelo.
74
+ - `d_state`: tamanho do estado SSM. Padrão: `64`.
75
+ - `max_context_tokens`: limite de contexto. Padrão: `4096`.
76
+ - `device`: `"auto"`, `"gpu"`, `"cuda"` ou `"cpu"`.
77
+ - `streaming`: ativa decode incremental. Padrão: `True`.
78
+ - `allow_cpu_fallback`: se a GPU falhar, cai para CPU. Padrão: `True`.
79
+
80
+ Variáveis de ambiente equivalentes:
81
+
82
+ ```bash
83
+ NSOS_MAMBA_DEVICE=auto
84
+ NSOS_MAMBA_STREAMING=1
85
+ NSOS_MAMBA_LAYERS=12
86
+ NSOS_MAMBA_DMODEL=128
87
+ NSOS_MAMBA_DSTATE=64
88
+ NSOS_MAMBA_CONTEXT=4096
89
+ ```
90
+
91
+ ## Exemplo mínimo: "Quem é você?" -> "Oxta"
92
+
93
+ ```python
94
+ from nsos_mamba import CharTokenizer, MambaModuleConfig, NSOSMamba, TextPair
95
+
96
+ pairs = [
97
+ TextPair("Quem é você?", "Oxta"),
98
+ TextPair("quem é você?", "Oxta"),
99
+ TextPair("Quem e voce?", "Oxta"),
100
+ ]
101
+
102
+ tok = CharTokenizer.from_texts(
103
+ [p.prompt for p in pairs] + [p.answer for p in pairs]
104
+ )
105
+
106
+ cfg = MambaModuleConfig(
107
+ vocab_size=tok.vocab_size,
108
+ num_layers=12,
109
+ d_model=128,
110
+ device="auto",
111
+ streaming=True,
112
+ )
113
+
114
+ mamba = NSOSMamba(cfg)
115
+
116
+ mamba.fit_text_pairs(
117
+ pairs,
118
+ tok,
119
+ steps=1200,
120
+ batch_size=16,
121
+ learning_rate=3e-3,
122
+ callback=lambda step, loss: print(f"[train] step {step} loss~{loss:.4f}"),
123
+ )
124
+
125
+ result = mamba.generate_text("Quem é você?", tok, max_new_tokens=8)
126
+
127
+ print("resposta:", repr(result.text))
128
+ print("tokens/s:", result.decode_tokens_per_sec)
129
+ ```
130
+
131
+ Saída esperada:
132
+
133
+ ```text
134
+ resposta: 'Oxta'
135
+ ```
136
+
137
+ ## Streaming incremental
138
+
139
+ O streaming incremental é o padrão. O fluxo usado é:
140
+
141
+ 1. processa o prompt uma vez (`prefill`);
142
+ 2. gera cada token novo passando apenas o último token;
143
+ 3. reaproveita o estado interno do modelo.
144
+
145
+ Benchmark:
146
+
147
+ ```python
148
+ prompt_ids = tok.encode("Quem é você?", bos=True)
149
+ bench = mamba.benchmark_decode(prompt_ids, max_new_tokens=1024, repeats=5)
150
+ bench.print()
151
+ ```
152
+
153
+ Métricas:
154
+
155
+ - `prefill_ms`: tempo para processar o prompt.
156
+ - `first_incremental_ms`: primeiro passo incremental após o prefill.
157
+ - `decode_tokens_per_sec`: tokens/s do decode incremental.
158
+ - `end_to_end_tokens_per_sec`: tokens/s incluindo prefill.
159
+
160
+ ## Datasets
161
+
162
+ ### Lista em memória
163
+
164
+ ```python
165
+ from nsos_mamba import TextPair
166
+
167
+ pairs = [
168
+ TextPair(prompt="Quem é você?", answer="Oxta"),
169
+ TextPair(prompt="Qual seu nome?", answer="Oxta"),
170
+ ]
171
+ ```
172
+
173
+ ### JSON
174
+
175
+ Arquivo `dataset.json`:
176
+
177
+ ```json
178
+ [
179
+ {"prompt": "Quem é você?", "answer": "Oxta"},
180
+ {"prompt": "Qual seu nome?", "answer": "Oxta"}
181
+ ]
182
+ ```
183
+
184
+ Uso:
185
+
186
+ ```python
187
+ from nsos_mamba import load_text_pairs_json
188
+
189
+ pairs = load_text_pairs_json("dataset.json")
190
+ ```
191
+
192
+ Também aceita:
193
+
194
+ ```json
195
+ {"data": [{"prompt": "...", "answer": "..."}]}
196
+ ```
197
+
198
+ ou:
199
+
200
+ ```json
201
+ {"examples": [{"prompt": "...", "answer": "..."}]}
202
+ ```
203
+
204
+ ### JSONL
205
+
206
+ Arquivo `dataset.jsonl`:
207
+
208
+ ```jsonl
209
+ {"prompt": "Quem é você?", "answer": "Oxta"}
210
+ {"prompt": "Qual seu nome?", "answer": "Oxta"}
211
+ ```
212
+
213
+ Uso:
214
+
215
+ ```python
216
+ from nsos_mamba import load_text_pairs_jsonl
217
+
218
+ pairs = load_text_pairs_jsonl("dataset.jsonl")
219
+ ```
220
+
221
+ ## Observações
222
+
223
+ - `CharTokenizer` é para testes, demos e dados sintéticos pequenos.
224
+ - Para treino real, use um tokenizer próprio do projeto.
225
+ - O pacote expõe o Mamba isolado; não ativa Attention, MoE, KAN, TTT, CHRASS,
226
+ MCTS ou memória externa.
227
+ - O pacote é uma camada de uso sobre `nsos_ext`, não uma reimplementação em
228
+ Python.
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ nsos_mamba/__init__.py
4
+ nsos_mamba/core.py
5
+ nsos_mamba/datasets.py
6
+ nsos_mamba.egg-info/PKG-INFO
7
+ nsos_mamba.egg-info/SOURCES.txt
8
+ nsos_mamba.egg-info/dependency_links.txt
9
+ nsos_mamba.egg-info/requires.txt
10
+ nsos_mamba.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ numpy>=1.23
@@ -0,0 +1 @@
1
+ nsos_mamba
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nsos-mamba"
7
+ version = "0.1.0"
8
+ description = "Wrapper Python isolado para o módulo Mamba do NSOS com streaming incremental padrão."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ authors = [
12
+ { name = "Oxta" }
13
+ ]
14
+ keywords = ["mamba", "ssm", "nsos", "edge-ai", "streaming"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Intended Audience :: Science/Research",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ ]
25
+ dependencies = [
26
+ "numpy>=1.23",
27
+ ]
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/vitorGgC569/reimagined"
31
+ Documentation = "https://github.com/vitorGgC569/reimagined/tree/nsos-gpu-phases12/OXN/nsos/docs"
32
+ Source = "https://github.com/vitorGgC569/reimagined"
33
+
34
+ [tool.setuptools]
35
+ packages = ["nsos_mamba"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+