|
import os |
|
import gradio as gr |
|
from huggingface_hub import InferenceClient |
|
import firebase_admin |
|
from firebase_admin import credentials, firestore, auth |
|
import uuid |
|
import tempfile |
|
import json |
|
|
|
class XylariaChat: |
|
def __init__(self): |
|
|
|
self.hf_token = os.environ.get("HF_TOKEN") |
|
firebase_cred_json = os.environ.get("FIREBASE_SERVICE_ACCOUNT") |
|
|
|
|
|
if not self.hf_token: |
|
print("Warning: HuggingFace token not found. Some functionality may be limited.") |
|
|
|
|
|
self.db = None |
|
if firebase_cred_json: |
|
try: |
|
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as temp_cred_file: |
|
|
|
try: |
|
firebase_creds = json.loads(firebase_cred_json) |
|
json.dump(firebase_creds, temp_cred_file) |
|
temp_cred_file.close() |
|
|
|
firebase_cred = credentials.Certificate(temp_cred_file.name) |
|
firebase_admin.initialize_app(firebase_cred) |
|
self.db = firestore.client() |
|
except json.JSONDecodeError: |
|
print("Error: Invalid Firebase credentials JSON") |
|
|
|
|
|
os.unlink(temp_cred_file.name) |
|
except Exception as e: |
|
print(f"Firebase initialization error: {e}") |
|
|
|
|
|
self.client = None |
|
if self.hf_token: |
|
try: |
|
self.client = InferenceClient( |
|
model="Qwen/QwQ-32B-Preview", |
|
api_key=self.hf_token |
|
) |
|
except Exception as e: |
|
print(f"Inference client initialization error: {e}") |
|
|
|
|
|
self.conversation_history = [] |
|
|
|
def signup(self, username, email, password): |
|
"""User signup method""" |
|
if not self.db: |
|
return "Firebase is not initialized. Signup unavailable." |
|
|
|
try: |
|
|
|
user = auth.create_user( |
|
email=email, |
|
password=password, |
|
display_name=username |
|
) |
|
|
|
|
|
user_ref = self.db.collection('users').document(user.uid) |
|
user_ref.set({ |
|
'username': username, |
|
'email': email, |
|
'created_at': firestore.SERVER_TIMESTAMP |
|
}) |
|
|
|
return f"Successfully created account for {username}" |
|
except Exception as e: |
|
return f"Signup error: {str(e)}" |
|
|
|
def login(self, email, password): |
|
"""User login method""" |
|
if not self.db: |
|
return "Firebase is not initialized. Login unavailable." |
|
|
|
try: |
|
|
|
user = auth.get_user_by_email(email) |
|
|
|
|
|
|
|
return f"Login successful for {user.display_name}" |
|
except Exception as e: |
|
return f"Login error: {str(e)}" |
|
|
|
def save_message(self, user_id, message, sender): |
|
"""Save message to Firestore""" |
|
if not self.db: |
|
return |
|
|
|
try: |
|
messages_ref = self.db.collection('conversations').document(user_id) |
|
messages_ref.collection('messages').add({ |
|
'text': message, |
|
'sender': sender, |
|
'timestamp': firestore.SERVER_TIMESTAMP |
|
}) |
|
except Exception as e: |
|
print(f"Error saving message: {e}") |
|
|
|
def get_response(self, user_input, chat_history): |
|
|
|
if not self.client: |
|
return "AI response is currently unavailable." |
|
|
|
|
|
messages = [ |
|
{"role": "system", "content": """You are Xylaria 1.4 Senoa, an AI assistant developed by SK MD Saad Amin. |
|
- You're NOT MADE BY OPENAI OR ANY OTHER INSTITUTION |
|
- Be friendly and chatty, use emoji sometimes."""}, |
|
*[{"role": "user" if msg[0] else "assistant", "content": msg[1]} for msg in chat_history] |
|
] |
|
|
|
|
|
messages.append({"role": "user", "content": user_input}) |
|
|
|
|
|
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): |
|
|
|
custom_css = """ |
|
/* OS Theme Detection */ |
|
@media (prefers-color-scheme: dark) { |
|
.gradio-container { |
|
background-color: #121212; |
|
color: #ffffff; |
|
} |
|
} |
|
|
|
@media (prefers-color-scheme: light) { |
|
.gradio-container { |
|
font-family: 'Inter', sans-serif; |
|
background-color: #f3f4f6; |
|
color: #1f2937; |
|
} |
|
} |
|
""" |
|
|
|
def streaming_response(message, chat_history): |
|
|
|
user_id = str(uuid.uuid4()) |
|
|
|
|
|
self.save_message(user_id, message, 'user') |
|
|
|
|
|
response_stream = self.get_response(message, chat_history) |
|
|
|
|
|
if isinstance(response_stream, str): |
|
return "", chat_history + [[message, response_stream]] |
|
|
|
|
|
full_response = "" |
|
updated_history = chat_history + [[message, ""]] |
|
|
|
|
|
try: |
|
for chunk in response_stream: |
|
if chunk.choices[0].delta.content: |
|
chunk_content = chunk.choices[0].delta.content |
|
full_response += chunk_content |
|
|
|
|
|
updated_history[-1][1] = full_response |
|
yield "", updated_history |
|
except Exception as e: |
|
return "", updated_history + [["", f"Error in response: {str(e)}"]] |
|
|
|
|
|
self.save_message(user_id, full_response, 'bot') |
|
|
|
return "", updated_history |
|
|
|
with gr.Blocks(theme='soft', css=custom_css) as demo: |
|
|
|
with gr.Tabs(): |
|
with gr.TabItem("Login"): |
|
with gr.Column(): |
|
login_email = gr.Textbox(label="Email") |
|
login_password = gr.Textbox(label="Password", type="password") |
|
login_btn = gr.Button("Login") |
|
login_output = gr.Textbox(label="Login Status") |
|
|
|
with gr.TabItem("Signup"): |
|
with gr.Column(): |
|
signup_username = gr.Textbox(label="Username") |
|
signup_email = gr.Textbox(label="Email") |
|
signup_password = gr.Textbox(label="Password", type="password") |
|
signup_btn = gr.Button("Sign Up") |
|
signup_output = gr.Textbox(label="Signup Status") |
|
|
|
|
|
with gr.Column(scale=1): |
|
chatbot = gr.Chatbot( |
|
label="Xylaria 1.4 Senoa", |
|
height=500, |
|
show_copy_button=True, |
|
type="messages" |
|
) |
|
|
|
|
|
with gr.Row(): |
|
txt = gr.Textbox( |
|
show_label=False, |
|
placeholder="Type your message...", |
|
scale=4 |
|
) |
|
btn = gr.Button("Send", scale=1) |
|
|
|
|
|
clear = gr.Button("Clear Conversation") |
|
|
|
|
|
login_btn.click( |
|
fn=self.login, |
|
inputs=[login_email, login_password], |
|
outputs=[login_output] |
|
) |
|
|
|
signup_btn.click( |
|
fn=self.signup, |
|
inputs=[signup_username, signup_email, signup_password], |
|
outputs=[signup_output] |
|
) |
|
|
|
|
|
btn.click( |
|
fn=streaming_response, |
|
inputs=[txt, chatbot], |
|
outputs=[txt, chatbot] |
|
) |
|
txt.submit( |
|
fn=streaming_response, |
|
inputs=[txt, chatbot], |
|
outputs=[txt, chatbot] |
|
) |
|
clear.click(lambda: None, None, chatbot) |
|
|
|
return demo |
|
|
|
|
|
xylaria_chat = XylariaChat() |
|
xylaria_chat.create_interface().launch(share=True) |