Bennerdo commited on
Commit
47cbf33
·
verified ·
1 Parent(s): 765ac30

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +27 -7
  2. app.py +226 -0
  3. requirements.txt +5 -0
README.md CHANGED
@@ -1,13 +1,33 @@
1
  ---
2
- title: Rieko
3
- emoji: 🚀
4
- colorFrom: gray
5
- colorTo: green
6
  sdk: gradio
7
- sdk_version: 6.30.0
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: RiekoLM Dholuo AI Assistant
3
+ emoji: 🦁
4
+ colorFrom: yellow
5
+ colorTo: red
6
  sdk: gradio
7
+ sdk_version: 5.20.0
 
8
  app_file: app.py
9
  pinned: false
10
+ short_description: Gemma 3 1B Dholuo conversational & translation AI
11
+ models:
12
+ - google/gemma-3-1b-pt
13
+ - Bennerdo/rieko
14
  ---
15
 
16
+ # RiekoLM: Gemma 3 1B for Dholuo 🦁
17
+
18
+ Interactive demo for **RiekoLM** ([`Bennerdo/rieko`](https://huggingface.co/Bennerdo/rieko)), an open-source Dholuo (Luo language of Kenya and Tanzania) AI assistant fine-tuned on Google's **Gemma 3 1B** (`google/gemma-3-1b-pt`) with two-stage QLoRA.
19
+
20
+ ### 🌟 Key Capabilities
21
+ - 💬 **Conversational Dholuo**: Natural multi-turn dialogue in Dholuo with native idioms and cultural expressions.
22
+ - 🔄 **Bidirectional Translation**: High-quality translation between English ↔ Dholuo and Swahili → Dholuo.
23
+ - 📖 **Sigana & Cultural Knowledge**: Luo folklore (e.g., *Luanda Magere*, *Gor Mahia*), proverbs (*Ngeche*), and regional traditions around Lake Victoria (*Nam Lolwe*).
24
+
25
+ ### 🛠️ Architecture & Training
26
+ - **Base Model**: [`google/gemma-3-1b-pt`](https://huggingface.co/google/gemma-3-1b-pt)
27
+ - **Adapter**: [`Bennerdo/rieko`](https://huggingface.co/Bennerdo/rieko)
28
+ - **Stage 1**: Continued Pre-Training (CPT) on cleaned Dholuo corpora (Bible, Wikipedia, Mafand dataset, local archives).
29
+ - **Stage 2**: Supervised Fine-Tuning (SFT) on curated conversational instructions, folklore, and parallel translation pairs.
30
+ - **Hardware**: ZeroGPU (`zero-a10g`).
31
+
32
+ ---
33
+ Built with ❤️ for the Luo language community and African NLP preservation.
app.py ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ try:
2
+ import spaces
3
+ has_spaces = True
4
+ except ImportError:
5
+ has_spaces = False
6
+ class _MockSpaces:
7
+ def GPU(self, *args, **kwargs):
8
+ if len(args) == 1 and callable(args[0]):
9
+ return args[0]
10
+ def decorator(f):
11
+ return f
12
+ return decorator
13
+ spaces = _MockSpaces()
14
+
15
+ import os
16
+ from threading import Thread
17
+ import gradio as gr
18
+ import torch
19
+ from peft import PeftModel
20
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
21
+
22
+ BASE_MODEL_ID = "google/gemma-3-1b-pt"
23
+ ADAPTER_ID = "Bennerdo/rieko"
24
+ HF_TOKEN = os.environ.get("HF_TOKEN", None)
25
+
26
+ print(f"Loading tokenizer from {ADAPTER_ID}...")
27
+ tokenizer = AutoTokenizer.from_pretrained(ADAPTER_ID, token=HF_TOKEN)
28
+ if tokenizer.pad_token_id is None:
29
+ tokenizer.pad_token_id = tokenizer.eos_token_id
30
+
31
+ print(f"Loading base model from {BASE_MODEL_ID}...")
32
+ device = "cuda" if torch.cuda.is_available() else "cpu"
33
+ torch_dtype = torch.bfloat16 if torch.cuda.is_available() else torch.float32
34
+
35
+ base_model = AutoModelForCausalLM.from_pretrained(
36
+ BASE_MODEL_ID,
37
+ torch_dtype=torch_dtype,
38
+ token=HF_TOKEN,
39
+ )
40
+
41
+ if torch.cuda.is_available():
42
+ base_model = base_model.to("cuda")
43
+
44
+ print(f"Loading LoRA adapter from {ADAPTER_ID}...")
45
+ model = PeftModel.from_pretrained(base_model, ADAPTER_ID)
46
+ if torch.cuda.is_available():
47
+ model = model.to("cuda")
48
+
49
+ try:
50
+ model = model.merge_and_unload()
51
+ print("Merged LoRA adapter into base model successfully.")
52
+ except Exception as err:
53
+ print(f"Using PeftModel directly without merge: {err}")
54
+
55
+ model.eval()
56
+ print(f"Model ready on {device} ({torch_dtype})!")
57
+
58
+
59
+ @spaces.GPU(duration=60)
60
+ def respond(
61
+ message: str,
62
+ history: list[dict],
63
+ temperature: float = 0.3,
64
+ top_p: float = 0.9,
65
+ max_tokens: int = 384,
66
+ system_prompt: str = "",
67
+ ):
68
+ """Generate streaming conversational responses in Dholuo using RiekoLM.
69
+
70
+ Args:
71
+ message: The latest user prompt or query.
72
+ history: Prior messages formatted as [{'role': 'user'|'assistant', 'content': str}].
73
+ temperature: Sampling temperature (0.0 = deterministic, higher = creative).
74
+ top_p: Nucleus sampling probability cutoff.
75
+ max_tokens: Maximum number of new tokens to generate.
76
+ system_prompt: Optional background instruction or steering prompt.
77
+ """
78
+ messages = []
79
+ first_user_handled = False
80
+ sys_text = system_prompt.strip() if system_prompt else ""
81
+
82
+ for turn in history:
83
+ role = "assistant" if turn["role"] in ("assistant", "model") else "user"
84
+ content = turn["content"]
85
+ if role == "user" and not first_user_handled and sys_text:
86
+ content = f"[Instruction: {sys_text}]\n\n{content}"
87
+ first_user_handled = True
88
+ messages.append({"role": role, "content": content})
89
+
90
+ if not first_user_handled and sys_text:
91
+ current_content = f"[Instruction: {sys_text}]\n\n{message}"
92
+ else:
93
+ current_content = message
94
+
95
+ messages.append({"role": "user", "content": current_content})
96
+
97
+ prompt_text = tokenizer.apply_chat_template(
98
+ messages,
99
+ tokenize=False,
100
+ add_generation_prompt=True,
101
+ )
102
+ inputs = tokenizer(prompt_text, return_tensors="pt").to(model.device)
103
+
104
+ streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
105
+ stop_tokens = [tokenizer.eos_token_id]
106
+ end_of_turn = tokenizer.convert_tokens_to_ids("<end_of_turn>")
107
+ if end_of_turn is not None and end_of_turn not in stop_tokens:
108
+ stop_tokens.append(end_of_turn)
109
+
110
+ generation_kwargs = dict(
111
+ input_ids=inputs["input_ids"],
112
+ attention_mask=inputs.get("attention_mask"),
113
+ max_new_tokens=max_tokens,
114
+ do_sample=temperature > 0,
115
+ temperature=max(temperature, 0.01) if temperature > 0 else 1.0,
116
+ top_p=top_p if temperature > 0 else 1.0,
117
+ repetition_penalty=1.15,
118
+ no_repeat_ngram_size=4,
119
+ eos_token_id=stop_tokens,
120
+ streamer=streamer,
121
+ )
122
+
123
+ thread = Thread(target=model.generate, kwargs=generation_kwargs)
124
+ thread.start()
125
+
126
+ partial_message = ""
127
+ for new_token in streamer:
128
+ clean_token = new_token.replace("<end_of_turn>", "").replace("<eos>", "").replace("<bos>", "")
129
+ partial_message += clean_token
130
+ yield partial_message
131
+
132
+ thread.join()
133
+
134
+
135
+ custom_css = """
136
+ .gradio-container {
137
+ max-width: 950px !important;
138
+ margin: 0 auto !important;
139
+ }
140
+ .header-badge {
141
+ display: inline-block;
142
+ padding: 3px 10px;
143
+ border-radius: 9999px;
144
+ font-size: 0.78rem;
145
+ font-weight: 600;
146
+ margin-right: 6px;
147
+ background: rgba(234, 179, 8, 0.15);
148
+ color: #eab308;
149
+ border: 1px solid rgba(234, 179, 8, 0.3);
150
+ }
151
+ .header-badge-gpu {
152
+ display: inline-block;
153
+ padding: 3px 10px;
154
+ border-radius: 9999px;
155
+ font-size: 0.78rem;
156
+ font-weight: 600;
157
+ margin-right: 6px;
158
+ background: rgba(34, 197, 94, 0.15);
159
+ color: #22c55e;
160
+ border: 1px solid rgba(34, 197, 94, 0.3);
161
+ }
162
+ """
163
+
164
+ theme = gr.themes.Soft(
165
+ primary_hue="amber",
166
+ secondary_hue="red",
167
+ neutral_hue="slate",
168
+ )
169
+
170
+ with gr.Blocks(theme=theme, css=custom_css, title="RiekoLM: Dholuo AI Assistant") as demo:
171
+ gr.HTML(
172
+ """
173
+ <div style="text-align: center; margin-top: 10px; margin-bottom: 16px;">
174
+ <h1 style="font-size: 2.2rem; font-weight: 800; margin-bottom: 6px;">
175
+ 🦁 RiekoLM: Dholuo AI Assistant
176
+ </h1>
177
+ <p style="font-size: 1.05rem; opacity: 0.85; max-width: 650px; margin: 0 auto 12px auto;">
178
+ Interactive AI assistant fine-tuned on Google's <b>Gemma 3 1B</b> with two-stage QLoRA for fluent Dholuo conversation, translation, and cultural knowledge.
179
+ </p>
180
+ <div>
181
+ <span class="header-badge">Google Gemma 3 1B</span>
182
+ <span class="header-badge">Dholuo · Luo</span>
183
+ <span class="header-badge">English ↔ Dholuo</span>
184
+ <span class="header-badge">Swahili → Dholuo</span>
185
+ <span class="header-badge-gpu">⚡ ZeroGPU Accelerated</span>
186
+ </div>
187
+ </div>
188
+ """
189
+ )
190
+
191
+ gr.ChatInterface(
192
+ fn=respond,
193
+ type="messages",
194
+ examples=[
195
+ ["An iluonga ni Akinyi, in iluongi ni ng'a?", 0.3, 0.9, 384, ""],
196
+ ["Translate from English to Dholuo:\nGood morning, how was your night? I hope you rested well.", 0.2, 0.9, 384, ""],
197
+ ["Translate from Dholuo to English:\nEre kaka inyalo konyo joma nigi tho e dala?", 0.2, 0.9, 384, ""],
198
+ ["Lwanda Magere ne en ng'a e sigand Luo, kendo nyocha otho nang'o?", 0.4, 0.9, 384, ""],
199
+ ["Nyisa kaka inyalo pidho mbuta kod ngege e nam Lolwe.", 0.3, 0.9, 384, ""],
200
+ ["Tafsiri kwa Dholuo: Karibu nyumbani, tuketi tule chakula pamoja.", 0.2, 0.9, 384, ""],
201
+ ],
202
+ additional_inputs=[
203
+ gr.Slider(minimum=0.0, maximum=1.0, value=0.3, step=0.05, label="Temperature", info="Lower = more deterministic & factual, higher = creative"),
204
+ gr.Slider(minimum=0.1, maximum=1.0, value=0.9, step=0.05, label="Top-p", info="Nucleus sampling probability threshold"),
205
+ gr.Slider(minimum=64, maximum=512, value=384, step=32, label="Max New Tokens", info="Maximum generation length"),
206
+ gr.Textbox(value="", label="System / Cultural Steering (Optional)", placeholder="E.g., You are a wise Luo elder explaining traditions...", lines=1),
207
+ ],
208
+ )
209
+
210
+ with gr.Accordion("ℹ️ About RiekoLM & Research Details", open=False):
211
+ gr.Markdown(
212
+ """
213
+ ### Overview
214
+ **RiekoLM** is an open-source Dholuo (Luo language of Kenya and Tanzania) AI assistant.
215
+ It adapts Google's `google/gemma-3-1b-pt` using a two-stage QLoRA strategy:
216
+ 1. **Continued Pre-Training (CPT)**: Trained across cleaned Dholuo texts (Bible, Wikipedia, Mafand dataset, local historical scripts).
217
+ 2. **Supervised Fine-Tuning (SFT)**: Instruct-tuned on conversational interactions, cultural lore (*Sigendini*), and multi-lingual translation pairs.
218
+
219
+ - **Model Card & Weights**: [Bennerdo/rieko](https://huggingface.co/Bennerdo/rieko)
220
+ - **Base Architecture**: [google/gemma-3-1b-pt](https://huggingface.co/google/gemma-3-1b-pt)
221
+ - **License**: Gemma Terms of Use
222
+ """
223
+ )
224
+
225
+ if __name__ == "__main__":
226
+ demo.launch(mcp_server=True)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ transformers>=4.50.0
2
+ peft>=0.14.0
3
+ accelerate
4
+ sentencepiece
5
+ protobuf