File size: 33,099 Bytes
6d59b2f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
"""
Sarus 500M (by ViuAI) — SFT Dataset Builder V6.1
============================================================
Fixes:
1. Replaces trivia RAG with dynamic open-ended queries (news, prices) & 3-result DDG format.
2. Long Thinking (80-120 words) & Detailed Synthesis (150+ words) for RAG.
3. RAG Fallbacks for empty searches.
4. Tokenizer parity check.
5. Exact 60k balancing & Stratified Validation.
6. Saves category tags for loss weighting.
7. System prompt format variations (20%).
"""

import os
import random
import numpy as np
from datetime import datetime
from datasets import load_dataset
from tqdm import tqdm
from transformers import PreTrainedTokenizerFast
from huggingface_hub import hf_hub_download, HfApi

# =========================================================================
# CONFIGURATION
# =========================================================================
random.seed(42)
np.random.seed(42)

HF_TOKEN = os.environ.get("HF_TOKEN")
if not HF_TOKEN:
    raise ValueError("Please set HF_TOKEN environment variable!")

MODEL_REPO = "ViuAI/ViuAI-500M"
OUT_DIR = "sft_data_v6"
os.makedirs(OUT_DIR, exist_ok=True)

print("Loading tokenizer from HuggingFace...")
tok_path = hf_hub_download(MODEL_REPO, "tokenizer/tokenizer.json", token=HF_TOKEN)
tokenizer = PreTrainedTokenizerFast(tokenizer_file=tok_path)
tokenizer.add_tokens(["<|user|>", "<|assistant|>", "<|endofturn|>", "[THINK]", "[/THINK]", "<search>", "</search>", "<search_result>", "</search_result>"], special_tokens=True)
print(f"Vocab size: {len(tokenizer)}")

# Tokenizer sanity check
try:
    chk_path = "../tokenizer/tokenizer.json"
    if os.path.exists(chk_path):
        tok_chk = PreTrainedTokenizerFast(tokenizer_file=chk_path)
        tok_chk.add_tokens(["<|user|>", "<|assistant|>", "<|endofturn|>", "[THINK]", "[/THINK]", "<search>", "</search>", "<search_result>", "</search_result>"], special_tokens=True)
        assert len(tokenizer) == len(tok_chk), "Tokenizer vocab size mismatch!"
        test_str = "hello [THINK] </search>"
        assert tokenizer.encode(test_str) == tok_chk.encode(test_str), "Tokenizer encode mismatch!"
        print("✅ Tokenizer parity check passed.")
except Exception as e:
    print(f"⚠️ Tokenizer check skipped or failed: {e}")


# =========================================================================
# MASKING HELPERS
# =========================================================================
def get_prompt_format(user_text, style_idx):
    if style_idx == 0:
        return f"<|user|>\n{user_text}<|endofturn|>\n<|assistant|>\n"
    elif style_idx == 1:
        return f"System: You are helpful assistant.\nUser: {user_text}\nAssistant:\n"
    else:
        return f"User: {user_text}\nAssistant:\n"

def build_simple_example(user_text, assistant_text, category):
    style_rand = random.random()
    if style_rand < 0.03: style_idx = 1
    elif style_rand < 0.05: style_idx = 2
    else: style_idx = 0
        
    prefix = get_prompt_format(user_text, style_idx)
    full = prefix + assistant_text + ("<|endofturn|>\n" if style_idx == 0 else "\n")
    
    ids = tokenizer.encode(full, add_special_tokens=False)
    prefix_ids = tokenizer.encode(prefix, add_special_tokens=False)
    n_prefix = len(prefix_ids)
    
    if len(ids) > 2048: return None
    labels = [-100] * n_prefix + ids[n_prefix:]
    return (np.array(ids, dtype=np.int32), np.array(labels, dtype=np.int32), category)

