I want to write a python program with gradio that can let the LLM generate excel file with the process as follow:
1. LLM will generate the csv format text as user required
2. The program will detect the LLM generated output, then convert to excel format
3. Have a convert to excel button that can let the user to choose to convert the llm output to excel file if he want

My program:
"
import os
import time
import pandas as pd
import re
from io import StringIO
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer, BitsAndBytesConfig
import gradio as gr
from threading import Thread
from PyPDF2 import PdfReader  # Import PyPDF2 for PDF text extraction

MODEL_PATH = r"C:\Users\shermanhonsf\Desktop\Python\llama_develop\model\Llama3.1-8B-Instruct-HF-Model"  # Your model path

TITLE = """
Chat with Llama 3.2 1B
"""

PLACEHOLDER = """
Hi, I'm Llama. Ask me anything or upload a PDF to chat about its content.
"""

CSS = """
.duplicate-button {
    margin: auto !important;
    color: white !important;
    background: black !important;
    border-radius: 100vh !important;
}
h3 {
    text-align: center;
}
"""

# Set the device to "cuda" if you have a compatible GPU, otherwise "cpu"
device = "cuda" if torch.cuda.is_available() else "cpu"

# Configure quantization if needed (optional)
quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4"
)

# Load the tokenizer and model from the local directory
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)
model = AutoModelForCausalLM.from_pretrained(
    MODEL_PATH,
    torch_dtype=torch.bfloat16,
    device_map="auto",
    quantization_config=quantization_config
)

# Move the model to the appropriate device
model.to(device)


def extract_pdf_text(pdf_path):
    """Extract all text from a PDF file."""
    try:
        reader = PdfReader(pdf_path)
        text = ""
        for page in reader.pages:
            text += page.extract_text()
        return text
    except Exception as e:
        return f"Error reading PDF: {e}"

# Function to extract CSV data from LLM output
def extract_csv_data(llm_output):
    # Define start and end markers for the CSV content
    start_marker = "#START_CSV"
    end_marker = "#END_CSV"
    
    # Locate the markers in the LLM output
    start_index = llm_output.find(start_marker)
    end_index = llm_output.find(end_marker)

    if start_index == -1 or end_index == -1:
        print("CSV markers not found in the output.")
        return None
    
    # Extract the CSV content between the markers
    csv_content = llm_output[start_index + len(start_marker):end_index].strip()
    return csv_content

# Function to convert the extracted CSV data to an Excel file
def convert_to_excel(llm_output):
    # Extract the CSV data from the LLM output
    csv_data = extract_csv_data(llm_output)
    
    # If no CSV data is found, return or handle the error
    if not csv_data:
        print("No CSV data found.")
        return None  # Return None instead of a string message

    # Convert the CSV data into a Pandas DataFrame
    try:
        df = pd.read_csv(StringIO(csv_data), sep=",")
        # Save the DataFrame to an Excel file
        output_file = 'llm_output.xlsx'  # Use a consistent file name or generate a unique one if needed
        df.to_excel(output_file, index=False)
        return output_file  # Return the path to the generated file
    except Exception as e:
        print(f"Error converting to Excel: {e}")
        return None  # Return None in case of an error

# Function to handle the "Convert to Excel" button click
def handle_convert_to_excel(llm_output):
    if not llm_output.strip():
        return "No output to convert. Please generate something first.", None  # Return None for file path

    # Call the conversion function and return the result
    return "Excel file generated.", convert_to_excel(llm_output)  # Return a success message and the file path

def save_edited_excel(dataframe):
    try:
        # Save the edited DataFrame to an Excel file
        output_file = "edited_llm_output.xlsx"
        dataframe.to_excel(output_file, index=False)
        return output_file  # Return the path for downloading
    except Exception as e:
        print(f"Error saving edited Excel file: {e}")
        return None  # Return None in case of an error
    
