File size: 12,076 Bytes
5ea0a54
76e50b5
 
 
 
9f2e5fb
 
8092193
5ea0a54
 
 
7d2dfb8
 
5ea0a54
 
7d2dfb8
 
5ea0a54
7d2dfb8
 
 
8092193
5ea0a54
 
8092193
5ea0a54
 
 
8092193
 
 
 
7d2dfb8
5ea0a54
 
 
7d2dfb8
8092193
7d2dfb8
 
 
5ea0a54
 
 
7d2dfb8
 
5ea0a54
7d2dfb8
 
 
 
 
 
 
 
 
5ea0a54
7d2dfb8
5ea0a54
 
7d2dfb8
76e50b5
7d2dfb8
 
 
5ea0a54
 
7d2dfb8
 
76e50b5
7d2dfb8
 
 
 
 
 
76e50b5
5ea0a54
 
7d2dfb8
5ea0a54
76e50b5
7d2dfb8
 
5ea0a54
 
7d2dfb8
5ea0a54
 
 
7d2dfb8
 
5ea0a54
 
 
76e50b5
7d2dfb8
76e50b5
 
7d2dfb8
5ea0a54
76e50b5
5ea0a54
 
 
 
7d2dfb8
5ea0a54
 
 
7d2dfb8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ea0a54
 
76e50b5
7d2dfb8
76e50b5
 
7d2dfb8
5ea0a54
76e50b5
5ea0a54
 
 
 
 
 
 
 
 
7d2dfb8
5ea0a54
 
 
7d2dfb8
 
5ea0a54
 
 
 
76e50b5
5ea0a54
7d2dfb8
 
 
 
 
 
 
 
5ea0a54
 
76e50b5
7d2dfb8
5ea0a54
 
76e50b5
5ea0a54
 
9f2e5fb
5ea0a54
 
7d2dfb8
5ea0a54
 
 
 
 
 
 
 
 
 
9f2e5fb
5ea0a54
 
 
 
9f2e5fb
7d2dfb8
5ea0a54
 
 
 
9f2e5fb
 
7d2dfb8
9f2e5fb
76e50b5
5ea0a54
 
7d2dfb8
 
 
9f2e5fb
 
5ea0a54
 
 
76e50b5
 
7d2dfb8
 
5ea0a54
7d2dfb8
5ea0a54
7d2dfb8
 
5ea0a54
76e50b5
 
9f2e5fb
5ea0a54
7d2dfb8
5ea0a54
 
 
7d2dfb8
5ea0a54
 
 
 
76e50b5
9f2e5fb
5ea0a54
 
 
 
9f2e5fb
 
5ea0a54
7d2dfb8
 
5ea0a54
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
# coding: utf-8
import gradio as gr
import torch
import numpy as np
from PIL import Image
import fitz  # PyMuPDF
import pandas as pd
from huggingface_hub import login
from transformers import (
    AutoTokenizer, 
    AutoModelForCausalLM, 
    # AutoProcessor, # Replaced by ViltProcessor for VQA
    # AutoModelForVision2Seq, # Replaced by ViltForQuestionAnswering for VQA
    WhisperProcessor,
    WhisperForConditionalGeneration,
    ViltProcessor, # Added for ViLT VQA model
    ViltForQuestionAnswering # Added for ViLT VQA model
)
from parler_tts import ParlerTTSForConditionalGeneration
from transformers import AutoFeatureExtractor

import os
import scipy.io.wavfile as wavfile
import io

# --- Configuration & Model Loading ---

# Hugging Face Hub Login
token = os.getenv("HF_API_TOKEN")
if token:
    login(token=token)
else:
    print("تحذير: لم يتم تعيين متغير البيئة HF_API_TOKEN. بعض النماذج قد تتطلب تسجيل الدخول.")

# Device Configuration
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"استخدام الجهاز: {device}")

# 1. Text Generation Model: distilgpt2 (Lightweight)
text_model_name = "distilgpt2"
print(f"تحميل نموذج النص: {text_model_name}")
text_tokenizer = AutoTokenizer.from_pretrained(text_model_name)
text_model = AutoModelForCausalLM.from_pretrained(
    text_model_name, 
    torch_dtype=torch.float32, 
    device_map="auto"
)
if text_tokenizer.pad_token is None:
    text_tokenizer.pad_token = text_tokenizer.eos_token
print("تم تحميل نموذج النص.")

# 2. Image Analysis Model: dandelin/vilt-b32-finetuned-vqa (Lightweight, Public VQA)
image_model_name = "dandelin/vilt-b32-finetuned-vqa"
print(f"تحميل نموذج الصور (VQA): {image_model_name}")
image_processor = ViltProcessor.from_pretrained(image_model_name)
image_model = ViltForQuestionAnswering.from_pretrained(
    image_model_name, 
    torch_dtype=torch.float16 if device == "cuda" else torch.float32, 
    device_map="auto"
)
print("تم تحميل نموذج الصور (VQA).")