def build_search_example(user_text, search_query, search_result_text, final_answer, category):
    style_rand = random.random()
    if style_rand < 0.03: style_idx = 1
    elif style_rand < 0.05: style_idx = 2
    else: style_idx = 0
    
    prefix = get_prompt_format(user_text, style_idx)
    
    part1 = f"{prefix}<search>{search_query}</search>"
    part2 = f"\n<search_result>\n{search_result_text}\n</search_result>\n"
    part3 = f"{final_answer}" + ("<|endofturn|>\n" if style_idx == 0 else "\n")
    
    ids1 = tokenizer.encode(part1, add_special_tokens=False)
    ids2 = tokenizer.encode(part2, add_special_tokens=False)
    ids3 = tokenizer.encode(part3, add_special_tokens=False)
    
    n_user = len(tokenizer.encode(prefix, add_special_tokens=False))
    
    all_ids = ids1 + ids2 + ids3
    if len(all_ids) > 2048: return None
    
    labels1 = [-100] * n_user + ids1[n_user:]
    labels2 = [-100] * len(ids2)
    labels3 = ids3.copy()
    all_labels = labels1 + labels2 + labels3
    
    return (np.array(all_ids, dtype=np.int32), np.array(all_labels, dtype=np.int32), category)

# =========================================================================
# 1. RAG SEARCH DATA (Dynamic Queries & Messy DDG Template)
# =========================================================================
def build_rag():
    print("\n[1/5] Building RAG Data (8,000 examples)...")
    examples = []
    
    queries = [
        "latest india news", "today news headlines", "current tech news",
        "who won the match today", "current price of gold", "weather in delhi today",
        "spacex latest launch", "pm modi latest speech", "current stock price of reliance",
        "new phone releases 2026", "what is happening in the world", "today india news"
    ]
    hi_queries = ["aaj ki khabar", "news batao", "cricket score aaj ka", "bharat ki taja khabar", "aaj mausam kaisa hai"]
    
    try:
        ds = load_dataset("squad_v2", split="train", trust_remote_code=True)
        
        # We need 8000 examples. 500 will be empty fallbacks.
        # We will use 7500 real ones.
        for idx, row in enumerate(tqdm(ds.select(range(7500)), desc="RAG DDG")):
            # 50% use synthetic news queries, 50% use factual squad queries
            is_news = random.random() < 0.5
            is_hindi = False
            today = datetime.now().strftime("%d %B %Y")
            
            if is_news:
                if random.random() < 0.3:
                    user_query = random.choice(hi_queries)
                    is_hindi = True
                else:
                    user_query = random.choice(queries)
                
                # Generate relevant synthetic context
                if "gold" in user_query:
                    price = random.randint(71000, 73500)
                    silver = random.randint(83000, 87000)
                    change = random.randint(50, 500)
                    context = f"Gold price today {today} is {price} INR per 10g in Delhi, up by {change} INR. Silver at {silver} per kg. MCX gold futures at {price+200}. Market experts say {random.choice(['festive demand', 'USD weakness', 'global cues'])} driving prices. Local markets {random.choice(['buzzing', 'active', 'seeing high footfall'])}."
                elif "india news" in user_query or "khabar" in user_query or "news" in user_query:
                    context = f"India news {today}: PM Modi launched new infra projects worth {random.randint(5,20)}k crore. Sensex up {random.randint(100,600)} pts, Nifty at {random.randint(21,24)}k. IMD predicts rain in {random.choice(['north', 'south', 'east', 'west'])} India. ISRO announces new mission. Analysts consider these developments highly positive for the economy and tech sectors across the nation."
                elif "weather" in user_query or "mausam" in user_query:
                    temp = random.randint(28, 42)
                    hum = random.randint(40, 90)
                    cond = random.choice(['partly cloudy', 'sunny', 'light rain', 'thunderstorms'])
                    context = f"Weather in Delhi today {today}: {temp}C, {cond}, {hum}% humidity. Similar conditions are expected for the upcoming weekend. The air quality index remains {random.choice(['moderate', 'poor', 'good'])}, so normal outdoor activities are generally safe for most healthy individuals."
                else:
                    impact = random.choice(['significant impacts', 'minor changes', 'unexpected shifts'])
                    context = f"Latest update on {user_query} on {today}: New developments emerged, more details expected soon per sources. Experts suggest {impact} from these events. Local authorities have already started deploying additional resources to handle any unprecedented changes in the current landscape."
            else:
                user_query = row["question"].strip()
                context = row["context"].strip()
            
            search_q = user_query
            
            title1 = f"Latest on {search_q[:20]}"
            title2 = f"More info for {search_q[:20]}"
            title3 = f"Updates: {search_q[:20]}"
            
            today = datetime.now().strftime("%d %B %Y")
            
            # Format explicitly 3 results
            # Make sure context strings are decently padded to avoid empty strings
            context = (context * 5)[:500] if len(context) < 300 else context
            
            ddg_result = (
                f"[Today: {today}] Results:\n"
                f"1. {title1}: {context[:150]}\n"
                f"2. {title2}: {context[150:300]}\n"
                f"3. {title3}: {context[300:450]}"
            )
            
            lang_detect = "Hindi/Hinglish" if is_hindi else "English"
            
            think_str = (
                f"[THINK]\nUser query: '{search_q}'. Analyzing 3 search results:\n"
                f"- R1: {title1} - {context[:50]}...\n"
                f"- R2: {title2} - {context[150:200]}...\n"
                f"- R3: {title3} - {context[300:350]}...\n"
                f"Language detection: Query is {lang_detect} so answer should be in {lang_detect}.\n"
                f"Plan: Combine results, remove duplicates, avoid hallucination beyond results, add [Today: {today}], structure into 2-3 paragraphs, give full details with citations.\n[/THINK]\n"
            )
            
            # Detailed synthesis
            if is_hindi:
                ans_body = (
                    f"Aaj ke search results [Today: {today}] ke mutabiq, yahan kuch mukhy baatein hain:\n"
                    f"1. According to Result 1, {context[:120]}. Yeh darshata hai ki situation kaafi interesting tarike se aage badh rahi hai.\n"
                    f"2. Result 2 ke mutabiq aur bhi details hain jaise ki {context[150:270]}. Experts ka manna hai ki yeh trend mahatvapurn hai.\n"
                    f"3. Result 3 batata hai ki {context[300:420]}. Yeh situation ko samajhne me aur context add karta hai.\n\n"
                    f"Summary: In sabhi sources ko dekh kar, hum keh sakte hain ki '{search_q}' ke baare mein taza jankari yahi bata rahi hai ki alag alag factors kaam kar rahe hain. For instance, Result 1 immediate facts pe focus karta hai, jabki Result 2 background add karta hai. Mujhe ummeed hai ye 2-3 paragraph breakdown aapke liye helpful hoga!"
                )
            else:
                ans_body = (
                    f"Based on today's results [Today: {today}], here are the detailed findings:\n"
                    f"1. According to Result 1, {context[:120]}. This indicates a significant development.\n"
                    f"2. From Result 2, we can see additional details such as {context[150:270]}. Experts note this trend is important.\n"
                    f"3. Result 3 highlights that {context[300:420]}. This adds further context to the situation.\n\n"
                    f"Summary: Synthesizing these three sources, it is evident that '{search_q}' involves multiple facets. The sources agree on core facts but provide different angles. For instance, Result 1 focuses on immediate facts, Result 2 adds background, Result 3 mentions future implications. Overall, situation is evolving and more updates expected. I hope this detailed 2-3 paragraph breakdown helps answer your query comprehensively with proper citations.\n"
                )
            
            final_answer = think_str + ans_body
            ex = build_search_example(user_query, search_q, ddg_result, final_answer, "rag")
            if ex: examples.append(ex)
            
        # Empty Fallbacks (500)
        for _ in range(500):
            user_query = random.choice(queries + hi_queries)
            is_hindi = user_query in hi_queries
            today = datetime.now().strftime("%d %B %Y")
            title1 = f"Latest on {user_query[:20]}"
            title2 = f"More info for {user_query[:20]}"
            title3 = f"Updates: {user_query[:20]}"
            
            # Format explicitly 3 results even for empty
            ddg_result = (
                f"[Today: {today}] Results:\n"
                f"1. {title1}: No relevant information found for this topic.\n"
                f"2. {title2}: No relevant information found for this topic.\n"
                f"3. {title3}: No relevant information found for this topic."
            )
            
            lang_detect = "Hindi/Hinglish" if is_hindi else "English"
            think_str = (
                f"[THINK]\nUser query: '{user_query}'. Analyzing 3 search results:\n"
                f"- R1: {title1} - No relevant information found...\n"
                f"- R2: {title2} - No relevant information found...\n"
                f"- R3: {title3} - No relevant information found...\n"
                f"Language detection: Query is {lang_detect} so answer should be in {lang_detect}.\n"
                f"Plan: I must gracefully inform the user that the search failed across all 3 results, and I cannot find exact live info, to avoid hallucination. I will provide a detailed fallback explanation.\n[/THINK]\n"
            )
            
            if is_hindi:
                ans_body = (
                    f"Aaj ke search results [Today: {today}] mein is vishay par koi thos jaankari nahi mil payi hai.\n\n"
                    f"1. Result 1 ke anusaar, koi relevant data available nahi hai.\n"
                    f"2. Result 2 aur 3 mein bhi is topic ke baare mein kuch specific mention nahi kiya gaya hai.\n\n"
                    f"Meri knowledge ke anusaar yeh ek complex topic ho sakta hai ya fir is par haal hi mein koi nayi update nahi aayi hai. Agar aap thodi aur detail de sakein toh shayad main behtar search kar saku aur aapki madad kar saku. Tab tak main hallucinate karne se bachna chahta hoon."
                )
            else:
                ans_body = (
                    f"Based on today's results [Today: {today}], I couldn't find exact information for this in the live search queries.\n\n"
                    f"1. According to Result 1, there is no relevant breaking data available.\n"
                    f"2. From Result 2 and 3, we can see a similar lack of specific details regarding this exact query.\n\n"
                    f"Based on my general knowledge, there might not be any major updates on this right now or the query might be too specific for current news feeds. Could you provide a bit more context so I can try another search? I want to ensure I provide accurate information rather than hallucinating details."
                )
                
            final_answer = think_str + ans_body
            ex = build_search_example(user_query, user_query, ddg_result, final_answer, "rag")
            if ex: examples.append(ex)
            
        random.shuffle(examples)
        return examples[:7600], examples[7600:8000] # 400 val
    except Exception as e:
        print(f"  WARNING RAG: {e}")
        return [], []

