awacke1's picture
Create app.py
3434741 verified
raw
history blame
16.6 kB
import streamlit as st
import time
import random
import json
from datetime import datetime
import pytz
import platform
import uuid
import extra_streamlit_components as stx
from io import BytesIO
from PIL import Image
import base64
import cv2
import requests
from moviepy.editor import VideoFileClip
from gradio_client import Client
from openai import OpenAI
import openai
import os
from collections import deque
import numpy as np
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Set page config
st.set_page_config(page_title="Personalized Real-Time Chat", page_icon="💬", layout="wide")
# Initialize cookie manager
cookie_manager = stx.CookieManager()
# File to store chat history and user data
CHAT_FILE = "chat_history.txt"
# Function to save chat history and user data to file
def save_data():
with open(CHAT_FILE, 'w') as f:
json.dump({
'messages': st.session_state.messages,
'users': st.session_state.users
}, f)
# Function to load chat history and user data from file
def load_data():
try:
with open(CHAT_FILE, 'r') as f:
data = json.load(f)
st.session_state.messages = data['messages']
st.session_state.users = data['users']
except FileNotFoundError:
st.session_state.messages = []
st.session_state.users = []
# Load data at the start
load_data()
# Function to get or create user
def get_or_create_user():
user_id = cookie_manager.get(cookie='user_id')
if not user_id:
user_id = str(uuid.uuid4())
cookie_manager.set('user_id', user_id)
user = next((u for u in st.session_state.users if u['id'] == user_id), None)
if not user:
user = {
'id': user_id,
'name': random.choice(['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace', 'Henry']),
'browser': f"{platform.system()} - {st.session_state.get('browser_info', 'Unknown')}"
}
st.session_state.users.append(user)
save_data()
return user
# Initialize session state
if 'messages' not in st.session_state:
st.session_state.messages = []
if 'users' not in st.session_state:
st.session_state.users = []
if 'current_user' not in st.session_state:
st.session_state.current_user = get_or_create_user()
# Initialize OpenAI client
openai.api_key = os.getenv('OPENAI_API_KEY')
openai.organization = os.getenv('OPENAI_ORG_ID')
client = OpenAI(api_key=openai.api_key, organization=openai.organization)
GPT4O_MODEL = "gpt-4o-2024-05-13"
# Initialize HuggingFace client
hf_client = OpenAI(
base_url="https://api-inference.huggingface.co/v1",
api_key=os.environ.get('API_KEY')
)
# Create supported models
model_links = {
"GPT-4o": GPT4O_MODEL,
"Meta-Llama-3.1-70B-Instruct": "meta-llama/Meta-Llama-3.1-70B-Instruct",
"Meta-Llama-3.1-405B-Instruct-FP8": "meta-llama/Meta-Llama-3.1-405B-Instruct-FP8",
"Meta-Llama-3.1-405B-Instruct": "meta-llama/Meta-Llama-3.1-405B-Instruct",
"Meta-Llama-3.1-8B-Instruct": "meta-llama/Meta-Llama-3.1-8B-Instruct",
"Meta-Llama-3-70B-Instruct": "meta-llama/Meta-Llama-3-70B-Instruct",
"Meta-Llama-3-8B-Instruct": "meta-llama/Meta-Llama-3-8B-Instruct",
"C4ai-command-r-plus": "CohereForAI/c4ai-command-r-plus",
"Aya-23-35B": "CohereForAI/aya-23-35B",
"Zephyr-orpo-141b-A35b-v0.1": "HuggingFaceH4/zephyr-orpo-141b-A35b-v0.1",
"Mixtral-8x7B-Instruct-v0.1": "mistralai/Mixtral-8x7B-Instruct-v0.1",
"Codestral-22B-v0.1": "mistralai/Codestral-22B-v0.1",
"Nous-Hermes-2-Mixtral-8x7B-DPO": "NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO",
"Yi-1.5-34B-Chat": "01-ai/Yi-1.5-34B-Chat",
"Gemma-2-27b-it": "google/gemma-2-27b-it",
"Meta-Llama-2-70B-Chat-HF": "meta-llama/Llama-2-70b-chat-hf",
"Meta-Llama-2-7B-Chat-HF": "meta-llama/Llama-2-7b-chat-hf",
"Meta-Llama-2-13B-Chat-HF": "meta-llama/Llama-2-13b-chat-hf",
"Mistral-7B-Instruct-v0.1": "mistralai/Mistral-7B-Instruct-v0.1",
"Mistral-7B-Instruct-v0.2": "mistralai/Mistral-7B-Instruct-v0.2",
"Mistral-7B-Instruct-v0.3": "mistralai/Mistral-7B-Instruct-v0.3",
"Gemma-1.1-7b-it": "google/gemma-1.1-7b-it",
"Gemma-1.1-2b-it": "google/gemma-1.1-2b-it",
"Zephyr-7B-Beta": "HuggingFaceH4/zephyr-7b-beta",
"Zephyr-7B-Alpha": "HuggingFaceH4/zephyr-7b-alpha",
"Phi-3-mini-128k-instruct": "microsoft/Phi-3-mini-128k-instruct",
"Phi-3-mini-4k-instruct": "microsoft/Phi-3-mini-4k-instruct",
}
# Function to reset conversation
def reset_conversation():
st.session_state.conversation = []
st.session_state.messages = []
# Function to generate filenames
def generate_filename(prompt, file_type):
central = pytz.timezone('US/Central')
safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
replaced_prompt = prompt.replace(" ", "_").replace("\n", "_")
safe_prompt = "".join(x for x in replaced_prompt if x.isalnum() or x == "_")[:90]
return f"{safe_date_time}_{safe_prompt}.{file_type}"
# Function to create files
def create_file(filename, prompt, response, user_name, timestamp):
with open(filename, "w", encoding="utf-8") as f:
f.write(f"User: {user_name}\nTimestamp: {timestamp}\n\nPrompt:\n{prompt}\n\nResponse:\n{response}")
# Function to extract video frames
def extract_video_frames(video_path, seconds_per_frame=2):
base64Frames = []
video = cv2.VideoCapture(video_path)
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
fps = video.get(cv2.CAP_PROP_FPS)
frames_to_skip = int(fps * seconds_per_frame)
curr_frame = 0
while curr_frame < total_frames - 1:
video.set(cv2.CAP_PROP_POS_FRAMES, curr_frame)
success, frame = video.read()
if not success:
break
_, buffer = cv2.imencode(".jpg", frame)
base64Frames.append(base64.b64encode(buffer).decode("utf-8"))
curr_frame += frames_to_skip
video.release()
return base64Frames, None
# Function to process audio for video
def process_audio_for_video(video_input):
try:
transcription = client.audio.transcriptions.create(
model="whisper-1",
file=video_input,
)
return transcription.text
except:
return ''
# Function to process text with selected model
def process_text(user_name, text_input, selected_model, temp_values):
timestamp = datetime.now(pytz.utc).strftime('%Y-%m-%d %H:%M:%S %Z')
st.session_state.messages.append({"user": user_name, "message": text_input, "timestamp": timestamp})
with st.chat_message(user_name):
st.markdown(f"{user_name} ({timestamp}): {text_input}")
with st.chat_message("Assistant"):
try:
if selected_model == "GPT-4o":
completion = client.chat.completions.create(
model=GPT4O_MODEL,
messages=[
{"role": "user", "content": m["message"]}
for m in st.session_state.messages
],
stream=True,
temperature=temp_values
)
return_text = st.write_stream(completion)
else:
messages = [
{"content": m["message"]}
for m in st.session_state.messages
]
stream = hf_client.chat.completions.create(
model=model_links[selected_model],
messages=messages,
temperature=temp_values,
stream=True,
max_tokens=3000,
)
return_text = st.write_stream(stream)
except openai.APIError as e:
return_text = f"OpenAI API Error: {str(e)}"
st.error(return_text)
except requests.exceptions.RequestException as e:
return_text = f"Network Error: {str(e)}"
st.error(return_text)
except Exception as e:
return_text = f"Unexpected Error: {str(e)}"
st.error(return_text)
if not return_text.startswith("Error:"):
st.markdown(f"Assistant ({timestamp}): {return_text}")
filename = generate_filename(text_input, "md")
create_file(filename, text_input, return_text, user_name, timestamp)
st.session_state.messages.append({"user": "Assistant", "message": return_text, "timestamp": timestamp})
save_data()
return return_text
# Function to process image (using GPT-4o)
def process_image(user_name, image_input, user_prompt):
image = Image.open(BytesIO(image_input))
base64_image = base64.b64encode(image_input).decode("utf-8")
response = client.chat.completions.create(
model=GPT4O_MODEL,
messages=[
{"role": "system", "content": "You are a helpful assistant that responds in Markdown."},
{"role": "user", "content": [
{"type": "text", "text": user_prompt},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_image}"}}
]}
],
temperature=0.0,
)
image_response = response.choices[0].message.content
timestamp = datetime.now(pytz.utc).strftime('%Y-%m-%d %H:%M:%S %Z')
st.session_state.messages.append({"user": user_name, "message": image_response, "timestamp": timestamp})
with st.chat_message(user_name):
st.image(image)
st.markdown(f"{user_name} ({timestamp}): {user_prompt}")
with st.chat_message("Assistant"):
st.markdown(image_response)
filename_md = generate_filename(user_prompt, "md")
create_file(filename_md, user_prompt, image_response, user_name, timestamp)
save_data()
return image_response
# Function to process audio (using GPT-4o for transcription)
def process_audio(user_name, audio_input, text_input):
if audio_input:
transcription = client.audio.transcriptions.create(
model="whisper-1",
file=audio_input,
)
timestamp = datetime.now(pytz.utc).strftime('%Y-%m-%d %H:%M:%S %Z')
st.session_state.messages.append({"user": user_name, "message": transcription.text, "timestamp": timestamp})
with st.chat_message(user_name):
st.markdown(f"{user_name} ({timestamp}): {transcription.text}")
with st.chat_message("Assistant"):
st.markdown(transcription.text)
filename = generate_filename(transcription.text, "wav")
create_file(filename, text_input, transcription.text, user_name, timestamp)
st.session_state.messages.append({"user": "Assistant", "message": transcription.text, "timestamp": timestamp})
save_data()
return transcription.text
# Function to process video (using GPT-4o)
def process_video(user_name, video_input, user_prompt):
if isinstance(video_input, str):
with open(video_input, "rb") as video_file:
video_input = video_file.read()
base64Frames, audio_path = extract_video_frames(video_input)
transcript = process_audio_for_video(video_input)
response = client.chat.completions.create(
model=GPT4O_MODEL,
messages=[
{"role": "system", "content": "You are generating a video summary. Create a summary of the provided video and its transcript. Respond in Markdown"},
{"role": "user", "content": [
"These are the frames from the video.",
*map(lambda x: {"type": "image_url", "image_url": {"url": f'data:image/jpg;base64,{x}', "detail": "low"}}, base64Frames),
{"type": "text", "text": f"The audio transcription is: {transcript}"},
{"type": "text", "text": user_prompt}
]}
],
temperature=0,
)
video_response = response.choices[0].message.content
st.markdown(video_response)
timestamp = datetime.now(pytz.utc).strftime('%Y-%m-%d %H:%M:%S %Z')
filename_md = generate_filename(user_prompt, "md")
create_file(filename_md, user_prompt, video_response, user_name, timestamp)
st.session_state.messages.append({"user": user_name, "message": video_response, "timestamp": timestamp})
save_data()
return video_response
# Main function for each column
def main_column(column_name):
st.markdown(f"##### {column_name}")
selected_model = st.selectbox(f"Select Model for {column_name}", list(model_links.keys()), key=f"{column_name}_model")
temp_values = st.slider(f'Select a temperature value for {column_name}', 0.0, 1.0, (0.5), key=f"{column_name}_temp")
option = st.selectbox(f"Select an option for {column_name}", ("Text", "Image", "Audio", "Video"), key=f"{column_name}_option")
if option == "Text":
text_input = st.text_input(f"Enter your text for {column_name}:", key=f"{column_name}_text")
if st.button(f"Process Text for {column_name}"):
if text_input:
process_text(st.session_state.current_user['name'], text_input, selected_model, temp_values)
# Clear the input after processing
st.session_state[f"{column_name}_text"] = ""
elif option == "Image":
text_input = st.text_input(f"Enter text prompt to use with Image context for {column_name}:", key=f"{column_name}_image_text")
uploaded_files = st.file_uploader(f"Upload images for {column_name}", type=["png", "jpg", "jpeg"], accept_multiple_files=True, key=f"{column_name}_image_upload")
if st.button(f"Process Image for {column_name}"):
for image_input in uploaded_files:
image_bytes = image_input.read()
process_image(st.session_state.current_user['name'], image_bytes, text_input)
# Clear the inputs after processing
st.session_state[f"{column_name}_image_text"] = ""
st.session_state[f"{column_name}_image_upload"] = None
elif option == "Audio":
text_input = st.text_input(f"Enter text prompt to use with Audio context for {column_name}:", key=f"{column_name}_audio_text")
uploaded_files = st.file_uploader(f"Upload an audio file for {column_name}", type=["mp3", "wav"], accept_multiple_files=True, key=f"{column_name}_audio_upload")
if st.button(f"Process Audio for {column_name}"):
for audio_input in uploaded_files:
process_audio(st.session_state.current_user['name'], audio_input, text_input)
# Clear the inputs after processing
st.session_state[f"{column_name}_audio_text"] = ""
st.session_state[f"{column_name}_audio_upload"] = None
elif option == "Video":
video_input = st.file_uploader(f"Upload a video file for {column_name}", type=["mp4"], key=f"{column_name}_video_upload")
text_input = st.text_input(f"Enter text prompt to use with Video context for {column_name}:", key=f"{column_name}_video_text")
if st.button(f"Process Video for {column_name}"):
if video_input and text_input:
process_video(st.session_state.current_user['name'], video_input, text_input)
# Clear the inputs after processing
st.session_state[f"{column_name}_video_text"] = ""
st.session_state[f"{column_name}_video_upload"] = None
# Main Streamlit app
st.title("Personalized Real-Time Chat")
# Sidebar
with st.sidebar:
st.title("User Info")
st.write(f"Current User: {st.session_state.current_user['name']}")
st.write(f"Browser: {st.session_state.current_user['browser']}")
new_name = st.text_input("Change your name:")
if st.button("Update Name"):
if new_name:
for user in st.session_state.users:
if user['id'] == st.session_state.current_user['id']:
user['name'] = new_name
st.session_state.current_user['name'] = new_name
save_data()
st.success(f"Name updated to {new_name}")
break
st.title("Active Users")
for user in st.session_state.users:
st.write(f"{user['name']} ({user['browser']})")
if st.button('Reset Chat'):
reset_conversation()
# Create two columns
col1, col2 = st.columns(2)
# Run main function for each column
with col1:
main_column("Column 1")
with col2:
main_column("Column 2")
# Run the Streamlit app
if __name__ == "__main__":
st.markdown("\n[by Aaron Wacker](https://huggingface.co/spaces/awacke1/).")