chan4lk commited on
Commit
ba3c25f
·
verified ·
1 Parent(s): 14db3a2

Upload folder using huggingface_hub

Browse files
Files changed (7) hide show
  1. README.md +161 -0
  2. architecture.py +285 -0
  3. config.json +11 -0
  4. config_q4.json +15 -0
  5. model.safetensors +3 -0
  6. model_q4.safetensors +3 -0
  7. okr_tokenizer.model +3 -0
README.md ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ library_name: mlx
6
+ tags:
7
+ - mlx
8
+ - tool-use
9
+ - okr
10
+ - agent
11
+ - asms
12
+ - micro-model
13
+ - apple-silicon
14
+ pipeline_tag: text-generation
15
+ model-index:
16
+ - name: okr-micro-asms
17
+ results:
18
+ - task:
19
+ type: text-generation
20
+ name: OKR Tool Call Generation
21
+ metrics:
22
+ - type: accuracy
23
+ value: 80
24
+ name: Workflow Routing Accuracy
25
+ - type: accuracy
26
+ value: 50
27
+ name: Valid JSON Tool Call Rate
28
+ ---
29
+
30
+ # OKR Micro-Model (ASMS)
31
+
32
+ A **15M parameter** decoder-only transformer trained from scratch to handle OKR (Objectives and Key Results) management via tool calls to the [Keyflow MCP](https://keyflow.tecbizsolutions.com) API.
33
+
34
+ Built using **Agent-Specific Model Synthesis (ASMS)** — a pipeline that treats large LLMs as compilers, not runtimes. Instead of routing every OKR query through Claude or GPT-4, this micro-model handles workflow routing and tool-call generation locally on Apple Silicon.
35
+
36
+ ## Model Details
37
+
38
+ | Property | Value |
39
+ |----------|-------|
40
+ | Architecture | Decoder-only Transformer (GPT-style) |
41
+ | Parameters | 15M (13.5M after quantization overhead) |
42
+ | Layers | 6 |
43
+ | Hidden Dim | 384 |
44
+ | Attention Heads | 6 |
45
+ | FFN Dim | 768 |
46
+ | Max Sequence Length | 512 |
47
+ | Vocabulary | 6,000 tokens (task-specific BPE) |
48
+ | Framework | Apple MLX |
49
+ | Quantized Size | 10.1 MB (INT4) |
50
+ | FP16 Size | 26.9 MB |
51
+ | Training Data | 5,759 synthetic examples |
52
+ | Training Time | 58 minutes on M3 Pro |
53
+ | Training Cost | $0 (local hardware, in-session corpus generation) |
54
+
55
+ ## Performance
56
+
57
+ | Metric | Score |
58
+ |--------|-------|
59
+ | Workflow Routing | 8/10 (80%) |
60
+ | Valid JSON Tool Calls | 5/10 (50%) |
61
+ | Best Val Loss | 1.14 |
62
+ | Inference Latency | 100-250ms on M3 Pro |
63
+
64
+ The model correctly routes queries to 6 OKR workflows (`goal_to_okr`, `view_okrs`, `check_in`, `reports`, `onboard`, `align`) and generates valid, parseable Keyflow MCP tool calls:
65
+
66
+ ```json
67
+ {"tool": "objective", "action": "list", "params": {"cycleId": "cyc_q2_2026", "ownerId": "usr_107"}}
68
+ {"tool": "key_result", "action": "check_in", "params": {"keyResultId": "kr_102", "value": 1}}
69
+ {"tool": "report", "action": "health_check", "params": {"cycleId": "cyc_q2_2026"}}
70
+ ```
71
+
72
+ ## How to Use
73
+
74
+ ### With MLX (recommended)
75
+
76
+ ```python
77
+ import mlx.core as mx
78
+ import mlx.nn as nn
79
+ import sentencepiece as spm
80
+ import json
81
+
82
+ # Load model
83
+ from architecture import OKRModelConfig, create_model
84
+
85
+ with open("config.json") as f:
86
+ config_dict = json.load(f)
87
+ config = OKRModelConfig(**{k: v for k, v in config_dict.items() if k in OKRModelConfig.__dataclass_fields__})
88
+ model = create_model(config)
89
+ weights = mx.load("model.safetensors")
90
+ model.load_weights(list(weights.items()))
91
+ mx.eval(model.parameters())
92
+
93
+ # Load tokenizer
94
+ sp = spm.SentencePieceProcessor()
95
+ sp.Load("okr_tokenizer.model")
96
+
97
+ # Inference
98
+ query = "Show me my OKRs"
99
+ context = json.dumps({"userId": "usr_001", "activeCycleId": "cyc_q2_2026"})
100
+ text = f"QUERY: {query} CONTEXT: {context} "
101
+ tokens = mx.array([[sp.bos_id()] + sp.Encode(text)])
102
+ output = model.generate(tokens, max_new_tokens=256, temperature=0.0)
103
+ print(sp.Decode(output[0].tolist()))
104
+ ```
105
+
106
+ ### With the ASMS Server
107
+
108
+ ```bash
109
+ git clone https://github.com/chan4lk/timm
110
+ cd timm
111
+ uv sync
112
+ uv run deploy/server.py model/checkpoints/best
113
+ # Open http://localhost:8800 for the chat UI
114
+ ```
115
+
116
+ ## Files
117
+
118
+ | File | Description | Size |
119
+ |------|-------------|------|
120
+ | `model.safetensors` | FP16 model weights | 51 MB |
121
+ | `model_q4.safetensors` | INT4 quantized weights | 10 MB |
122
+ | `config.json` | Model architecture config (FP16) | 181 B |
123
+ | `config_q4.json` | Model architecture config (INT4) | 242 B |
124
+ | `okr_tokenizer.model` | SentencePiece BPE tokenizer (6K vocab) | 325 KB |
125
+ | `architecture.py` | MLX model definition | - |
126
+
127
+ ## Training
128
+
129
+ Trained using the ASMS (Agent-Specific Model Synthesis) pipeline:
130
+
131
+ 1. **Role Specification:** 5 Keyflow MCP tools, 20 operations, 6 workflows, ~500 effective decision paths
132
+ 2. **Corpus Generation:** 5,759 synthetic examples generated by Claude Sonnet 4.6 agents (80% normal, 15% edge, 5% adversarial)
133
+ 3. **Tokenizer:** SentencePiece BPE, 6,000 vocabulary tokens
134
+ 4. **Architecture:** 6-layer decoder-only transformer, 384 hidden dim, 6 heads, SwiGLU FFN, RoPE, RMSNorm
135
+ 5. **Training:** Curriculum learning (normal → edge → adversarial), AdamW with cosine LR schedule, 30 epochs, batch_size=16
136
+ 6. **Hardware:** Apple M3 Pro, MLX with Metal acceleration, 58 minutes total
137
+
138
+ ## Key Findings
139
+
140
+ 1. **Model capacity matters more than data volume.** Scaling from 5.7M to 15M params on the same data improved routing +60% and valid JSON +150%.
141
+ 2. **Tokenizer must be frozen.** Rebuilding the tokenizer between corpus versions resets all learned patterns.
142
+ 3. **Early stopping is essential.** Best checkpoint at epoch 3-4, not final epoch.
143
+
144
+ ## Citation
145
+
146
+ ```bibtex
147
+ @article{ranaweera2026asms,
148
+ title={Agent-Specific Model Synthesis: Compiling Task-Bounded Intelligence from Large Language Models into CPU-Deployable Micro-Models},
149
+ author={Ranaweera, Chandima},
150
+ year={2026},
151
+ note={Draft v0.2, Bistec Global}
152
+ }
153
+ ```
154
+
155
+ ## Paper
156
+
157
+ [Agent-Specific Model Synthesis (ASMS)](https://github.com/chan4lk/timm/blob/main/docs/2026-03-31-agent-specific-model-synthesis.md) — Ranaweera, C. (2026). Draft v0.2.
158
+
159
+ ## License
160
+
161
+ Apache 2.0
architecture.py ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ASMS Stage 4: MLX Micro-Transformer Architecture
3
+
4
+ Decoder-only transformer sized to the OKR agent's decision complexity:
5
+ - 4 layers, 256 hidden dim, 4 attention heads
6
+ - ~15M parameters
7
+ - 512 max context length
8
+ - Task-specific vocabulary (~8K tokens)
9
+
10
+ Designed for Apple Silicon (M-series) via MLX with Metal acceleration.
11
+ """
12
+
13
+ import math
14
+ from dataclasses import dataclass
15
+
16
+ import mlx.core as mx
17
+ import mlx.nn as nn
18
+
19
+
20
+ @dataclass
21
+ class OKRModelConfig:
22
+ """Architecture config matched to ASMS Medium complexity (|D| ≈ 500)."""
23
+
24
+ vocab_size: int = 8000
25
+ max_seq_len: int = 512
26
+ num_layers: int = 6
27
+ hidden_dim: int = 384
28
+ num_heads: int = 6
29
+ ffn_dim: int = 768 # 2x hidden
30
+ dropout: float = 0.1
31
+ rope_theta: float = 10000.0
32
+
33
+ @property
34
+ def head_dim(self) -> int:
35
+ return self.hidden_dim // self.num_heads
36
+
37
+ @property
38
+ def param_count_estimate(self) -> int:
39
+ """Rough parameter count."""
40
+ embed = self.vocab_size * self.hidden_dim # token embeddings
41
+ # Per layer: attention (4 projections) + FFN (2 layers) + 2 layer norms
42
+ attn = 4 * self.hidden_dim * self.hidden_dim # Q, K, V, O projections
43
+ ffn = 2 * self.hidden_dim * self.ffn_dim # up + down projections
44
+ ln = 4 * self.hidden_dim # 2 layer norms per layer
45
+ per_layer = attn + ffn + ln
46
+ total_layers = self.num_layers * per_layer
47
+ final_ln = self.hidden_dim # final layer norm
48
+ lm_head = self.vocab_size * self.hidden_dim # output projection
49
+ return embed + total_layers + final_ln + lm_head
50
+
51
+
52
+ class RoPE(nn.Module):
53
+ """Rotary Position Embedding — efficient positional encoding for short contexts."""
54
+
55
+ def __init__(self, dim: int, max_seq_len: int = 512, theta: float = 10000.0):
56
+ super().__init__()
57
+ self.dim = dim
58
+ freqs = 1.0 / (theta ** (mx.arange(0, dim, 2, dtype=mx.float32) / dim))
59
+ t = mx.arange(max_seq_len, dtype=mx.float32)
60
+ angles = mx.outer(t, freqs)
61
+ self._cos = mx.cos(angles)
62
+ self._sin = mx.sin(angles)
63
+
64
+ def __call__(self, x: mx.array, offset: int = 0) -> mx.array:
65
+ seq_len = x.shape[-2]
66
+ cos = self._cos[offset : offset + seq_len]
67
+ sin = self._sin[offset : offset + seq_len]
68
+
69
+ # Reshape for broadcasting: (seq_len, dim//2) -> (1, 1, seq_len, dim//2)
70
+ # x shape is (B, heads, seq_len, head_dim)
71
+ cos = cos.reshape(1, 1, seq_len, -1)
72
+ sin = sin.reshape(1, 1, seq_len, -1)
73
+
74
+ # Split into pairs and rotate
75
+ x1 = x[..., : self.dim // 2]
76
+ x2 = x[..., self.dim // 2 : self.dim]
77
+ rotated = mx.concatenate([x1 * cos - x2 * sin, x1 * sin + x2 * cos], axis=-1)
78
+
79
+ if x.shape[-1] > self.dim:
80
+ rotated = mx.concatenate([rotated, x[..., self.dim :]], axis=-1)
81
+ return rotated
82
+
83
+
84
+ class Attention(nn.Module):
85
+ """Multi-head self-attention with RoPE and causal masking."""
86
+
87
+ def __init__(self, config: OKRModelConfig):
88
+ super().__init__()
89
+ self.num_heads = config.num_heads
90
+ self.head_dim = config.head_dim
91
+ self.scale = math.sqrt(self.head_dim)
92
+
93
+ self.q_proj = nn.Linear(config.hidden_dim, config.hidden_dim, bias=False)
94
+ self.k_proj = nn.Linear(config.hidden_dim, config.hidden_dim, bias=False)
95
+ self.v_proj = nn.Linear(config.hidden_dim, config.hidden_dim, bias=False)
96
+ self.o_proj = nn.Linear(config.hidden_dim, config.hidden_dim, bias=False)
97
+ self.rope = RoPE(config.head_dim, config.max_seq_len, config.rope_theta)
98
+
99
+ def __call__(self, x: mx.array, mask: mx.array | None = None, cache=None) -> mx.array:
100
+ B, L, _ = x.shape
101
+
102
+ q = self.q_proj(x).reshape(B, L, self.num_heads, self.head_dim).transpose(0, 2, 1, 3)
103
+ k = self.k_proj(x).reshape(B, L, self.num_heads, self.head_dim).transpose(0, 2, 1, 3)
104
+ v = self.v_proj(x).reshape(B, L, self.num_heads, self.head_dim).transpose(0, 2, 1, 3)
105
+
106
+ # Apply RoPE
107
+ offset = 0
108
+ if cache is not None:
109
+ offset = cache[0].shape[2]
110
+
111
+ q = self.rope(q, offset=offset)
112
+ k = self.rope(k, offset=offset)
113
+
114
+ # KV cache for inference
115
+ if cache is not None:
116
+ k = mx.concatenate([cache[0], k], axis=2)
117
+ v = mx.concatenate([cache[1], v], axis=2)
118
+ new_cache = (k, v)
119
+
120
+ # Scaled dot-product attention
121
+ scores = (q @ k.transpose(0, 1, 3, 2)) / self.scale
122
+
123
+ if mask is not None:
124
+ scores = scores + mask
125
+
126
+ weights = mx.softmax(scores, axis=-1)
127
+ out = (weights @ v).transpose(0, 2, 1, 3).reshape(B, L, -1)
128
+ return self.o_proj(out), new_cache
129
+
130
+
131
+ class FeedForward(nn.Module):
132
+ """SwiGLU feed-forward — slightly more expressive than ReLU for small models."""
133
+
134
+ def __init__(self, config: OKRModelConfig):
135
+ super().__init__()
136
+ self.gate_proj = nn.Linear(config.hidden_dim, config.ffn_dim, bias=False)
137
+ self.up_proj = nn.Linear(config.hidden_dim, config.ffn_dim, bias=False)
138
+ self.down_proj = nn.Linear(config.ffn_dim, config.hidden_dim, bias=False)
139
+
140
+ def __call__(self, x: mx.array) -> mx.array:
141
+ return self.down_proj(nn.silu(self.gate_proj(x)) * self.up_proj(x))
142
+
143
+
144
+ class TransformerBlock(nn.Module):
145
+ """Pre-norm transformer block with attention + SwiGLU FFN."""
146
+
147
+ def __init__(self, config: OKRModelConfig):
148
+ super().__init__()
149
+ self.attn_norm = nn.RMSNorm(config.hidden_dim)
150
+ self.attn = Attention(config)
151
+ self.ffn_norm = nn.RMSNorm(config.hidden_dim)
152
+ self.ffn = FeedForward(config)
153
+
154
+ def __call__(self, x: mx.array, mask: mx.array | None = None, cache=None):
155
+ # Pre-norm attention with residual
156
+ h, new_cache = self.attn(self.attn_norm(x), mask=mask, cache=cache)
157
+ x = x + h
158
+ # Pre-norm FFN with residual
159
+ x = x + self.ffn(self.ffn_norm(x))
160
+ return x, new_cache
161
+
162
+
163
+ class OKRMicroModel(nn.Module):
164
+ """
165
+ ASMS Micro-Transformer for OKR agent tool-call generation.
166
+
167
+ Architecture: Decoder-only transformer (GPT-style)
168
+ Input: Tokenized user query + session context
169
+ Output: Structured tool-call JSON tokens (autoregressive)
170
+ """
171
+
172
+ def __init__(self, config: OKRModelConfig):
173
+ super().__init__()
174
+ self.config = config
175
+ self.embed = nn.Embedding(config.vocab_size, config.hidden_dim)
176
+ self.layers = [TransformerBlock(config) for _ in range(config.num_layers)]
177
+ self.norm = nn.RMSNorm(config.hidden_dim)
178
+ self.lm_head = nn.Linear(config.hidden_dim, config.vocab_size, bias=False)
179
+
180
+ def __call__(self, tokens: mx.array, cache=None) -> tuple[mx.array, list]:
181
+ B, L = tokens.shape
182
+ x = self.embed(tokens)
183
+
184
+ # Causal mask
185
+ mask = None
186
+ if L > 1:
187
+ mask = nn.MultiHeadAttention.create_additive_causal_mask(L)
188
+ mask = mask.astype(x.dtype)
189
+
190
+ # Adjust mask shape if using KV cache
191
+ if cache is not None and cache[0] is not None:
192
+ offset = cache[0][0].shape[2]
193
+ prefix_mask = mx.zeros((L, offset), dtype=x.dtype)
194
+ mask = mx.concatenate([prefix_mask, mask], axis=-1)
195
+
196
+ new_caches = []
197
+ for i, layer in enumerate(self.layers):
198
+ layer_cache = cache[i] if cache is not None else None
199
+ x, new_cache = layer(x, mask=mask, cache=layer_cache)
200
+ new_caches.append(new_cache)
201
+
202
+ x = self.norm(x)
203
+ logits = self.lm_head(x)
204
+ return logits, new_caches
205
+
206
+ def generate(
207
+ self,
208
+ prompt_tokens: mx.array,
209
+ max_new_tokens: int = 256,
210
+ temperature: float = 0.1,
211
+ top_p: float = 0.9,
212
+ eos_token_id: int = 3,
213
+ ) -> mx.array:
214
+ """Autoregressive generation with KV caching."""
215
+ tokens = prompt_tokens
216
+ cache = None
217
+ generated = []
218
+
219
+ for _ in range(max_new_tokens):
220
+ logits, cache = self(tokens, cache=cache)
221
+ # Take last token's logits
222
+ next_logits = logits[:, -1, :]
223
+
224
+ if temperature > 0:
225
+ next_logits = next_logits / temperature
226
+ # Top-p sampling
227
+ sorted_logits = mx.sort(next_logits, axis=-1)[:, ::-1]
228
+ sorted_probs = mx.softmax(sorted_logits, axis=-1)
229
+ cumsum = mx.cumsum(sorted_probs, axis=-1)
230
+ # Zero out tokens beyond top_p
231
+ mask = cumsum - sorted_probs > top_p
232
+ sorted_logits = mx.where(mask, mx.array(float("-inf")), sorted_logits)
233
+ probs = mx.softmax(sorted_logits, axis=-1)
234
+ next_token = mx.random.categorical(mx.log(probs + 1e-10))
235
+ next_token = mx.expand_dims(next_token, axis=-1)
236
+ else:
237
+ next_token = mx.argmax(next_logits, axis=-1, keepdims=True)
238
+
239
+ generated.append(next_token)
240
+
241
+ if next_token.item() == eos_token_id:
242
+ break
243
+
244
+ tokens = next_token
245
+
246
+ return mx.concatenate([prompt_tokens] + generated, axis=-1) if generated else prompt_tokens
247
+
248
+ @property
249
+ def num_params(self) -> int:
250
+ """Count actual model parameters."""
251
+ nparams = sum(v.size for _, v in nn.utils.tree_flatten(self.parameters()))
252
+ return nparams
253
+
254
+
255
+ def create_model(config: OKRModelConfig | None = None) -> OKRMicroModel:
256
+ """Create model with default ASMS Medium config."""
257
+ if config is None:
258
+ config = OKRModelConfig()
259
+ model = OKRMicroModel(config)
260
+ return model
261
+
262
+
263
+ if __name__ == "__main__":
264
+ config = OKRModelConfig()
265
+ print(f"OKR Micro-Transformer Config:")
266
+ print(f" Layers: {config.num_layers}")
267
+ print(f" Hidden dim: {config.hidden_dim}")
268
+ print(f" Heads: {config.num_heads}")
269
+ print(f" FFN dim: {config.ffn_dim}")
270
+ print(f" Vocab size: {config.vocab_size}")
271
+ print(f" Max seq: {config.max_seq_len}")
272
+ print(f" Est params: {config.param_count_estimate:,}")
273
+
274
+ model = create_model(config)
275
+ mx.eval(model.parameters())
276
+ actual = model.num_params
277
+ print(f" Real params: {actual:,}")
278
+ print(f" Size (FP16): ~{actual * 2 / 1e6:.1f} MB")
279
+ print(f" Size (INT4): ~{actual * 0.5 / 1e6:.1f} MB")
280
+
281
+ # Test forward pass
282
+ batch = mx.zeros((1, 32), dtype=mx.int32)
283
+ logits, _ = model(batch)
284
+ print(f"\n Forward pass: input {batch.shape} -> logits {logits.shape}")
285
+ print(f" Metal GPU: {mx.metal.is_available()}")
config.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "vocab_size": 6000,
3
+ "max_seq_len": 512,
4
+ "num_layers": 6,
5
+ "hidden_dim": 384,
6
+ "num_heads": 6,
7
+ "ffn_dim": 768,
8
+ "dropout": 0.1,
9
+ "rope_theta": 10000.0,
10
+ "step": 2060
11
+ }
config_q4.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "vocab_size": 6000,
3
+ "max_seq_len": 512,
4
+ "num_layers": 6,
5
+ "hidden_dim": 384,
6
+ "num_heads": 6,
7
+ "ffn_dim": 768,
8
+ "dropout": 0.1,
9
+ "rope_theta": 10000.0,
10
+ "step": 2060,
11
+ "quantization": {
12
+ "bits": 4,
13
+ "group_size": 32
14
+ }
15
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8443357be1fbcdaf4083a757841e7c2c15ed2892d5df6695f3605b47c771fa32
3
+ size 53846941
model_q4.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:01bef788966f93aa03633bad583727906f1872111cff4b12bc2bc9b2cb01c7dd
3
+ size 10125343
okr_tokenizer.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:61dff754d3eeebcc2839bda0b03cb55843184db215e2975d5eab66393df2e31d
3
+ size 332527