File size: 7,708 Bytes
6a24301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994b3c9
6a24301
 
994b3c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a24301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
MANAS - Model for Awadhi Natural Autoregressive Sequences
Interactive inference script. Run: python run.py

Requirements:
    pip install -r requirements.txt

Then download the model:
    python -c "from huggingface_hub import hf_hub_download; hf_hub_download('JayF14/MANAS', 'tulsidas_model.pth', local_dir='.')"
"""

import torch
import torch.nn as nn
from torch.nn import functional as F
import json
import re
import sys
import os

try:
    from indic_transliteration import sanscript
    from indic_transliteration.sanscript import transliterate
    TRANSLITERATION_AVAILABLE = True
except ImportError:
    TRANSLITERATION_AVAILABLE = False
    print("Note: indic-transliteration not installed. Only Devanagari input will work.")
    print("Install with: pip install indic-transliteration\n")

# Hyperparameters (must match training exactly)
block_size = 256
n_embd = 384
n_head = 6
n_layer = 6
dropout = 0.0  # 0 for inference
device = 'cuda' if torch.cuda.is_available() else 'cpu'

# Load vocabulary from vocab.json
VOCAB_FILE = os.path.join(os.path.dirname(__file__), 'vocab.json')
MODEL_FILE = os.path.join(os.path.dirname(__file__), 'tulsidas_model.pth')

if not os.path.exists(VOCAB_FILE):
    print("Error: vocab.json not found. Make sure it is in the same folder as this script.")
    sys.exit(1)

if not os.path.exists(MODEL_FILE):
    print("Error: tulsidas_model.pth not found.")
    print("Download it with:")
    print('  python -c "from huggingface_hub import hf_hub_download; hf_hub_download(\'JayF14/MANAS\', \'tulsidas_model.pth\', local_dir=\'.\')"\n')
    sys.exit(1)

with open(VOCAB_FILE, 'r', encoding='utf-8') as f:
    vocab = json.load(f)

vocab_size = vocab['vocab_size']
stoi = vocab['stoi']
itos = {int(k): v for k, v in vocab['itos'].items()}

def encode(s):
    unknown = [c for c in s if c not in stoi]
    if unknown:
        raise ValueError(f"Character(s) not in model vocabulary: {unknown}")
    return [stoi[c] for c in s]

def decode(l):
    return ''.join([itos[i] for i in l])


# Model architecture
class Head(nn.Module):
    def __init__(self, head_size):
        super().__init__()
        self.key   = nn.Linear(n_embd, head_size, bias=False)
        self.query = nn.Linear(n_embd, head_size, bias=False)
        self.value = nn.Linear(n_embd, head_size, bias=False)
        self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
        self.dropout = nn.Dropout(dropout)
    def forward(self, x):
        B, T, C = x.shape
        k, q = self.key(x), self.query(x)
        wei = q @ k.transpose(-2, -1) * (k.shape[-1] ** -0.5)
        wei = wei.masked_fill(self.tril[:T, :T] == 0, float('-inf'))
        wei = F.softmax(wei, dim=-1)
        return self.dropout(wei) @ self.value(x)

class MultiHeadAttention(nn.Module):
    def __init__(self, num_heads, head_size):
        super().__init__()
        self.heads   = nn.ModuleList([Head(head_size) for _ in range(num_heads)])
        self.proj    = nn.Linear(n_embd, n_embd)
        self.dropout = nn.Dropout(dropout)
    def forward(self, x):
        return self.dropout(self.proj(torch.cat([h(x) for h in self.heads], dim=-1)))

class FeedForward(nn.Module):
    def __init__(self, n_embd):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(n_embd, 4 * n_embd), nn.ReLU(),
            nn.Linear(4 * n_embd, n_embd), nn.Dropout(dropout),
        )
    def forward(self, x): return self.net(x)

class Block(nn.Module):
    def __init__(self, n_embd, n_head):
        super().__init__()
        head_size = n_embd // n_head
        self.sa   = MultiHeadAttention(n_head, head_size)
        self.ffwd = FeedForward(n_embd)
        self.ln1  = nn.LayerNorm(n_embd)
        self.ln2  = nn.LayerNorm(n_embd)
    def forward(self, x):
        x = x + self.sa(self.ln1(x))
        x = x + self.ffwd(self.ln2(x))
        return x

class LanguageModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.token_embedding_table    = nn.Embedding(vocab_size, n_embd)
        self.position_embedding_table = nn.Embedding(block_size, n_embd)
        self.blocks  = nn.Sequential(*[Block(n_embd, n_head=n_head) for _ in range(n_layer)])
        self.ln_f    = nn.LayerNorm(n_embd)
        self.lm_head = nn.Linear(n_embd, vocab_size)

    def forward(self, idx, targets=None):
        B, T = idx.shape
        x = self.token_embedding_table(idx) + self.position_embedding_table(torch.arange(T, device=device))
        logits = self.lm_head(self.ln_f(self.blocks(x)))
        return logits, None

    def generate(self, idx, max_new_tokens):
        for _ in range(max_new_tokens):
            logits, _ = self(idx[:, -block_size:])
            idx_next = torch.multinomial(F.softmax(logits[:, -1, :], dim=-1), num_samples=1)
            idx = torch.cat((idx, idx_next), dim=1)
        return idx


# Load model
print(f"\nUsing device: {device}")
print("Loading MANAS model...")
model = LanguageModel()
model.load_state_dict(torch.load(MODEL_FILE, map_location=device))
model.to(device)
model.eval()
print("Model loaded successfully!\n")

# Common ITRANS overrides for accurate Hindi transliteration
OVERRIDES = {
    "shri ram": "श्री राम",
    "shri rama": "श्री राम",
    "jai shri ram": "जय श्री राम",
    "ram": "राम",
    "sita": "सीता",
    "hanuman": "हनुमान",
    "tulsidas": "तुलसीदास",
    "ramcharitmanas": "रामचरितमानस",
}

def to_devanagari(user_input):
    lower = user_input.lower().strip()
    if lower in OVERRIDES:
        return OVERRIDES[lower], True
    if re.search(r'[a-zA-Z]', user_input) and TRANSLITERATION_AVAILABLE:
        return transliterate(user_input, sanscript.ITRANS, sanscript.DEVANAGARI), True
    return user_input, False


# Interactive loop
print("=" * 55)
print("  MANAS - Model for Awadhi Natural Autoregressive Sequences")
print("=" * 55)
print("Type a Hindi/Awadhi prompt in Devanagari or English.")
print("Examples:  shri ram   |   tulsidas   |   jai shri ram")
print("Type 'quit' or press Ctrl+C to exit.")
print()
print("-" * 55)
print("  IF HINDI TEXT LOOKS BROKEN (overlapping/garbled):")
print()
print("  Windows Terminal (recommended fix):")
print("    Settings > Profiles > Appearance > Font face")
print("    -> Set to:  Nirmala UI   or   Mangal")
print()
print("  VS Code terminal:")
print("    Settings > terminal.integrated.fontFamily")
print("    -> Set to:  Nirmala UI")
print()
print("  Old cmd.exe / PowerShell window:")
print("    Right-click title bar > Properties > Font")
print("    -> Select:  NSimSun  (best fallback)")
print()
print("  Best overall: Use Windows Terminal (free from")
print("  Microsoft Store) with Nirmala UI font.")
print("-" * 55)
print()


while True:
    try:
        user_input = input("You: ").strip()
        if not user_input:
            continue
        if user_input.lower() in ('quit', 'exit', 'q'):
            print("जय श्री राम 🙏")
            break

        hindi_input, was_translated = to_devanagari(user_input)

        if was_translated:
            print(f"[Transliterated] → {hindi_input}")

        try:
            context = torch.tensor([encode(hindi_input)], dtype=torch.long, device=device)
            tokens = model.generate(context, max_new_tokens=400)[0].tolist()
            print(f"\nMANAS:\n{decode(tokens)}\n")
            print("-" * 55)
        except ValueError as e:
            print(f"[Error] {e}")
            print("Tip: Type in Devanagari or standard ITRANS (e.g. 'shri ram').\n")

    except KeyboardInterrupt:
        print("\nजय श्री राम 🙏")
        break