# Function to handle editing and saving in the DataFrame
def handle_excel_editing(llm_output):
    # Convert the CSV data to a Pandas DataFrame
    csv_data = extract_csv_data(llm_output)
    if not csv_data:
        return "No CSV data found.", None, None  # Return default values

    try:
        df = pd.read_csv(StringIO(csv_data), sep=",")
        # Return the DataFrame to display in the editor
        return "Excel file ready for editing.", df, None
    except Exception as e:
        return f"Error converting to DataFrame: {e}", None, None
    
def stream_chat(message: str, history: list, system_prompt: str ="You are a helpful assistant. When asked to generate tabular data, please follow these rules:\n"
                    "1. Produce the specified data as a CSV table with a header row.\n"
                    "2. Ensure that fields in the CSV are separated by commas (',').\n"
                    "3. Enclose the CSV output with markers: \n"
                    "   - Begin with `#START_CSV`.\n"
                    "   - End with `#END_CSV`.\n",
                temperature: float = 0.8, max_new_tokens: int = 1024, top_p: float = 1.0, top_k: int = 20, penalty: float = 1.2, pdf_text: str = ""):
    print(f'message: {message}')
    print(f'history: {history}')
    print(f'system_prompt: {system_prompt}')
    print(f'temperature: {temperature}')
    print(f'max_new_tokens: {max_new_tokens}')
    print(f'top_p: {top_p}')
    print(f'top_k: {top_k}')
    print(f'penalty: {penalty}')
    print(f'Message: {message}')
    print(f'History: {history}')
    print(f'PDF Text: {pdf_text}')

    conversation = [
        {"role": "system", "content": system_prompt}
    ]

    # Add PDF content to the system prompt if provided
    if pdf_text.strip():
        pdf_summary = f"The user uploaded a PDF with the following content:\n{pdf_text[:2000]}...\n(This is a truncated summary if the text is too long.)"
        conversation.append({"role": "system", "content": pdf_summary})

    for prompt, answer in history:
        conversation.extend([
            {"role": "user", "content": prompt},
            {"role": "assistant", "content": answer},
        ])

    conversation.append({"role": "user", "content": message})

    # Prepare input IDs
    input_ids = tokenizer.apply_chat_template(
        conversation,
        # tokenize=True,
        add_generation_prompt=True,
        return_tensors="pt"
    ).to(device)

    streamer = TextIteratorStreamer(
        tokenizer,
        timeout=60.0,
        skip_prompt=True,
        skip_special_tokens=True
    )

    generate_kwargs = dict(
        input_ids=input_ids,
        max_new_tokens=max_new_tokens,
        do_sample=False if temperature == 0 else True,
        top_p=top_p,
        top_k=top_k,
        temperature=temperature,
        eos_token_id=[128001, 128008, 128009],
        streamer=streamer,
    )

    # Generate the response in a background thread
    with torch.no_grad():
        thread = Thread(target=model.generate, kwargs=generate_kwargs)
        thread.start()

    # Stream the response back to the user
    buffer = ""
    for new_text in streamer:
        buffer += new_text
        yield buffer

# Create the Gradio interface
chatbot = gr.Chatbot(height=600, show_copy_button=True, layout="panel", placeholder=PLACEHOLDER)

