I am going to give you code that implements an LLM chatting app using streamlit.
I want you to add the following features: 

Implement buttons for:
- Cancelling an operation, right next to the "send" buttons
- When we hover over a message in the history, display 3 buttons to:
    - Copy the content of the message to the clipboard
    - Regenerate from that point
    - Edit the message, spawning a message box for editting the content.


Current working code:
```py
import streamlit as st
from gradio_client import Client
import json

# Set page configuration
st.set_page_config(
    page_title="LLM Chat Application",
    layout="wide",
    initial_sidebar_state="auto"
)


# Initialize session_state variables
if 'chat_history' not in st.session_state:
    st.session_state['chat_history'] = []

if 'chat_counter' not in st.session_state:
    st.session_state['chat_counter'] = 0

if 'user_prefix' not in st.session_state:
    st.session_state['user_prefix'] = "User: "

if 'assistant_prefix' not in st.session_state:
    st.session_state['assistant_prefix'] = "Assistant: "

if 'temperature' not in st.session_state:
    st.session_state['temperature'] = 1.0

if 'top_p' not in st.session_state:
    st.session_state['top_p'] = 1.0

if 'input' not in st.session_state:
    st.session_state['input'] = ''

# Sidebar for parameters
with st.sidebar:
    st.header("Parameters")
    st.session_state['temperature'] = st.slider('Temperature', 0.0, 5.0, st.session_state['temperature'])
    st.session_state['top_p'] = st.slider('Top-p (nucleus sampling)', 0.0, 1.0, st.session_state['top_p'])
    st.session_state['user_prefix'] = st.text_input('User prefix', st.session_state['user_prefix'])
    st.session_state['assistant_prefix'] = st.text_input('Assistant prefix', st.session_state['assistant_prefix'])
    st.write("## Export / Import Chat History")
    # Export chat history
    json_data = json.dumps(st.session_state['chat_history'], indent=4)
    st.download_button(label='Download Chat History', data=json_data, file_name='chat_history.json', mime='application/json')

    # Upload chat history
    uploaded_file = st.file_uploader("Upload Chat History (JSON)", type='json')
    if uploaded_file is not None:
        file_content = uploaded_file.read()
        st.session_state['chat_history'] = json.loads(file_content)
        st.experimental_rerun()

# Function to process user input
def process_input():
    user_input = st.session_state['input']
    if user_input.strip() != '':
        # Append user's message to chat history
        user_message = {'role': 'user', 'content': st.session_state['user_prefix'] + user_input}
        st.session_state['chat_history'].append(user_message)

        # Prepare chatbot history for the API
        # The 'chatbot' parameter expects a list of tuples (user message, assistant message)
        # We'll rebuild this from the session chat history
        chatbot_history = []
        for i in range(0, len(st.session_state['chat_history']), 2):
            user_msg = st.session_state['chat_history'][i]['content'] if i < len(st.session_state['chat_history']) else ''
            assistant_msg = st.session_state['chat_history'][i+1]['content'] if i+1 < len(st.session_state['chat_history']) else ''
            chatbot_history.append((user_msg, assistant_msg))

        # Use Gradio Client to send input and get response
        client = Client("yuntian-deng/ChatGPT4")
        response = client.predict(
            inputs=user_input,
            top_p=st.session_state['top_p'],
            temperature=st.session_state['temperature'],
            chat_counter=st.session_state['chat_counter'],
            chatbot=chatbot_history,
            api_name="/predict"
        )

        # Extract assistant's response
        assistant_response = response[0][-1][1]  # Get the last assistant message from the chatbot output

        # Append assistant's message to chat history
        assistant_message = {'role': 'assistant', 'content': st.session_state['assistant_prefix'] + assistant_response}
        st.session_state['chat_history'].append(assistant_message)
        # Update chat counter
        st.session_state['chat_counter'] += 1
        # Clear input box
        #st.session_state['input'] = '' # Exception: Not allowed to reset already instantiated text area
        # Rerun the app to reflect changes
        st.rerun()

# Main interface
st.title("LLM Chatbot")

# Container to hold chat history
message_container = st.container()

# Display chat messages
with message_container:
    for chat in st.session_state['chat_history']:
        if chat['role'] == 'user':
            st.markdown(
                f"""
                <div style='display: flex; justify-content: flex-start;'>
                    <div style='background-color:#2E7D32; color: white; padding:10px; border-radius:10px; margin:5px 0; display: inline-block; max-width: 80%; word-wrap:break-word;'>
                        {chat['content']}
                    </div>
                </div>
                """,
                unsafe_allow_html=True)
        else:
            st.markdown(
                f"""
                <div style='display: flex; justify-content: flex-end;'>
                    <div style='background-color:#1565C0; color: white; padding:10px; border-radius:10px; margin:5px 0; display: inline-block; max-width: 80%; word-wrap:break-word;'>
                        {chat['content']}
                    </div>
                </div>
                """,
                unsafe_allow_html=True)

# Function to handle Ctrl+Enter (not fully supported in Streamlit)
def on_input_change():
    pass  # Placeholder for any action when input changes (currently not used)

# Text area for user message
st.text_area("You:", value=st.session_state['input'], key='input', placeholder="Type your message here...", height=80, on_change=on_input_change)

# Send button
if st.button("Send", help="Click to send your message or press Ctrl+Enter"):
    process_input()

# Auto-scroll to the bottom
st.markdown("""
    <script>
    var chatBox = parent.document.getElementById('root');
    chatBox.scrollTop = chatBox.scrollHeight;
    </script>
    """, unsafe_allow_html=True)
```