# 3. Speech-to-Text Model: openai/whisper-tiny (Lightweight)
stt_model_name = "openai/whisper-tiny"
print(f"تحميل نموذج تحويل الكلام إلى نص: {stt_model_name}")
stt_processor = WhisperProcessor.from_pretrained(stt_model_name)
stt_model = WhisperForConditionalGeneration.from_pretrained(stt_model_name).to(device)
stt_model.config.forced_decoder_ids = None
print("تم تحميل نموذج تحويل الكلام إلى نص.")

# 4. Text-to-Speech Model: parler-tts/parler-tts-tiny-v1 (Lightweight)
tts_model_repo_id = "parler-tts/parler-tts-tiny-v1"
print(f"تحميل نموذج تحويل النص إلى كلام: {tts_model_repo_id}")
tts_model = ParlerTTSForConditionalGeneration.from_pretrained(tts_model_repo_id).to(device)
tts_feature_extractor = AutoFeatureExtractor.from_pretrained(tts_model_repo_id)
print("تم تحميل مكونات تحويل النص إلى كلام.")

# --- Helper Functions for Model Inference ---

# 1. Text Generation (using distilgpt2)
def generate_text_response(prompt_text):
    try:
        full_prompt = f"السؤال: {prompt_text}\nالإجابة الودية والواضحة:"
        inputs = text_tokenizer(full_prompt, return_tensors="pt", padding=True, truncation=True, max_length=512).to(text_model.device)
        outputs = text_model.generate(
            **inputs,
            max_new_tokens=150,
            temperature=0.7,
            top_k=50,
            do_sample=True,
            pad_token_id=text_tokenizer.eos_token_id,
            no_repeat_ngram_size=2
        )
        response_text = text_tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
        return response_text.strip()
    except Exception as e:
        print(f"خطأ في توليد النص: {e}")
        return f"خطأ في معالجة النص: {str(e)}"

# 2. Image Analysis (using dandelin/vilt-b32-finetuned-vqa)
def analyze_image(pil_image, question_text=None):
    try:
        if pil_image is None:
            return "الرجاء رفع صورة أولاً."
        if isinstance(pil_image, np.ndarray):
            pil_image = Image.fromarray(pil_image).convert("RGB")
        else:
            pil_image = pil_image.convert("RGB")

        if not question_text or question_text.strip() == "":
            # ViLT is a VQA model, it needs a question.
            # If no question, we can ask a generic one, or return a message.
            # For now, let's ask a generic question if none is provided.
            question_text = "What is in this image?"
        
        # Prepare inputs for ViLT
        encoding = image_processor(pil_image, question_text, return_tensors="pt").to(image_model.device)
        
        # Forward pass
        with torch.no_grad():
            outputs = image_model(**encoding)
        
        logits = outputs.logits
        idx = logits.argmax(-1).item()
        response_text = image_model.config.id2label[idx]
        
        return response_text
    except Exception as e:
        print(f"خطأ في تحليل الصورة: {e}")
        return f"خطأ في تحليل الصورة: {str(e)}"

# 3. Audio Processing (STT with Whisper Tiny and TTS with ParlerTTS Tiny)
def process_audio(audio_input):
    try:
        if audio_input is None:
            return "الرجاء تسجيل الصوت أولاً.", "", (16000, np.array([], dtype=np.int16))
            
        sample_rate, audio_data = audio_input
        
        if audio_data.dtype != np.float32:
            audio_data = audio_data.astype(np.float32)
        if np.max(np.abs(audio_data)) > 0:
            audio_data = audio_data / np.max(np.abs(audio_data))
        else:
            return "تم استقبال صوت صامت.", "", (16000, np.array([], dtype=np.int16))

        input_features = stt_processor(audio_data, sampling_rate=sample_rate, return_tensors="pt").input_features.to(device)
        predicted_ids = stt_model.generate(input_features, language="ar")
        transcription = stt_processor.batch_decode(predicted_ids, skip_special_tokens=True)[0].strip()

        if not transcription:
            return "لم يتمكن النموذج من استخراج نص من الصوت.", "", (16000, np.array([], dtype=np.int16))

        text_response = generate_text_response(transcription)
        
        prompt = text_response
        with torch.no_grad():
            generation_output = tts_model.generate(input_ids=None, 
                                                   prompt=prompt, 
                                                   do_sample=True, 
                                                   temperature=1.0).cpu().numpy().squeeze()
        audio_output_np = generation_output
        tts_sample_rate = tts_model.config.sampling_rate

        return transcription, text_response, (tts_sample_rate, audio_output_np)
    except Exception as e:
        print(f"خطأ في معالجة الصوت: {e}")
        empty_audio_data = np.array([], dtype=np.float32)
        return f"خطأ في معالجة الصوت: {str(e)}", "", (16000, empty_audio_data)

