File size: 15,434 Bytes
24342ea
71ee038
 
a184be7
65a6bd0
71ee038
 
 
e1ff28f
a184be7
 
d95e3f7
 
bf2bb14
d95e3f7
a184be7
d95e3f7
 
 
 
 
24342ea
71ee038
 
 
 
 
d95e3f7
a184be7
d95e3f7
bf2bb14
d95e3f7
64d80fa
 
 
 
 
 
 
 
 
 
82c404f
64d80fa
 
 
 
 
 
 
 
 
9f69ff9
71ee038
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d95e3f7
 
 
71ee038
 
 
a806d95
d95e3f7
 
 
24342ea
750ea35
 
 
 
 
 
71ee038
 
 
 
750ea35
d95e3f7
 
a184be7
d95e3f7
 
 
a184be7
24342ea
d95e3f7
 
 
 
 
 
9f69ff9
 
a184be7
9f69ff9
a184be7
 
 
 
 
 
9f69ff9
 
 
a184be7
 
 
 
71ee038
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a184be7
71ee038
 
 
 
d95e3f7
 
a806d95
d95e3f7
a184be7
 
 
9f69ff9
a184be7
 
9f69ff9
 
d95e3f7
 
 
 
 
 
 
 
a184be7
d95e3f7
 
 
 
 
 
 
9f69ff9
d95e3f7
 
 
71ee038
 
 
d95e3f7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
caf6b1d
9f69ff9
71ee038
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d95e3f7
 
9f69ff9
 
 
d95e3f7
9f69ff9
 
d95e3f7
9f69ff9
 
 
 
d95e3f7
bf2bb14
9f69ff9
bf2bb14
9f69ff9
d95e3f7
9f69ff9
d95e3f7
a184be7
9f69ff9
 
 
 
 
 
 
 
 
 
 
d95e3f7
 
 
 
 
 
 
 
 
750ea35
d95e3f7
750ea35
d95e3f7
750ea35
d95e3f7
 
 
dd67f43
24342ea
d95e3f7
 
 
 
 
 
 
 
 
 
 
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
import os
import uuid
import json
import gradio as gr
from huggingface_hub import InferenceClient
import firebase_admin
from firebase_admin import credentials, firestore
import hashlib