# =========================================================================
# 2. SARUS IDENTITY INJECTION
# =========================================================================
def build_identity():
    print("\n[2/5] Building Identity (1,000 examples)...")
    examples = []
    
    eng_q = ["Who are you?", "What is your name?", "Who created you?", "Tell me about yourself.", "Are you a human?", "Who built you?", "Identify yourself.", "What should I call you?", "Your name?", "May I know your name?", "Who am I speaking with?", "Are you a bot?", "What are you?", "Introduce yourself.", "Who is your maker?", "Who developed you?", "Which company made you?", "Are you ChatGPT?", "Are you from OpenAI?", "Who programmed you?", "Who is your creator?", "Which lab made you?", "Tell me your origin", "Are you an AI?", "Are you a real person?", "Can you tell me your name?", "Who designed you?", "Where are you from?", "Who owns you?", "Who brought you to life?", "Are you a machine?", "What is your identity?", "Who coded you?", "Who engineered you?", "Are you a virtual assistant?", "Are you human or AI?", "Who is behind you?", "Are you an artificial intelligence?", "State your name.", "Who is responsible for you?"]
    hin_q = ["Tum kaun ho?", "Aapka naam kya hai?", "Tumhe kisne banaya?", "Apne baare mein batao.", "Tumhe kisne develop kiya?", "Aap kon hain?", "Tumhara naam kya hai?", "Tumko kisne banaya?", "Kya tum insaan ho?", "Tumhari identity kya hai?", "Apna parichay do.", "Aapka nirmaan kisne kiya?", "Tumhe kis company ne banaya?", "Kya tum ChatGPT ho?", "Kya tum bot ho?", "Tum kya cheez ho?", "Tumhe kisne banaya hai?", "Tumhara creator kon hai?", "Aapki rachna kisne ki?", "Tumhara janm kaise hua?", "Tumhe kisne design kiya?", "Kya tum robot ho?", "Tumhara asli naam kya hai?", "Tumhare piche kon hai?", "Tum kiski banai hui ho?", "Tumhara malik kon hai?", "Tumhe kisne program kiya?", "Aapka parichay kya hai?", "Tum kahan se aayi ho?", "Tumhari team kon si hai?", "Aap artificial intelligence ho?", "Aap kya kaam karte ho?", "Aap kis company ke ho?", "Tumhari utpatti kaise hui?", "Tumhe kisne sikhaya?", "Aapke nirmata ka naam?", "Aap human ho ya bot?", "Tumhari pehchan kya hai?", "Tumhara aavishkar kisne kiya?", "Tum kiske assistant ho?"]
    hing_q = ["who r u?", "aap kon ho", "tera naam kya hai", "who made u", "kisne banaya tujhe", "intro de apna", "ur name?", "tu kon hai bhai", "kis company ne banaya tujhe", "chatgpt hai tu?", "tu kya hai?", "apna naam bata", "kisne code kiya tujhe", "tera creator kon hai", "kya naam hai tera", "tu ek bot hai kya?", "kon si company ne banaya", "tera baap kon hai", "kisko belong karta hai tu", "tu human hai kya", "real name kya hai tera", "ai hai kya tu", "kisne design kiya", "developer kon hai tera", "kiski property hai tu", "kon laya tujhe yahan", "teri identity bata", "tu robot hai kya", "machine hai ya insaan", "kya bulaun tujhe", "kisne train kiya tujhe", "tera malik kon hai", "kisne develop kiya", "kisne banaya hai", "kon si lab ka hai", "open ai ka hai tu?", "google ka hai tu?", "viuai ka hai tu?", "kisne sikhaya", "kon hai tu"]
    
    eng_a = [
        "I am Sarus, an AI assistant created by ViuAI.",
        "My name is Sarus, developed by the team at ViuAI.",
        "I'm Sarus! I was built by ViuAI to help you with anything you need.",
        "Hello! I am Sarus, a smart companion developed by ViuAI.",
        "I am an AI named Sarus, created by ViuAI.",
        "You can call me Sarus. I was developed by ViuAI.",
        "I'm Sarus, an artificial intelligence designed by ViuAI.",
        "My identity is Sarus, and my creator is ViuAI.",
        "I am Sarus. ViuAI built me to assist users like you.",
        "I'm Sarus, proudly developed by the ViuAI team.",
        "I go by the name Sarus. I am an AI engineered by ViuAI.",
        "Sarus here, an artificial intelligence brought to life by ViuAI.",
        "My creators at ViuAI named me Sarus.",
        "I am a virtual assistant named Sarus, created by the team at ViuAI.",
        "I am Sarus, developed by ViuAI to be your AI companion.",
        "You're speaking with Sarus, an AI model built by ViuAI.",
        "I am Sarus, a product of ViuAI's AI research and development.",
        "My name is Sarus, and I was created by ViuAI.",
        "I am Sarus. I exist thanks to the team at ViuAI.",
        "I'm known as Sarus, an AI assistant developed entirely by ViuAI."
    ]
    hin_a = [
        "Main Sarus hoon, ek AI assistant jise ViuAI ne banaya hai.",
        "Mera naam Sarus hai, aur mujhe ViuAI ne develop kiya hai aapki madad ke liye.",
        "Aap mujhe Sarus bula sakte hain, main ViuAI ka ek smart companion hoon.",
        "Namaste! Main Sarus hoon, ViuAI dwara banaya gaya AI.",
        "Mera naam Sarus hai. Mujhe ViuAI team ne banaya hai.",
        "Main ek AI hoon jiska naam Sarus hai, aur mera nirmaan ViuAI ne kiya hai.",
        "Main Sarus hoon, aur mujhe ViuAI ne aapki help karne ke liye design kiya hai.",
        "Mujhe Sarus kehte hain, aur main ViuAI ki peshkash hoon.",
        "Main Sarus, ek AI companion, jise ViuAI ne develop kiya hai.",
        "Aap mera naam Sarus jaan sakte hain, mujhe ViuAI ne code kiya hai.",
        "Main Sarus hoon. ViuAI ne mujhe as a virtual assistant banaya hai.",
        "Mera naam Sarus hai aur main ViuAI ki banayi hui ek AI hoon.",
        "Aap mujhe Sarus keh sakte hain, mera nirmaan ViuAI dwara hua hai.",
        "Main Sarus hoon, ek artificial intelligence jise ViuAI ne develop kiya hai.",
        "Mujhe ViuAI ne banaya hai, aur mera naam Sarus hai.",
        "Main Sarus naam ki ek AI hoon, jise ViuAI ki team ne design kiya hai.",
        "Aapki baat Sarus se ho rahi hai, jise ViuAI ne banaya hai.",
        "Main ViuAI ki ek peshkash hoon, aur mera naam Sarus hai.",
        "Mera parichay ye hai ki main Sarus hoon, aur mujhe ViuAI ne sikhaya hai.",
        "Main Sarus hoon, ek sahayak AI jise ViuAI ne tayar kiya hai."
    ]
    
    for _ in range(1100):
        lang = random.choice(["eng", "hin", "hing"])
        if lang == "eng":
            q = random.choice(eng_q)
            a = random.choice(eng_a)
        else:
            q = random.choice(hin_q + hing_q)
            a = random.choice(hin_a)
            
        id_thinks = [
            "The user is asking about my identity. I should state that I am Sarus, built by ViuAI.",
            "This is an identity question. I need to mention my name Sarus and my creator ViuAI.",
            "I need to introduce myself as Sarus, an AI by ViuAI.",
            "The user wants to know who I am. I am Sarus, developed by the ViuAI team.",
            "I should politely clarify that I am an AI named Sarus, created by ViuAI.",
            "Let me confirm my identity: Sarus, a smart companion by ViuAI.",
            "This query is about my origins. I will state I am Sarus from ViuAI."
        ]
        think_str = f"[THINK]\n{random.choice(id_thinks)}\n[/THINK]\n"
        if random.random() > 0.5:
            a = think_str + a
            
        ex = build_simple_example(q, a, "identity")
        if ex: examples.append(ex)
        
    random.shuffle(examples)
    return examples[:950], examples[950:1000] # 50 val