# 4. File Processing
def process_file(file_obj):
    try:
        if file_obj is None:
            return "الرجاء رفع ملف أولاً."
        file_path = file_obj.name
        text_content = ""
        if file_path.endswith(".pdf"):
            with fitz.open(file_path) as doc:
                text_content = "\n".join(page.get_text() for page in doc)
        elif file_path.endswith((".xlsx", ".xls")):
            df = pd.read_excel(file_path)
            text_content = df.to_string()
        elif file_path.endswith(".csv"):
            df = pd.read_csv(file_path)
            text_content = df.to_string()
        else:
            return "❌ نوع الملف غير مدعوم حالياً (يدعم PDF, Excel, CSV)."

        if not text_content.strip():
            return "الملف فارغ أو لا يمكن قراءة محتواه النصي."

        max_context_len = 1000
        if len(text_content) > max_context_len:
            text_content = text_content[:max_context_len] + "... [المحتوى تم اختصاره]"
            
        response = generate_text_response(f"لخص المحتوى التالي من الملف: \n\n{text_content}")
        return response
    except Exception as e:
        print(f"خطأ في معالجة الملف: {e}")
        return f"خطأ في قراءة الملف: {str(e)}"

# --- Gradio Interface ---
with gr.Blocks(css=".gradio-container {background-color: #f0f4f8; font-family: Arial; color: #333; padding: 20px;}", theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 🤖 Kemo Chat V3.2 - مساعد ذكي متعدد الوسائط (نماذج خفيفة الوزن - ViLT VQA)")
    gr.Markdown("🎯 تفاعل معي عبر النصوص، الصور، الصوت أو الملفات! (باستخدام نماذج أقل استهلاكًا للذاكرة).")
    gr.Markdown("📁 يدعم الملفات: PDF، Excel، CSV\n🖼️ يدعم الإجابة على الأسئلة حول الصور (VQA)\n🎙️ تحويل الصوت إلى نص والرد صوتياً")

    with gr.Tab("💬 المحادثة النصية"):
        text_input = gr.Textbox(label="اكتب سؤالك أو رسالتك هنا", lines=3)
        text_output = gr.Textbox(label="الرد", lines=5, interactive=False)
        text_submit = gr.Button("إرسال", variant="primary")
        text_submit.click(fn=generate_text_response, inputs=text_input, outputs=text_output)

    with gr.Tab("🖼️ تحليل الصور (سؤال وجواب)"):
        gr.Markdown("ارفع صورة واطرح سؤالاً عنها.")
        with gr.Row():
            image_input = gr.Image(type="pil", label="ارفع صورة")
            with gr.Column():
                image_question = gr.Textbox(label="اطرح سؤالاً عن الصورة (مطلوب لـ ViLT)", lines=2, placeholder="مثال: What color is the car?")
                image_output = gr.Textbox(label="الإجابة", lines=5, interactive=False)
        image_submit = gr.Button("تحليل الصورة", variant="primary")
        image_submit.click(fn=analyze_image, inputs=[image_input, image_question], outputs=image_output)

    with gr.Tab("🎤 التفاعل الصوتي"):
        gr.Markdown("سجّل رسالة صوتية، سيتم تحويلها إلى نص، ثم الرد عليها نصيًا وصوتيًا.")
        audio_input = gr.Audio(sources=["microphone"], type="numpy", label="سجّل رسالتك الصوتية")
        with gr.Row():
            audio_transcription = gr.Textbox(label="النص المستخرج من صوتك", interactive=False, lines=2)
            audio_text_response = gr.Textbox(label="الرد النصي على رسالتك", interactive=False, lines=3)
        audio_output_player = gr.Audio(label="الرد الصوتي من المساعد", type="numpy", interactive=False)
        audio_submit = gr.Button("معالجة الصوت", variant="primary")
        audio_submit.click(fn=process_audio, 
                           inputs=audio_input, 
                           outputs=[audio_transcription, audio_text_response, audio_output_player])

    with gr.Tab("📄 تحليل الملفات"):
        gr.Markdown("ارفع ملف (PDF, Excel, CSV) وسأقوم بتلخيص محتواه أو الإجابة على أسئلتك حوله.")
        file_input = gr.File(label="ارفع ملفك (PDF, Excel, CSV)", file_types=[".pdf", ".xls", ".xlsx", ".csv"])
        file_output = gr.Textbox(label="الرد على محتوى الملف", lines=5, interactive=False)
        file_submit = gr.Button("تحليل الملف", variant="primary")
        file_submit.click(fn=process_file, inputs=file_input, outputs=file_output)

if __name__ == "__main__":
    print("Launching Gradio Demo (Lightweight Models with ViLT VQA)...")
    demo.launch(share=True)