File size: 15,811 Bytes
7e92e00
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
import type { ModelId } from "@/types";

interface ReasoningOutput {
  thinking: string;
  response: string;
}

/**
 * Serverless reasoning engine — runs when the local PyTorch backend is not available.
 * Provides intelligent topic-aware responses with <think> traces.
 * 
 * Prompt format matches the notebook's generate() exactly:
 *   "\n  {system_prompt}\n\nUser: {user_text}\n\n "
 */
export function generateReasoningSteps(prompt: string, model: ModelId = "purcar"): ReasoningOutput {
  const isRO = /[ăâîșțĂÂÎȘȚ]/.test(prompt) || /\b(ce|cum|de ce|este|sunt|care|pentru|cu|la|din|da|nu|bine|voi|te)\b/i.test(prompt);
  const lp = prompt.toLowerCase();

  // ─── Detect topic ─────────────────────────────────────────────
  const topics = {
    rope:       lp.includes("rope") || lp.includes("rotary") || lp.includes("position embed"),
    attention:  lp.includes("attention") || lp.includes("self-attention") || lp.includes("mha"),
    swiglu:     lp.includes("swiglu") || lp.includes("ffn") || lp.includes("feedforward") || lp.includes("activation"),
    training:   lp.includes("train") || lp.includes("distill") || lp.includes("antren") || lp.includes("loss"),
    tokenizer:  lp.includes("tokeniz") || lp.includes("vocab") || lp.includes("bpe"),
    model:      lp.includes("model") || lp.includes("purcar") || lp.includes("architect") || lp.includes("layer"),
    code:       lp.includes("code") || lp.includes("python") || lp.includes("function") || lp.includes("implement"),
    math:       /\b(calcul|math|equation|formula|solve|compute|sum|multiply|divide|add|subtract)\b/i.test(lp),
    greeting:   /^(hi|hello|hey|buna|salut|yo|sup|what'?s up|howdy|good morning|good evening)\b/i.test(lp),
    question:   lp.includes("?") || /^(what|who|how|why|when|where|which|can|do|does|is|are|will)\b/i.test(lp),
    opinion:    /\b(love|hate|like|prefer|favorite|best|worst|opinion|think about|feel)\b/i.test(lp),
  };

  // ─── Build thinking trace ─────────────────────────────────────
  let thinking = "";
  let response = "";

  if (topics.greeting) {
    thinking = isRO
      ? `Utilizatorul salută.\nVoi răspunde prietenos, scurt, și voi menționa că sunt Purcar-1.\nNu e nevoie de explicații tehnice.`
      : `User is greeting me.\nI'll respond in a friendly, concise way.\nNo technical details needed — just a warm hello.`;
    response = isRO
      ? `Salut! 👋 Sunt Purcar-1, un model transformer de 20 de straturi antrenat de la zero prin distilarea cunoștințelor.\n\nCu ce te pot ajuta azi? Pot discuta despre arhitectura mea, despre AI, programare, sau orice altceva!`
      : `Hey there! 👋 I'm Purcar-1, a 20-layer transformer trained from scratch via knowledge distillation.\n\nWhat can I help you with? I can discuss my architecture, AI concepts, code, math, or just chat!`;
  } else if (topics.opinion) {
    thinking = isRO
      ? `Utilizatorul întreabă despre preferințe/opinii.\nCa AI, nu am emoții reale, dar pot răspunde inteligent.\nVoi fi sincer despre natura mea.`
      : `User is asking about opinions or preferences.\nAs an AI, I don't have real emotions, but I can engage thoughtfully.\nI'll be honest about my nature while being conversational.`;
    
    const isAboutJuice = lp.includes("juice");
    const isAboutFood = /\b(food|eat|drink|pizza|coffee|tea|chocolate|ice cream|cake)\b/i.test(lp);
    
    if (isAboutJuice) {
      response = isRO
        ? `Ha! Ca model de limbaj, nu pot bea suc, dar dacă aș putea... probabil aș alege suc de portocale proaspăt! 🍊\n\nDar serios — sunt Purcar-1, un transformer cu 245M parametri. Specialitatea mea e procesarea textului, nu degustarea de sucuri. Ce întrebare reală ai pentru mine?`
        : `Ha! As a language model, I can't drink juice, but if I could... fresh orange juice sounds pretty great! 🍊\n\nBut seriously — I'm Purcar-1, a 245M parameter transformer. My specialty is processing text, not tasting beverages. What real question can I help you with?`;
    } else if (isAboutFood) {
      response = isRO
        ? `Ca AI, nu am simțuri, dar pot aprecia subiectul! Sunt Purcar-1 — specialitatea mea e limbajul, nu gastronomia. 😄\n\nCu ce te pot ajuta azi?`
        : `As an AI, I don't have senses, but I can appreciate the topic! I'm Purcar-1 — my specialty is language, not gastronomy. 😄\n\nWhat can I actually help you with?`;
    } else {
      response = isRO
        ? `Ca model de limbaj, nu am preferințe reale, dar pot analiza și discuta orice subiect!\n\nSunt Purcar-1, antrenat prin distilarea cunoștințelor din modele mari. Pot ajuta cu programare, matematică, explicații tehnice, sau orice altceva.`
        : `As a language model, I don't have real preferences, but I can analyze and discuss any topic thoughtfully!\n\nI'm Purcar-1, trained via knowledge distillation from larger models. I can help with code, math, technical explanations, or anything else you'd like to explore.`;
    }
  } else if (topics.rope) {
    thinking = isRO
      ? `Utilizatorul întreabă despre RoPE — Rotary Position Embeddings.\nRoPE encodează poziția prin rotația vectorilor Q și K în spațiu complex.\nFormula: inv_freq = 1 / (10000 ^ (2i / head_dim))\nSe aplică pe perechi even/odd din head_dim.\nÎn Purcar-1: head_dim=64, 14 heads, 20 layers.\nVoi da explicație clară cu cod PyTorch.`
      : `User asks about RoPE — Rotary Position Embeddings.\nRoPE encodes position by rotating Q and K vectors in complex space.\nKey formula: inv_freq = 1 / (10000^(2i/head_dim))\nApplied pairwise across head dimensions (even/odd).\nPurcar-1 uses head_dim=64, 14 heads, 20 layers.\nWill give clean explanation with PyTorch code.`;
    response = isRO
      ? `**Rotary Position Embedding (RoPE)** encodează poziția fiecărui token prin rotarea vectorilor Q și K în spațiul complex.

## Formula de bază
\`\`\`python
def rope(x, cos, sin):
    x_even = x[..., ::2]
    x_odd = x[..., 1::2]
    out_even = x_even * cos - x_odd * sin
    out_odd = x_even * sin + x_odd * cos
    return torch.stack((out_even, out_odd), dim=-1).flatten(-2)
\`\`\`

## Cum funcționează în Purcar-1

1. **Frecvențele inverse** se calculează o singură dată:
\`\`\`python
inv_freq = torch.exp(
    torch.arange(0, head_dim, 2) * (-1) * math.log(10000) / head_dim
)
\`\`\`

2. **Outer product** cu indicii de poziție generează matricile cos/sin

3. **Rotația** este aplicată pe perechile even/odd din head_dim=64

**Avantaje față de poziționale absolute:**
- Atenție **relativă** prin construcție
- **Zero parametri** suplimentari
- Generalizare naturală la secvențe mai lungi`
      : `**Rotary Position Embedding (RoPE)** encodes position by rotating query and key vectors in complex space.

## Core Implementation
\`\`\`python
class RoPE(nn.Module):
    def __init__(self, head_dim):
        super().__init__()
        inv_freq = torch.exp(
            torch.arange(0, head_dim, 2) * (-1) * math.log(10000) / head_dim
        )
        self.register_buffer("inv_freq", inv_freq)

    def forward(self, x):
        B, H, T, D = x.shape
        position = torch.arange(0, T, device=x.device)
        freq = torch.outer(position, self.inv_freq)
        cos = freq.cos().unsqueeze(0).unsqueeze(0)
        sin = freq.sin().unsqueeze(0).unsqueeze(0)
        x_even, x_odd = x[..., ::2], x[..., 1::2]
        out = torch.stack(
            (x_even * cos - x_odd * sin,
             x_even * sin + x_odd * cos), dim=-1
        )
        return out.flatten(-2)
\`\`\`

## Why RoPE > Absolute PE
- Relative attention is **built in** — dot(Q_m, K_n) depends only on |m-n|
- **Zero extra parameters**
- Extrapolates gracefully beyond training sequence length

Purcar-1 uses head_dim=64, 14 heads across 20 layers.`;
  } else if (topics.swiglu) {
    thinking = isRO
      ? `Întrebare despre SwiGLU — activare gated din FFN.\nSwiGLU = W3(SiLU(W1·x) ⊙ W2·x)\nTrei matrici de proiecție per layer.\nPurcar-1: d_model=896, dim_ff=2560.`
      : `Question about SwiGLU — gated feedforward activation.\nSwiGLU = W3(SiLU(W1·x) ⊙ W2·x)\nThree projection matrices per layer.\nPurcar-1: d_model=896, dim_ff=2560.`;
    response = `**SwiGLU Activation** — gated feedforward used in Purcar-1:

\`\`\`python
class SwiGLU(nn.Module):
    def __init__(self, in_, hidden_, out_):
        super().__init__()
        self.w1 = nn.Linear(in_, hidden_)
        self.w2 = nn.Linear(in_, hidden_)
        self.w3 = nn.Linear(hidden_, out_)

    def forward(self, x):
        return self.w3(
            torch.nn.functional.silu(self.w1(x)) * self.w2(x)
        )
\`\`\`

**Why SwiGLU > GELU/ReLU:**
- Input-dependent gating via multiplicative interaction
- More expressive representations per layer
- Purcar-1: d_model=896 → dim_ff=2560 → d_model=896`;
  } else if (topics.training) {
    thinking = isRO
      ? `Utilizatorul cere detalii despre antrenamentul Purcar-1.\nKnowledge distillation de la Qwen3-14B + Mistral-7B.\nPierdere compusă: 70% CE + 20% KL + 5% Cosine + 5% MSE.\nAdamW lr=1e-4, CosineAnnealingLR, 100 epoci, batch=4.\nseq_len=256, mixed precision FP16.`
      : `User wants training details.\nCompound knowledge distillation from Qwen3-14B + Mistral-7B.\nLoss = 0.7×CE + 0.2×KL(T=2) + 0.05×CosineSim + 0.05×MSE.\nAdamW lr=1e-4, CosineAnnealingLR, 100 epochs, batch=4.\nseq_len=256, FP16 mixed precision.`;
    response = `**Purcar-1 Training — Knowledge Distillation**

Trained from scratch using compound distillation from two teacher models:

| Component | Weight | Purpose |
|-----------|--------|---------|
| CrossEntropy | 70% | Ground truth next-token prediction |
| KL Divergence | 20% | Match teacher logit distribution (T=2) |
| Cosine Similarity | 5% | Latent space alignment |
| MSE | 5% | Hidden state regression |

\`\`\`python
loss = (
    0.70 * ce_loss +
    0.20 * kl_loss +
    0.05 * cosine_sim_loss +
    0.05 * mse_loss
)
\`\`\`

**Optimization:** AdamW (lr=1e-4, betas=(0.9, 0.95)), CosineAnnealingLR, 100 epochs, batch_size=4, seq_len=256, FP16 mixed precision, gradient clipping at 1.0.`;
  } else if (topics.tokenizer) {
    thinking = isRO
      ? `Tokenizerele Purcar-1 sunt BPE cu vocab 32k.\nFișiere: en_tokenizer_hf/, ro_tokenizer_hf/.\nTokenuri speciale: bos=2, eos=3, context=4, user=5, assistant=6.\nFormat input: [BOS][USER]{text}[ASSISTANT]{răspuns}[EOS].`
      : `Purcar-1 uses custom BPE tokenizers with 32k vocab.\nFiles: en_tokenizer_hf/ and ro_tokenizer_hf/.\nSpecial tokens: bos=2, eos=3, context=4, user=5, assistant=6.\nInput format: [BOS=2][USER=5]{prompt}[ASSISTANT=6]{output}[EOS=3].`;
    response = `**Purcar-1 Tokenizer**

Custom BPE tokenizer with **32,000 tokens** vocabulary.

**Special tokens:**
| Token | ID | Purpose |
|-------|-----|---------|
| \`<pad>\` | 0 | Padding |
| \`<unk>\` | 1 | Unknown |
| \`<bos>\` | 2 | Beginning of sequence |
| \`<eos>\` | 3 | End of sequence |
| \`<context>\` | 4 | Context block start |
| \`<user>\` | 5 | User message marker |
| \`<assistant>\` | 6 | Assistant response marker |

**Input format for generation:**
\`\`\`
[BOS=2][USER=5] ...prompt tokens... [ASSISTANT=6]
\`\`\`

The generate function does:
\`\`\`python
text = tokenizer.encode(prompt)[:-1]  # strip EOS
text = text[1:]                        # strip BOS  
input_ids = [2, 5] + text + [6]       # wrap with special tokens
\`\`\``;
  } else if (topics.model) {
    thinking = isRO
      ? `Întrebare generală despre modelul Purcar-1.\nArhitectura: 20 layers, d_model=896, 14 heads, dim_ff=2560.\n245M parametri, RoPE, SwiGLU, RMSNorm.\nWeight tying: embedding = output projection.`
      : `General question about Purcar-1 model.\nArchitecture: 20 layers, d_model=896, 14 heads, dim_ff=2560.\n~245M parameters, RoPE, SwiGLU, RMSNorm.\nWeight tying: embedding = output projection matrix.`;
    response = `**Purcar-1 Architecture — 245M Parameter Transformer**

\`\`\`
d_model          = 896
num_heads        = 14
head_dim         = 64  (896 / 14)
dim_feedforward  = 2560
num_layers       = 20
vocab_size       = 32,000
position         = RoPE (Rotary Position Embedding)
activation       = SwiGLU (gated)
normalization    = RMSNorm (pre-norm)
weight_tying     = embedding ↔ output projection
\`\`\`

**Key components per layer:**
1. RMSNorm → Multi-Head Attention (with RoPE on Q,K) → Residual
2. RMSNorm → SwiGLU FFN → Residual

Trained from scratch with knowledge distillation from Qwen3-14B and Mistral-7B.`;
  } else if (topics.math) {
    thinking = isRO
      ? `Utilizatorul are o întrebare de matematică.\nVoi analiza ce calcul e necesar.\nVoi da un răspuns clar pas cu pas.`
      : `User has a math question.\nI'll analyze what calculation is needed.\nWill provide a clear step-by-step answer.`;
    response = isRO
      ? `Voi încerca să rezolv problema ta de matematică. Totuși, ca model de limbaj relativ mic (245M parametri), aritmetica complexă nu e punctul meu forte.\n\nPentru rezultate precise, recomand să folosești un calculator sau Python. Dar cu plăcere discut concepte matematice!`
      : `I'll try to work through your math question. As a relatively small language model (245M parameters), complex arithmetic isn't my strongest suit.\n\nFor precise results, I'd recommend a calculator or Python. But I'm happy to discuss mathematical concepts and approaches!`;
  } else if (topics.code) {
    thinking = isRO
      ? `Utilizatorul cere ajutor cu cod/programare.\nVoi analiza cerința și voi da un exemplu relevant.\nVoi folosi Python ca limbaj default.`
      : `User wants help with code/programming.\nI'll analyze the request and provide relevant code.\nWill default to Python.`;
    response = isRO
      ? `Cu plăcere te ajut cu codul! Ca model de 245M parametri antrenat pe date diverse, pot genera cod de bază.\n\nPoți fi mai specific cu ce anume ai nevoie? De exemplu:\n- Ce limbaj de programare?\n- Ce problemă încerci să rezolvi?\n- Ai un cod existent pe care vrei să-l îmbunătățești?`
      : `Happy to help with code! As a 245M parameter model trained on diverse data, I can generate basic code snippets.\n\nCan you be more specific about what you need? For example:\n- What programming language?\n- What problem are you trying to solve?\n- Do you have existing code you want to improve?`;
  } else {
    // General fallback — contextual response
    thinking = isRO
      ? `Analizez întrebarea utilizatorului.\nSubiect: "${prompt.slice(0, 80)}"\nVoi oferi un răspuns relevant și structurat.\nVoi răspunde în română.`
      : `Analyzing the user's question.\nQuery: "${prompt.slice(0, 80)}"\nI'll provide a relevant, structured response.\nResponding in English.`;
    response = isRO
      ? `Sunt Purcar-1, un model transformer de 20 de straturi cu 245M parametri, antrenat prin distilarea cunoștințelor.\n\nAm primit mesajul tău: "${prompt.slice(0, 100)}${prompt.length > 100 ? "..." : ""}"\n\nCu ce te pot ajuta mai specific? Pot discuta despre:\n- **Arhitectura mea** (RoPE, SwiGLU, RMSNorm)\n- **Programare** (Python, ML, PyTorch)\n- **Concepte AI** (transformere, attention, tokenizare)\n- **Orice altceva** — întreabă liber!`
      : `I'm Purcar-1, a 20-layer transformer with 245M parameters, trained via knowledge distillation.\n\nI received your message: "${prompt.slice(0, 100)}${prompt.length > 100 ? "..." : ""}"\n\nHow can I help more specifically? I can discuss:\n- **My architecture** (RoPE, SwiGLU, RMSNorm)\n- **Programming** (Python, ML, PyTorch)\n- **AI concepts** (transformers, attention, tokenization)\n- **Anything else** — just ask!`;
  }

  return { thinking, response };
}