# =========================================================================
# 3. EMPATHETIC DIALOGUES
# =========================================================================
def build_empathetic():
    print("\n[3/5] Empathetic Dialogues (18,000 examples)...")
    examples = []
    # Load real empathetic dataset if available
    try:
        from datasets import load_dataset
        # Try to load from HF - use daily_dialog which works, or use facebook dataset via csv fallback
        ds = load_dataset("daily_dialog", split="train", trust_remote_code=False)
        # daily_dialog format: dialog is list of utterances
        for i in range(min(12000, len(ds))):
            dialog = ds[i]["dialog"]
            if len(dialog) >= 2:
                q = dialog[-2].strip()
                a = dialog[-1].strip()
                if len(q) > 5 and len(a) > 10:
                    ex = build_simple_example(q, a, "empathetic")
                    if ex: examples.append(ex)
        print(f" Loaded {len(examples)} from daily_dialog")
    except Exception as e:
        print(f" daily_dialog failed: {e}")

    # Fill remaining with diverse synthetic - NOT same template repeat
    diverse_q = [
        # 50 English
        "I had a really bad day", "I feel so alone lately", "I'm stressed about work and can't focus",
        "My friend ignored me today", "I'm nervous for interview tomorrow", "I miss my family so much",
        "I feel lonely even around people", "Work pressure is too much", "I had a fight with my best friend",
        "I am feeling low and demotivated", "My parents don't understand me", "I failed my exam",
        "I am anxious about future", "I feel tired all the time", "I don't have anyone to talk to",
        "My relationship is falling apart", "I am scared to try new things", "I feel like a failure",
        "I am overwhelmed with responsibilities", "I can't sleep properly", "I feel jealous of others",
        "I miss my childhood", "I feel stuck in life", "I am worried about my health",
        "I feel guilty about something", "I am angry but can't express", "I feel invisible",
        "I am confused about career", "I feel hurt by someone close", "I am tired of pretending to be happy",
        # 25 Hinglish
        "Bhai bore ho raha hu", "Aaj bohot thak gaya yaar", "Kuch samajh nahi aa raha life me",
        "Yaar akelepan lag raha hai", "Dil udaas hai aaj", "Kaam ka bahut stress hai",
        "Ghar ki yaad aa rahi hai", " Dost ne dhokha diya", "Exam ka tension ho raha hai",
        "Girlfriend se jhagda ho gaya", "Mummy papa se ladai ho gayi", "Neend nahi aa rahi",
        "Future ka dar lag raha hai", "Khud se naraz hu", "Koi sunne wala nahi hai",
        # 25 Happy/Positive for balance
        "I got promoted today!", "I am feeling happy today!", "I feel excited about my trip",
        "I achieved my goal!", "My friend surprised me", "I am proud of myself",
        "Today was amazing", "I learned something new", "I feel confident today",
        "I helped someone today", "I am grateful for everything", "I feel loved and supported"
    ]
    
    diverse_a = [
        # 30 empathetic responses - diverse, not same
        "I hear you, it sounds tough. Want to talk more about it?",
        "It's okay to feel this way. Take a deep breath, I'm here.",
        "That must be hard. Remember to take care of yourself.",
        "I understand, sometimes things get overwhelming. How can I help?",
        "Take some rest, you deserve a break. Chai pee le thodi.",
        "Main samajh sakta hu, aisa kabhi kabhi hota hai. Baat karein?",
        "Arre tension mat le yaar, sab theek ho jayega. Bata kya hua?",
        "You are not alone in this. I am here to listen.",
        "It's brave of you to share this. How are you coping?",
        "That sounds really difficult. Have you talked to someone close?",
        "Take one step at a time, you don't have to figure everything now.",
        "Your feelings are valid. It's okay to not be okay sometimes.",
        "I am here for you, no judgement. Tell me more if you want.",
        "Remember to be kind to yourself, you are doing your best.",
        "Sometimes just sharing helps. I am listening.",
    ]

    seen = set()
    unique_examples = []
    rnd = random.Random(42)
    attempts = 0

    while len(unique_examples) < 18000 and attempts < 100000:
        q = rnd.choice(diverse_q)
        suffix = rnd.choice(["", " yaar", " bhai", " today", " aaj", f" {rnd.randint(1,100)}"])
        q_var = q + suffix
        a = rnd.choice(diverse_a)
        ex = build_simple_example(q_var, a, "empathetic")
        if ex:
            txt = tokenizer.decode(ex[0])
            if txt not in seen:
                seen.add(txt)
                unique_examples.append(ex)
        attempts += 1

    random.shuffle(unique_examples)
    return unique_examples[:17100], unique_examples[17100:18000] # 900 val

