My 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
Also have multiturn conversation and chat with pdf.
Read and study my code:
"
# Pdf, work

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.2-1B-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 the LLM output
def extract_csv_data(llm_output):
    # Find the start and end markers for the CSV content
    start_marker = "#START_CSV"
    end_marker = "#END_CSV"
    
    # Get the content between the markers
    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 data
    csv_content = llm_output[start_index + len(start_marker):end_index].strip()
    return csv_content

# Function to convert CSV data to an Excel file
def convert_to_excel(llm_output, output_file='output.xlsx'):
    csv_data = extract_csv_data(llm_output)
    
    # If there's no CSV data found, we could return or raise an error
    if not csv_data:
        print("No CSV data found.")
        return None

    # Read the CSV data into a DataFrame
    df = pd.read_csv(StringIO(csv_data), sep=",")

    # Save the DataFrame to an Excel file
    df.to_excel(output_file, index=False)
    return output_file  # Return the saved file path

# Gradio function to handle "Convert to Excel" button
def handle_excel_conversion(chat_history):
    # Get the latest LLM output from the chat history
    if not chat_history:
        return "No chat history found.", None

    # Extract the assistant's last message
    last_message = chat_history[-1][1]  # Assuming [(user_message, assistant_message), ...]

    # Convert to Excel
    excel_file_path = convert_to_excel(last_message)
    if excel_file_path:
        return "Excel file successfully created.", excel_file_path
    else:
        return "No valid CSV data found in the last LLM response.", 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)

# Gradio Blocks Interface (with your original gr.ChatInterface)
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)
    
    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)

    # Original gr.ChatInterface
    gr.ChatInterface(
        fn=stream_chat,  # Your original LLM function
        chatbot=gr.Chatbot(height=600, show_copy_button=True, layout="panel", placeholder=PLACEHOLDER),
        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 CSV format\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,
    )

    # Add Excel conversion functionality
    with gr.Row():
        convert_button = gr.Button("Convert to Excel")
        excel_status = gr.Textbox(label="Excel Status", interactive=False)  # Status message for Excel conversion
        excel_download = gr.File(label="Download Excel File")  # File download for the generated Excel

    # Connect the "Convert to Excel" button
    convert_button.click(
        handle_excel_conversion,
        inputs=[gr.State([])],  # Pass the internal chat history
        outputs=[excel_status, excel_download],
    )

if __name__ == "__main__":
    demo.launch(debug=True)
"
There is a bug: When message: generate 10 rows of sample sales data with random dates in January 2023, product names as 'Product A', 'Product B', etc., and random quantities and revenue values
history: [['hi', 'How can I assist you today?']]
system_prompt: You are a helpful assistant. When asked to generate tabular data, please follow these rules:
1. Produce the specified data as CSV format
2. Ensure that fields in the CSV are separated by commas (',').
3. Enclose the CSV output with markers: 
   - Begin with `#START_CSV`.
   - End with `#END_CSV`.

temperature: 0.8
max_new_tokens: 1024
top_p: 1
top_k: 20
penalty: 1.2
PDF Text:
I then click Convert To Excel, the Excel Status: No Chat history found. Please fix the problem. Check handle_excel_conversion() specifically, this function is strange.