with gr.Blocks(css=CSS) as demo:
    gr.HTML(TITLE)
    
    # Add file uploader for PDF input
    pdf_file = gr.File(label="Upload PDF", type="filepath")
    pdf_textbox = gr.Textbox(label="Extracted PDF Text", placeholder="PDF content will appear here after upload.", lines=10, interactive=False, visible=False)
    
    def process_pdf(file):
        if file is not None:
            pdf_text = extract_pdf_text(file.name)
            return pdf_text
        return "No PDF uploaded."

    pdf_file.change(process_pdf, inputs=pdf_file, outputs=pdf_textbox)
    
    # Chatbot interface
    chatbot = gr.Chatbot(height=600, show_copy_button=True, layout="panel", placeholder=PLACEHOLDER)

    # Chat interface
    gr.ChatInterface(
        fn=stream_chat,
        chatbot=chatbot,
        fill_height=True,
        additional_inputs_accordion=gr.Accordion(
            label="⚙️ Parameters",
            open=False,
            render=False
        ),
        additional_inputs=[
            gr.Textbox(
                value="You are a helpful assistant. When asked to generate tabular data, please follow these rules:\n"
                    "1. Produce the specified data as a CSV table with a header row.\n"
                    "2. Ensure that fields in the CSV are separated by commas (',').\n"
                    "3. Enclose the CSV output with markers: \n"
                    "   - Begin with `#START_CSV`.\n"
                    "   - End with `#END_CSV`.\n",
                label="System Prompt",
                render=False,
            ),
            gr.Slider(
                minimum=0,
                maximum=1,
                step=0.1,
                value=0.8,
                label="Temperature",
                render=False,
            ),
            gr.Slider(
                minimum=128,
                maximum=8192,
                step=1,
                value=1024,
                label="Max new tokens",
                render=False,
            ),
            gr.Slider(
                minimum=0.0,
                maximum=1.0,
                step=0.1,
                value=1.0,
                label="Top_p",
                render=False,
            ),
            gr.Slider(
                minimum=1,
                maximum=20,
                step=1,
                value=20,
                label="Top_k",
                render=False,
            ),
            gr.Slider(
                minimum=0.0,
                maximum=2.0,
                step=0.1,
                value=1.2,
                label="Repetition penalty",
                render=False,
            ),
            pdf_textbox  # Pass the extracted PDF text as an additional input
        ],
        examples=[
            ["What are the main themes explored in the movie 'Dune', and how do they reflect contemporary issues?"],
            ["What are 3 practical techniques I can use to prioritize tasks more effectively in a busy work schedule?"],
            ["How does blockchain technology work, and what are its potential applications beyond cryptocurrencies?"],
            ["Show me a code snippet of a website's sticky header in CSS and JavaScript."],
        ],
        cache_examples=False,
    )

    llm_output_box = gr.Textbox(label="LLM Output", placeholder="Generated output will appear here.", lines=10, interactive=False, visible=False)
    
    # Add the "Convert to Excel" button
    convert_button = gr.Button("Convert to Excel")

    # Add a DataFrame for viewing and editing the Excel file
    excel_editor = gr.DataFrame(label="Excel Editor (Editable)", interactive=True)

    excel_status_box = gr.Textbox(label="Excel Conversion Status", placeholder="Excel conversion status will appear here.", interactive=False)

    excel_download_box = gr.File(label="Download Excel", interactive=False)
    
    # Connect the button to the conversion function
    convert_button.click(
        handle_excel_editing,
        inputs=llm_output_box,
        outputs=[excel_status_box, excel_editor, excel_download_box]
    )

    # Add functionality to save the modified DataFrame and download it
    excel_editor.change(
        save_edited_excel,
        inputs=excel_editor,
        outputs=excel_download_box
    )

    # Update the LLM output box when the chatbot generates a response
    def update_llm_output(history):
        print(f"Current history: {history}")  # Add this line to see the history content
        if history:
            print("update_llm_output function test: Have history")
            return history[-1][1]  # Return the assistant's latest response
        print("update_llm_output function test: No history")
        return ""  # Handle the case where there is no history

    chatbot.change(update_llm_output, inputs=chatbot, outputs=llm_output_box)

if __name__ == "__main__":
    demo.launch(debug=True)
"
However, sometimes the Excel Conversion Status is "Error converting to DataFrame: Error tokenizing data. C error: Expected 2 fields in line 11, saw 3", the llm generate something like:
"#START_CSV
Parameter,Value
Learning Rate Schedule,Warmup with linear schedule, then inverse square root schedule
Sequence Length,512
#END_CSV
"
Some row have 2 values and some have 3, it can't generate, fx it.Show the added or modified code in the correct order relative to the original code.