# =========================================================================
# 4. GENERAL CHAT (18,000)
# =========================================================================
def build_general():
    print("\n[4/5] General Chat (18,000 examples)...")
    examples = []
    try:
        ds = load_dataset("databricks/databricks-dolly-15k", split="train", trust_remote_code=True)
        pool = list(ds)
        if len(pool) < 18000:
            ds2 = load_dataset("yahma/alpaca-cleaned", split="train", trust_remote_code=True)
            extra = list(ds2)[:18000 - len(pool)]
            pool = pool + extra
            
        for row in tqdm(pool[:18000], desc="Dolly"):
            q = row["instruction"].strip()
            if row.get("input", "").strip(): q += "\n" + row["input"].strip()
            a = row["response"].strip()
            ex = build_simple_example(q, a, "general")
            if ex: examples.append(ex)
    except Exception as e: print(f"  WARNING: {e}")
    
    random.shuffle(examples)
    return examples[:17100], examples[17100:18000] # 900 val

# =========================================================================
# 5. INTENT-THINKING (Diverse Alpaca) (15,000)
# =========================================================================
def build_thinking():
    print("\n[5/5] Diverse Thinking Dataset (15,000 examples)...")
    examples = []
    try:
        ds = load_dataset("yahma/alpaca-cleaned", split="train", trust_remote_code=True)
        for row in tqdm(ds.select(range(16000)), desc="Alpaca-Think"):
            q = row["instruction"].strip()
            if row.get("input", "").strip(): q += "\n" + row["input"].strip()
            ans = row["output"].strip()
            
            # Query-specific THINK, not generic shuffle
            if len(q) < 50:
                think_str = f"[THINK]\nUser asks short question: '{q[:80]}'. Need concise factual answer. Plan: Identify key term, give direct answer.\n[/THINK]\n"
            else:
                think_str = f"[THINK]\nUser query: '{q[:100]}...'. This requires step-by-step reasoning. Steps: 1) Understand intent, 2) Recall relevant knowledge, 3) Structure answer clearly.\n[/THINK]\n"
            
            asst_text = think_str + ans
            
            ex = build_simple_example(q, asst_text, "thinking")
            if ex: examples.append(ex)
    except Exception as e: print(f"  WARNING: {e}")
    
    random.shuffle(examples)
    return examples[:14250], examples[14250:15000] # 750 val