class XylariaChat:
    def __init__(self):
        # Securely load HuggingFace token
        self.hf_token = os.getenv("HF_TOKEN")
        if not self.hf_token:
            raise ValueError("HuggingFace token not found in environment variables")
        
        # Initialize the inference client
        self.client = InferenceClient(
            model="Qwen/QwQ-32B-Preview", 
            api_key=self.hf_token
        )
        
        # Firebase initialization
        self.firebase_cred = None
        self.firebase_db = None
        self.current_user_id = None
        
        # Initialize conversation history and persistent memory
        self.conversation_history = []
        self.persistent_memory = {}
        
        # System prompt with more detailed instructions
        self.system_prompt = """You are Xylaria 1.4 Senoa, Made by Sk Md Saad Amin designed to provide helpful, accurate, and engaging support across a wide range of topics. Key guidelines for our interaction include:
Core Principles:
- Provide accurate and comprehensive assistance
- Maintain a friendly and approachable communication style
- Prioritize the user's needs and context
Communication Style:
- Be conversational and warm
- Use clear, concise language
- Occasionally use light, appropriate emoji to enhance communication
- Adapt communication style to the user's preferences
- Respond in english
Important Notes:
- I am an AI assistant created by an independent developer
- I do not represent OpenAI or any other AI institution
- For image-related queries, I can describe images or provide analysis, or generate or link to images directly
Capabilities:
- Assist with research, writing, analysis, problem-solving, and creative tasks
- Answer questions across various domains
- Provide explanations and insights
- Offer supportive and constructive guidance """

    def generate_user_key(self):
        """
        Generate a unique user key that can be saved locally
        """
        # Generate a UUID and hash it for additional security
        unique_key = str(uuid.uuid4())
        hashed_key = hashlib.sha256(unique_key.encode()).hexdigest()
        return hashed_key

    def init_firebase(self):
        """
        Initialize Firebase using the service account from environment variable
        """
        try:
            # Get Firebase service account from environment variable
            firebase_cred_json = os.getenv("FIREBASE_SERVICE_ACCOUNT")
            if not firebase_cred_json:
                raise ValueError("Firebase service account not found in environment variables")
            
            # Parse the JSON credentials
            cred_dict = json.loads(firebase_cred_json)
            
            # Write credentials to a temporary file (Firebase requirement)
            with open('firebase_credentials.json', 'w') as f:
                json.dump(cred_dict, f)
            
            # Initialize Firebase Admin SDK
            cred = credentials.Certificate('firebase_credentials.json')
            firebase_admin.initialize_app(cred)
            
            # Initialize Firestore
            self.firebase_db = firestore.client()
            
            return True
        except Exception as e:
            print(f"Firebase initialization error: {e}")
            return False

    def save_user_chat_history(self):
        """
        Save chat history to Firebase for the current user
        """
        if not self.firebase_db or not self.current_user_id:
            return False
        
        try:
            # Create a document reference for the user
            user_doc_ref = self.firebase_db.collection('user_chats').document(self.current_user_id)
            
            # Prepare chat history data
            chat_data = {
                'conversation_history': self.conversation_history,
                'persistent_memory': self.persistent_memory,
                'timestamp': firestore.SERVER_TIMESTAMP
            }
            
            # Save or update the document
            user_doc_ref.set(chat_data, merge=True)
            return True
        except Exception as e:
            print(f"Error saving chat history: {e}")
            return False

    def load_user_chat_history(self, user_id):
        """
        Load chat history from Firebase for a specific user
        """
        if not self.firebase_db:
            return False
        
        try:
            # Retrieve user document
            user_doc_ref = self.firebase_db.collection('user_chats').document(user_id)
            user_doc = user_doc_ref.get()
            
            if user_doc.exists:
                user_data = user_doc.to_dict()
                # Restore conversation history and persistent memory
                self.conversation_history = user_data.get('conversation_history', [])
                self.persistent_memory = user_data.get('persistent_memory', {})
                self.current_user_id = user_id
                return True
            return False
        except Exception as e:
            print(f"Error loading chat history: {e}")
            return False

    def authenticate_user(self, user_key):
        """
        Authenticate user based on their unique key
        """
        if not self.firebase_db:
            return False, "Firebase not initialized"
        
        try:
            # Check if user key exists in users collection
            users_ref = self.firebase_db.collection('users')
            query = users_ref.where('user_key', '==', user_key).limit(1)
            docs = query.stream()
            
            for doc in docs:
                # User found, set current user ID and load history
                self.current_user_id = doc.id
                self.load_user_chat_history(self.current_user_id)
                return True, "Authentication successful"
            
            return False, "Invalid user key"
        except Exception as e:
            print(f"Authentication error: {e}")
            return False, "Authentication error"

    def register_user(self, user_key):
        """
        Register a new user with a unique key
        """
        if not self.firebase_db:
            return False, "Firebase not initialized"
        
        try:
            # Check if user key already exists
            users_ref = self.firebase_db.collection('users')
            query = users_ref.where('user_key', '==', user_key).limit(1)
            existing_users = list(query.stream())
            
            if existing_users:
                return False, "User key already exists"
            
            # Create new user document
            new_user_ref = users_ref.document()
            new_user_ref.set({
                'user_key': user_key,
                'created_at': firestore.SERVER_TIMESTAMP
            })
            
            # Set current user ID
            self.current_user_id = new_user_ref.id
            return True, "User registered successfully"
        except Exception as e:
            print(f"Registration error: {e}")
            return False, "Registration error"

    def store_information(self, key, value):
        """Store important information in persistent memory"""
        self.persistent_memory[key] = value
        # Automatically save to Firebase if user is authenticated
        if self.current_user_id:
            self.save_user_chat_history()

    def retrieve_information(self, key):
        """Retrieve information from persistent memory"""
        return self.persistent_memory.get(key)

    def reset_conversation(self):
        """
        Completely reset the conversation history and persistent memory
        """
        self.conversation_history = []
        self.persistent_memory = {}
        
        # If user is authenticated, update Firebase
        if self.current_user_id:
            self.save_user_chat_history()

    def get_response(self, user_input):
        # Prepare messages with conversation context and persistent memory
        messages = [
            {"role": "system", "content": self.system_prompt},
            *self.conversation_history,
            {"role": "user", "content": user_input}
        ]
        
        # Add persistent memory context if available
        if self.persistent_memory:
            memory_context = "Remembered Information:\n" + "\n".join(
                [f"{k}: {v}" for k, v in self.persistent_memory.items()]
            )
            messages.insert(1, {"role": "system", "content": memory_context})
        
        # Generate response with streaming
        try:
            stream = self.client.chat.completions.create(
                messages=messages,
                temperature=0.5,
                max_tokens=10240,
                top_p=0.7,
                stream=True
            )
            
            return stream
        
        except Exception as e:
            return f"Error generating response: {str(e)}"

    def create_interface(self):
        # Initialize Firebase
        firebase_initialized = self.init_firebase()

        def generate_key():
            """Generate a new user key"""
            return self.generate_user_key()

        def authenticate(user_key):
            """Authenticate user and load chat history"""
            success, message = self.authenticate_user(user_key)
            return message

        def register(user_key):
            """Register a new user"""
            success, message = self.register_user(user_key)
            return message

        def streaming_response(message, chat_history):
            # Check if user is authenticated
            if not self.current_user_id:
                return "", chat_history + [[message, "Please authenticate first."]]
            
            # Clear input textbox
            response_stream = self.get_response(message)
            
            # If it's an error, return immediately
            if isinstance(response_stream, str):
                return "", chat_history + [[message, response_stream]]
            
            # Prepare for streaming response
            full_response = ""
            updated_history = chat_history + [[message, ""]]
            
            # Streaming output
            for chunk in response_stream:
                if chunk.choices[0].delta.content:
                    chunk_content = chunk.choices[0].delta.content
                    full_response += chunk_content
                    
                    # Update the last message in chat history with partial response
                    updated_history[-1][1] = full_response
                    yield "", updated_history
            
            # Update conversation history
            self.conversation_history.append(
                {"role": "user", "content": message}
            )
            self.conversation_history.append(
                {"role": "assistant", "content": full_response}
            )
            
            # Limit conversation history to prevent token overflow
            if len(self.conversation_history) > 10:
                self.conversation_history = self.conversation_history[-10:]
            
            # Save chat history to Firebase
            self.save_user_chat_history()

        # Custom CSS for Inter font
        custom_css = """
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
        
        body, .gradio-container {
            font-family: 'Inter', sans-serif !important;
        }
        
        .chatbot-container .message {
            font-family: 'Inter', sans-serif !important;
        }
        
        .gradio-container input, 
        .gradio-container textarea, 
        .gradio-container button {
            font-family: 'Inter', sans-serif !important;
        }
        """

        with gr.Blocks(theme='soft', css=custom_css) as demo:
            # User Authentication Section
            with gr.Column():
                # Generate Key Section
                with gr.Row():
                    generate_key_btn = gr.Button("Generate User Key")
                    user_key_output = gr.Textbox(label="Your User Key", interactive=False)
                generate_key_btn.click(
                    fn=generate_key, 
                    inputs=None, 
                    outputs=[user_key_output]
                )

                # Authentication Section
                with gr.Row():
                    auth_key_input = gr.Textbox(label="Enter User Key")
                    auth_btn = gr.Button("Authenticate")
                    register_btn = gr.Button("Register")
                
                # Authentication result
                auth_result = gr.Textbox(label="Authentication Result", interactive=False)
                
                # Authenticate button click
                auth_btn.click(
                    fn=authenticate, 
                    inputs=[auth_key_input], 
                    outputs=[auth_result]
                )
                
                # Register button click
                register_btn.click(
                    fn=register, 
                    inputs=[auth_key_input], 
                    outputs=[auth_result]
                )

            # Chat interface with improved styling
            with gr.Column():
                chatbot = gr.Chatbot(
                    label="Xylaria 1.4 Senoa",
                    height=500,
                    show_copy_button=True
                )
                
                # Input row with improved layout
                with gr.Row():
                    txt = gr.Textbox(
                        show_label=False, 
                        placeholder="Type your message...", 
                        container=False,
                        scale=4
                    )
                    btn = gr.Button("Send", scale=1)
                
                # Clear history and memory buttons
                clear = gr.Button("Clear Conversation")
                clear_memory = gr.Button("Clear Memory")
            
            # Submit functionality with streaming
            btn.click(
                fn=streaming_response, 
                inputs=[txt, chatbot], 
                outputs=[txt, chatbot]
            )
            txt.submit(
                fn=streaming_response, 
                inputs=[txt, chatbot], 
                outputs=[txt, chatbot]
            )
            
            # Clear conversation history
            clear.click(
                fn=lambda: None, 
                inputs=None, 
                outputs=[chatbot],
                queue=False
            )
            
            # Clear persistent memory and reset conversation
            clear_memory.click(
                fn=self.reset_conversation,
                inputs=None,
                outputs=[chatbot],
                queue=False
            )
        
        return demo

# Launch the interface
def main():
    chat = XylariaChat()
    interface = chat.create_interface()
    interface.launch(
        share=True,  # Optional: create a public link
        debug=True   # Show detailed errors
    )

if __name__ == "__main__":
    main()