Spaces:
Runtime error
Runtime error
Upload 4 files
Browse files- READ.me.md +43 -0
- app.py.py +263 -0
- combined (1).json +0 -0
- requirements.txt +7 -0
READ.me.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Swahili Chat Assistant
|
| 3 |
+
emoji: 🇹🇿
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 4.0.0
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# Swahili Chat Assistant 🤖
|
| 13 |
+
|
| 14 |
+
A dual-interface chat assistant featuring:
|
| 15 |
+
- **Kiswahili Learning Assistant**: RAG-powered specialized Swahili education
|
| 16 |
+
- **General Conversation Assistant**: Open-ended chat using fine-tuned Swahili Gemma model
|
| 17 |
+
|
| 18 |
+
## Model Used
|
| 19 |
+
This app uses my fine-tuned Swahili Gemma model: [BrianGithaiga/swahili-gemma-finetuned](https://huggingface.co/BrianGithaiga/swahili-gemma-finetuned)
|
| 20 |
+
|
| 21 |
+
## Features
|
| 22 |
+
- 🎓 Educational RAG system for Swahili learning
|
| 23 |
+
- 💬 General conversation in Swahili
|
| 24 |
+
- 🚀 Fast inference with optimized tokenizer
|
| 25 |
+
- 📱 Responsive Gradio interface
|
| 26 |
+
|
| 27 |
+
## Usage
|
| 28 |
+
1. **Left Side**: Ask questions about Swahili literature, grammar, and culture
|
| 29 |
+
2. **Right Side**: Have general conversations in Swahili
|
| 30 |
+
|
| 31 |
+
## Technical Details
|
| 32 |
+
- **Base Model**: CraneAILabs/swahili-gemma-1b
|
| 33 |
+
- **Fine-tuned**: BrianGithaiga/swahili-gemma-finetuned
|
| 34 |
+
- **Framework**: Transformers + Gradio
|
| 35 |
+
- **Hosting**: Hugging Face Spaces
|
| 36 |
+
|
| 37 |
+
## License
|
| 38 |
+
Apache-2.0 License
|
| 39 |
+
|
| 40 |
+
## Model Performance
|
| 41 |
+
- Validation Loss: 1.3800
|
| 42 |
+
- Perplexity: 3.97
|
| 43 |
+
- Optimized for Swahili conversation
|
app.py.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import json
|
| 4 |
+
import numpy as np
|
| 5 |
+
import pandas as pd
|
| 6 |
+
from typing import List, Dict, Tuple
|
| 7 |
+
import re
|
| 8 |
+
|
| 9 |
+
# Install required packages (for Hugging Face Spaces compatibility)
|
| 10 |
+
os.system("pip install transformers==4.35.2 accelerate==0.24.1 sentencepiece==0.1.99 torch==2.1.2 gradio==4.13.0")
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer
|
| 14 |
+
|
| 15 |
+
print("PyTorch version:", torch.__version__)
|
| 16 |
+
|
| 17 |
+
class SwahiliLiteratureRAG:
|
| 18 |
+
def __init__(self, knowledge_base_path: str = None, model_name: str = None):
|
| 19 |
+
"""
|
| 20 |
+
Initialize the RAG system
|
| 21 |
+
"""
|
| 22 |
+
self.knowledge_base = self.load_knowledge_base(knowledge_base_path)
|
| 23 |
+
self.model = self.load_model(model_name)
|
| 24 |
+
self.similarity_threshold = 0.15
|
| 25 |
+
self.swahili_stop_words = {
|
| 26 |
+
'ni', 'nini', 'na', 'ya', 'za', 'wa', 'la', 'kwa', 'katika', 'au', 'je',
|
| 27 |
+
'hii', 'ile', 'hilo', 'hiyo', 'hayo', 'hao', 'yule', 'huyu', 'huu',
|
| 28 |
+
'kuhusu', 'juu', 'chini', 'mbele', 'nyuma', 'ndani', 'nje', 'karibu',
|
| 29 |
+
'mbali', 'hapa', 'pale', 'kule', 'sasa', 'jana', 'kesho', 'leo'
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
def load_knowledge_base(self, path: str = None) -> List[Dict]:
|
| 33 |
+
"""Load your knowledge base from JSON file"""
|
| 34 |
+
if path and os.path.exists(path):
|
| 35 |
+
try:
|
| 36 |
+
with open(path, 'r', encoding='utf-8') as f:
|
| 37 |
+
return json.load(f)
|
| 38 |
+
except Exception as e:
|
| 39 |
+
print(f"Error loading knowledge base: {e}")
|
| 40 |
+
|
| 41 |
+
# Default sample data
|
| 42 |
+
return [
|
| 43 |
+
{
|
| 44 |
+
"instruction": "Eleza kuhusu Fasihi Simulizi.",
|
| 45 |
+
"input": "",
|
| 46 |
+
"output": "Fasihi simulizi ni sanaa inayotumia lugha kuwasilisha ujumbe unaomhusu binadamu..."
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"instruction": "Tofautisha Fasihi na Sanaa Nyingine.",
|
| 50 |
+
"input": "",
|
| 51 |
+
"output": "Fasihi hutumia lugha na wahusika kuwasilisha maudhui..."
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
"instruction": "Tofautisha Fasihi Simulizi na Fasihi Andishi.",
|
| 55 |
+
"input": "",
|
| 56 |
+
"output": "Fasihi simulizi huwasilishwa kwa mdomo... Fasihi andishi huwasilishwa kwa maandishi..."
|
| 57 |
+
},
|
| 58 |
+
{
|
| 59 |
+
"instruction": "Eleza vipengele vya Fasihi Simulizi.",
|
| 60 |
+
"input": "",
|
| 61 |
+
"output": "Vipengele vya fasihi simulizi ni pamoja na lugha, mandhari, wahusika, maudhui, na mtindo."
|
| 62 |
+
}
|
| 63 |
+
]
|
| 64 |
+
|
| 65 |
+
def load_model(self, model_name: str = None):
|
| 66 |
+
"""Load your fine-tuned Gemma model from Hugging Face Hub"""
|
| 67 |
+
if not model_name:
|
| 68 |
+
print("No model name provided. Using knowledge base only.")
|
| 69 |
+
return None
|
| 70 |
+
|
| 71 |
+
try:
|
| 72 |
+
print(f"Loading model from Hugging Face: {model_name}")
|
| 73 |
+
|
| 74 |
+
# Load base tokenizer with special tokens
|
| 75 |
+
base_tokenizer = AutoTokenizer.from_pretrained(
|
| 76 |
+
"CraneAILabs/swahili-gemma-1b",
|
| 77 |
+
trust_remote_code=True
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
# Add special tokens
|
| 81 |
+
special_tokens = {
|
| 82 |
+
"additional_special_tokens": ["<|user|>", "<|assistant|>"],
|
| 83 |
+
"bos_token": "<start_of_turn>",
|
| 84 |
+
"eos_token": "<end_of_turn>"
|
| 85 |
+
}
|
| 86 |
+
base_tokenizer.add_special_tokens(special_tokens)
|
| 87 |
+
base_tokenizer.pad_token = base_tokenizer.eos_token
|
| 88 |
+
|
| 89 |
+
# Load model from Hugging Face Hub
|
| 90 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 91 |
+
model_name,
|
| 92 |
+
torch_dtype=torch.float32,
|
| 93 |
+
device_map="cpu",
|
| 94 |
+
trust_remote_code=True,
|
| 95 |
+
use_cache=True
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
pipe = pipeline(
|
| 99 |
+
"text-generation",
|
| 100 |
+
model=model,
|
| 101 |
+
tokenizer=base_tokenizer,
|
| 102 |
+
torch_dtype=torch.float32
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
print("✅ Model loaded successfully from Hugging Face Hub!")
|
| 106 |
+
return pipe
|
| 107 |
+
|
| 108 |
+
except Exception as e:
|
| 109 |
+
print(f"❌ Error loading model: {e}")
|
| 110 |
+
print("Continuing with knowledge base only...")
|
| 111 |
+
return None
|
| 112 |
+
|
| 113 |
+
# [KEEP ALL YOUR EXISTING METHODS - normalize_text, extract_key_terms, preprocess_query, calculate_similarity, retrieve_documents, generate_with_model, answer_query]
|
| 114 |
+
|
| 115 |
+
class GeneralChatbot:
|
| 116 |
+
def __init__(self, model_name: str = None):
|
| 117 |
+
"""
|
| 118 |
+
Initialize general chatbot that uses model without RAG
|
| 119 |
+
"""
|
| 120 |
+
self.model = self.load_model(model_name)
|
| 121 |
+
|
| 122 |
+
def load_model(self, model_name: str = None):
|
| 123 |
+
"""Load the model for general conversation from Hugging Face Hub"""
|
| 124 |
+
if not model_name:
|
| 125 |
+
print("No model name provided for general chat. Using basic responses.")
|
| 126 |
+
return None
|
| 127 |
+
|
| 128 |
+
try:
|
| 129 |
+
print(f"Loading general chat model from Hugging Face: {model_name}")
|
| 130 |
+
|
| 131 |
+
# Load base tokenizer
|
| 132 |
+
base_tokenizer = AutoTokenizer.from_pretrained(
|
| 133 |
+
"CraneAILabs/swahili-gemma-1b",
|
| 134 |
+
trust_remote_code=True
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
# Add special tokens
|
| 138 |
+
special_tokens = {
|
| 139 |
+
"additional_special_tokens": ["<|user|>", "<|assistant|>"],
|
| 140 |
+
"bos_token": "<start_of_turn>",
|
| 141 |
+
"eos_token": "<end_of_turn>"
|
| 142 |
+
}
|
| 143 |
+
base_tokenizer.add_special_tokens(special_tokens)
|
| 144 |
+
base_tokenizer.pad_token = base_tokenizer.eos_token
|
| 145 |
+
|
| 146 |
+
# Load model from Hugging Face Hub
|
| 147 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 148 |
+
model_name,
|
| 149 |
+
torch_dtype=torch.float32,
|
| 150 |
+
device_map="cpu",
|
| 151 |
+
trust_remote_code=True,
|
| 152 |
+
use_cache=True
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
pipe = pipeline(
|
| 156 |
+
"text-generation",
|
| 157 |
+
model=model,
|
| 158 |
+
tokenizer=base_tokenizer,
|
| 159 |
+
torch_dtype=torch.float32
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
pipe.tokenizer = base_tokenizer
|
| 163 |
+
|
| 164 |
+
print("✅ General chat model loaded successfully!")
|
| 165 |
+
return pipe
|
| 166 |
+
|
| 167 |
+
except Exception as e:
|
| 168 |
+
print(f"❌ Error loading general chat model: {e}")
|
| 169 |
+
return None
|
| 170 |
+
|
| 171 |
+
def generate_response(self, query: str) -> str:
|
| 172 |
+
"""Generate response using model's general knowledge only"""
|
| 173 |
+
if not query.strip():
|
| 174 |
+
return "Hello! How can I help you today?"
|
| 175 |
+
|
| 176 |
+
if self.model is None:
|
| 177 |
+
# Basic fallback responses
|
| 178 |
+
fallback_responses = {
|
| 179 |
+
"hello": "Hello! How are you doing today?",
|
| 180 |
+
"hi": "Hi there! What would you like to talk about?",
|
| 181 |
+
"how are you": "I'm doing well, thank you for asking! How about you?",
|
| 182 |
+
"what is your name": "I'm a general conversation assistant. What's your name?",
|
| 183 |
+
"bye": "Goodbye! Have a great day!",
|
| 184 |
+
"thank you": "You're welcome! Is there anything else I can help you with?",
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
query_lower = query.lower().strip()
|
| 188 |
+
for key, response in fallback_responses.items():
|
| 189 |
+
if key in query_lower:
|
| 190 |
+
return response
|
| 191 |
+
|
| 192 |
+
return "I understand you want to chat, but I don't have access to my full capabilities right now. What would you like to talk about?"
|
| 193 |
+
|
| 194 |
+
try:
|
| 195 |
+
# Use the chat format that the model was trained with
|
| 196 |
+
formatted_prompt = f"<start_of_turn>user\n{query}<end_of_turn>\n<start_of_turn>model\n"
|
| 197 |
+
|
| 198 |
+
response = self.model(
|
| 199 |
+
formatted_prompt,
|
| 200 |
+
max_new_tokens=150,
|
| 201 |
+
temperature=0.7,
|
| 202 |
+
do_sample=True,
|
| 203 |
+
repetition_penalty=1.1,
|
| 204 |
+
pad_token_id=self.model.tokenizer.eos_token_id,
|
| 205 |
+
eos_token_id=self.model.tokenizer.eos_token_id,
|
| 206 |
+
use_cache=True
|
| 207 |
+
)
|
| 208 |
+
|
| 209 |
+
generated_text = response[0]['generated_text']
|
| 210 |
+
|
| 211 |
+
# Extract assistant response
|
| 212 |
+
if "<start_of_turn>model\n" in generated_text:
|
| 213 |
+
answer = generated_text.split("<start_of_turn>model\n")[-1]
|
| 214 |
+
answer = answer.replace("<end_of_turn>", "").strip()
|
| 215 |
+
else:
|
| 216 |
+
answer = generated_text.replace(formatted_prompt, "").strip()
|
| 217 |
+
|
| 218 |
+
return answer if answer else "I'm not sure how to respond to that. Could you try rephrasing?"
|
| 219 |
+
|
| 220 |
+
except Exception as e:
|
| 221 |
+
print(f"Error generating general response: {e}")
|
| 222 |
+
return "I'm having trouble generating a response right now. What else would you like to talk about?"
|
| 223 |
+
|
| 224 |
+
# Initialize both systems
|
| 225 |
+
def initialize_systems():
|
| 226 |
+
"""Initialize both RAG and general chat systems"""
|
| 227 |
+
# Use your Hugging Face model
|
| 228 |
+
model_name = "BrianGithaiga/swahili-gemma-finetuned"
|
| 229 |
+
|
| 230 |
+
# For knowledge base
|
| 231 |
+
knowledge_base_path = "combined.json" # Upload this file to your Space if needed
|
| 232 |
+
|
| 233 |
+
print("🚀 Initializing systems with model:", model_name)
|
| 234 |
+
|
| 235 |
+
# Initialize both systems
|
| 236 |
+
rag_system = SwahiliLiteratureRAG(knowledge_base_path, model_name)
|
| 237 |
+
general_chat = GeneralChatbot(model_name)
|
| 238 |
+
|
| 239 |
+
return rag_system, general_chat
|
| 240 |
+
|
| 241 |
+
# Create both system instances
|
| 242 |
+
print("Initializing both systems...")
|
| 243 |
+
rag_system, general_chat = initialize_systems()
|
| 244 |
+
print("Both systems initialized successfully!")
|
| 245 |
+
|
| 246 |
+
# [KEEP YOUR EXISTING create_app() FUNCTION EXACTLY AS IS]
|
| 247 |
+
|
| 248 |
+
# For Hugging Face Spaces deployment
|
| 249 |
+
if __name__ == "__main__":
|
| 250 |
+
# Set environment variables for better performance
|
| 251 |
+
os.environ['TORCH_LOGS'] = ''
|
| 252 |
+
os.environ['TORCHDYNAMO_VERBOSE'] = '0'
|
| 253 |
+
os.environ['TOKENIZERS_PARALLELISM'] = 'false'
|
| 254 |
+
|
| 255 |
+
print("Creating Gradio app...")
|
| 256 |
+
app = create_app()
|
| 257 |
+
|
| 258 |
+
# For Hugging Face Spaces
|
| 259 |
+
app.launch(
|
| 260 |
+
server_name="0.0.0.0",
|
| 261 |
+
server_port=7860,
|
| 262 |
+
share=False
|
| 263 |
+
)
|
combined (1).json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
transformers>=4.35.0
|
| 2 |
+
accelerate>=0.24.0
|
| 3 |
+
sentencepiece>=0.1.99
|
| 4 |
+
torch>=2.1.0,<2.2.0
|
| 5 |
+
gradio>=4.13.0
|
| 6 |
+
numpy>=1.24.0
|
| 7 |
+
pandas>=2.0.0
|