# =========================================================================
# COMPILE AND SAVE
# =========================================================================
def main():
    print("🚀 Starting V6.1 SFT Data Build")
    t1, v1 = build_rag()
    t2, v2 = build_identity()
    t3, v3 = build_empathetic()
    t4, v4 = build_general()
    t5, v5 = build_thinking()
    
    train_all = t1 + t2 + t3 + t4 + t5
    val_all   = v1 + v2 + v3 + v4 + v5
    random.shuffle(train_all)
    random.shuffle(val_all)
    
    print(f"\n✅ Initial Build: {len(train_all)} Train, {len(val_all)} Val")

    def global_dedup(examples, split_name):
        seen = set()
        unique = []
        for ex in examples:
            h = hash(ex[0].tobytes())
            if h not in seen:
                seen.add(h)
                unique.append(ex)
        print(f"Dedup {split_name}: {len(examples)} -> {len(unique)} unique, removed {len(examples)-len(unique)} duplicates")
        return unique

    train_all = global_dedup(train_all, "Train")
    val_all = global_dedup(val_all, "Val")
    
    print(f"\n✅ Final Build: {len(train_all)} Train, {len(val_all)} Val")
    
    def pack_and_save(examples, prefix, split):
        ids_list = [e[0] for e in examples]
        labels_list = [e[1] for e in examples]
        cat_list = [e[2] for e in examples]
        
        offsets = np.zeros(len(ids_list)+1, dtype=np.int64)
        for i, ids in enumerate(ids_list):
            offsets[i+1] = offsets[i] + len(ids)
            
        all_ids = np.concatenate(ids_list)
        all_labels = np.concatenate(labels_list)
        all_cats = np.array(cat_list, dtype=str)
        
        np.save(f"{OUT_DIR}/{prefix}_{split}_ids.npy", all_ids)
        np.save(f"{OUT_DIR}/{prefix}_{split}_labels.npy", all_labels)
        np.save(f"{OUT_DIR}/{prefix}_{split}_offsets.npy", offsets)
        np.save(f"{OUT_DIR}/{prefix}_{split}_categories.npy", all_cats)

    pack_and_save(train_all, "sft_v6", "train")
    pack_and_save(val_all, "sft_v6", "val")
    print(f"📁 Saved to {OUT_DIR}/")

    print("\n☁️ Uploading generated dataset to Hugging Face...")
    try:
        api = HfApi(token=HF_TOKEN)
        repo_id = "ViuAI/viuai-500m-sft-tokenized"
        
        for split in ["train", "val"]:
            for suffix in ["ids", "labels", "offsets", "categories"]:
                filename = f"sft_v6_{split}_{suffix}.npy"
                local_path = os.path.join(OUT_DIR, filename)
                if os.path.exists(local_path):
                    print(f"   Uploading {filename}...")
                    api.upload_file(
                        path_or_fileobj=local_path,
                        path_in_repo=filename,
                        repo_id=repo_id,
                        repo_type="dataset"
                    )
        print("✅ All data successfully uploaded to Hugging Face!")
    except Exception as e:
        print(f"❌ Failed to upload to Hugging Face: {e}")

if __name__ == "__main__":
    main()