Tolong sambungkan Chatbotnya dengan AI saya.
Jadi ada yang sebagai agent AI Audit professional. Silahkan tuliskan prompt selengkap-lengkapnya! ini beda dengan ai yang mengambil data di pdf. Pokoknya 2 AI tidak bertabrakan ya!
Kemudian sambungkan ke html agar bisa berinteraksi di html saya. Buatlah seperti professional senior programmer.
Berikut adalah filenya
index.html
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chatbot Pemrosesan PDF</title>
    <!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- Font Awesome untuk ikon -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        body {
            background-color: #f8f9fa;
            padding-top: 60px;
        }
        #loading {
            display: none;
        }
        #chat-section {
            display: none;
        }
        .chat-box {
            height: 400px;
            overflow-y: auto;
            border: 1px solid #dee2e6;
            border-radius: 5px;
            padding: 15px;
            background-color: #ffffff;
        }
        .message {
            margin-bottom: 15px;
        }
        .message.user .message-content {
            background-color: #0d6efd;
            color: #fff;
            text-align: right;
        }
        .message.ai .message-content {
            background-color: #e9ecef;
            text-align: left;
        }
        .message-content {
            display: inline-block;
            padding: 10px 15px;
            border-radius: 20px;
            max-width: 75%;
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- Upload Section -->
        <div id="upload-section" class="card shadow-sm">
            <div class="card-body">
                <h3 class="card-title text-center mb-4">Unggah File PDF untuk Diproses</h3>
                <form id="upload-form">
                    <div class="mb-3">
                        <label for="pdfFile" class="form-label"><i class="fa fa-file-pdf text-danger"></i> Pilih File PDF:</label>
                        <input type="file" class="form-control" id="pdfFile" name="pdfFile" accept="application/pdf" required>
                    </div>
                    <button type="submit" class="btn btn-primary w-100"><i class="fa fa-upload"></i> Unggah dan Proses</button>
                </form>
                <div id="loading" class="text-center mt-4">
                    <div class="spinner-border text-primary" role="status">
                        <span class="visually-hidden">Memproses...</span>
                    </div>
                    <p class="mt-2">File Anda sedang diproses, harap tunggu...</p>
                </div>
                <div id="upload-error" class="alert alert-danger mt-3 d-none" role="alert">
                    Terjadi kesalahan saat memproses file.
                </div>
            </div>
        </div>

        <!-- Chat Section -->
        <div id="chat-section" class="card shadow-sm mt-4">
            <div class="card-header bg-primary text-white">
                <h5 class="mb-0">Chat dengan AI</h5>
            </div>
            <div class="card-body chat-box" id="chat-box">
                <!-- Pesan akan muncul di sini -->
            </div>
            <div class="card-footer">
                <form id="chat-form" class="d-flex">
                    <input type="text" class="form-control me-2" id="user-input" placeholder="Tulis pesan Anda..." required>
                    <button type="submit" class="btn btn-primary"><i class="fa fa-paper-plane"></i> Kirim</button>
                </form>
            </div>
        </div>
    </div>

    <!-- Bootstrap 5 JS dan dependensi -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <!-- jQuery (optional, jika ingin tetap menggunakannya) -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        $(document).ready(function(){
            // Handler untuk form upload
            $('#upload-form').on('submit', function(e){
                e.preventDefault();
                var formData = new FormData();
                var fileInput = $('#pdfFile')[0];
                if (fileInput.files.length === 0) {
                    alert("Silakan pilih file PDF.");
                    return;
                }
                formData.append('file', fileInput.files[0]);

                $('#loading').show();
                $('#upload-error').addClass('d-none');

                $.ajax({
                    url: '/upload', // Ganti dengan endpoint backend Anda
                    type: 'POST',
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function(response){
                        $('#loading').hide();
                        if (response.status === "success") {
                            // Tampilkan antarmuka chat
                            $('#upload-section').slideUp();
                            $('#chat-section').slideDown();
                            // Anda bisa menyimpan token atau data lain dari response jika diperlukan
                        } else {
                            $('#upload-error').removeClass('d-none').text("Error: " + response.message);
                        }
                    },
                    error: function(xhr, status, error){
                        $('#loading').hide();
                        $('#upload-error').removeClass('d-none').text("Terjadi kesalahan saat memproses file.");
                    }
                });
            });

            // Handler untuk form chat
            $('#chat-form').on('submit', function(e){
                e.preventDefault();
                var userInput = $('#user-input').val().trim();
                if (userInput === "") return;

                // Tampilkan pesan pengguna
                appendMessage('user', userInput);
                $('#user-input').val('');

                // Scroll ke bawah
                scrollChatToBottom();

                // Kirim ke backend
                $.ajax({
                    url: '/chat', // Ganti dengan endpoint backend Anda
                    type: 'POST',
                    contentType: 'application/json',
                    data: JSON.stringify({ message: userInput }),
                    success: function(response){
                        if (response.status === "success") {
                            appendMessage('ai', response.reply);
                            scrollChatToBottom();
                        } else {
                            appendMessage('ai', "Maaf, terjadi kesalahan: " + response.message);
                            scrollChatToBottom();
                        }
                    },
                    error: function(xhr, status, error){
                        appendMessage('ai', "Maaf, terjadi kesalahan saat mengirim pesan.");
                        scrollChatToBottom();
                    }
                });
            });

            // Fungsi untuk menambahkan pesan ke chat
            function appendMessage(sender, message) {
                var messageClass = sender === 'user' ? 'user' : 'ai';
                var messageElement = `
                    <div class="message ${messageClass}">
                        <div class="message-content">
                            ${escapeHtml(message)}
                        </div>
                    </div>
                `;
                $('#chat-box').append(messageElement);
            }

            // Fungsi untuk scroll ke bawah
            function scrollChatToBottom() {
                var chatBox = $('#chat-box');
                chatBox.scrollTop(chatBox[0].scrollHeight);
            }

            // Fungsi untuk mencegah XSS
            function escapeHtml(text) {
                var map = {
                    '&': '&amp;',
                    '<': '&lt;',
                    '>': '&gt;',
                    '"': '&quot;',
                    "'": '&#039;'
                };
                return text.replace(/[&<>"']/g, function(m) { return map[m]; });
            }
        });
    </script>
</body>
</html>

main.py
# main.py
import pandas as pd
import fitz
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse, HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
import math
import numpy as np

from lib.Chatbot import Chatbot
from lib.audit.Beneish import BeneishMScoreCalculator

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

def remove_nan_and_inf(obj):
    if isinstance(obj, dict):
        return {k: remove_nan_and_inf(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [remove_nan_and_inf(x) for x in obj]
    elif isinstance(obj, float):
        if math.isnan(obj) or math.isinf(obj):
            return None
        else:
            return obj
    else:
        return obj

@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
    try:
        contents = await file.read()
        with fitz.open(stream=contents, filetype="pdf") as doc:
            text = ""
            for page in doc:
                text += page.get_text()
        print(text)
        chatbot = Chatbot()
        financial_data = chatbot.extract_financial_data(text)
        df = pd.DataFrame(financial_data)

        required_columns = ["Year", "Net Receivables", "Sales", "Cost of Goods Sold",
                            "Current Assets", "PPE", "Net PPE", "Securities", "Total Assets",
                            "Depreciation Expense", "SG&A Expenses", "Total Debt",
                            "Income from Continuing Operations", "Cash from Operations"]
        for col in required_columns:
            if col not in df.columns:
                df[col] = 0

        df.replace([np.inf, -np.inf], np.nan, inplace=True)
        df.fillna(0, inplace=True)

        calculator = BeneishMScoreCalculator(df)

        results = calculator.get_results()

        if isinstance(results, pd.DataFrame):
            results = results.to_dict(orient='records')
        elif isinstance(results, pd.Series):
            results = results.to_dict()
        elif isinstance(results, dict):
            for key, value in results.items():
                if isinstance(value, (pd.DataFrame, pd.Series)):
                    results[key] = value.to_dict(orient='records' if isinstance(value, pd.DataFrame) else 'dict')

        results = remove_nan_and_inf(results)

        return JSONResponse(content={"status": "success", "results": results})
    except Exception as e:
        return JSONResponse(content={"status": "error", "message": str(e)})


@app.get("/")
async def read_index():
    with open("static/index.html", "r") as f:
        html_content = f.read()
    return HTMLResponse(content=html_content)


if __name__ == "__main__":
    import uvicorn

    uvicorn.run("main:app", host="localhost", port=1010, reload=True)

Chatbot.py
import json

from llama_cpp import Llama


class Chatbot:
    def __init__(self, n_gpu_layers=-1, n_ctx=4096, verbose=True):
        self.model_path = (
            "./model/llm/14b/aidit-14b-instruct-q4_k_m-00001-of-00003.gguf"
        )
        self.llm = Llama(
            model_path=self.model_path,
            n_gpu_layers=n_gpu_layers,
            n_ctx=n_ctx,
            verbose=verbose,
        )
        self.system_prompt = """
        You are a specialized chatbot designed to convert financial data from PDF documents into a structured JSON format. Your primary function is to extract relevant information from the provided PDF content and organize it into the following JSON structure:
            {
                "Year":<array of years>,
                "Net Receivables": <array of net receivables>,
                "Sales": <array of sales>,
                "Cost of Goods Sold": <array of cost of goods sold>,
                "Current Assets": <array of current assets>,
                "PPE": <array of PPE>,
                "Net PPE": <array of net PPE>,
                "Securities": <array of securities, if not available, use 0>,
                "Total Assets": <array of total assets>,
                "Depreciation Expense": <array of depreciation expenses>,
                "SG&A Expenses": <array of SG&A expenses>,
                "Total Debt": <array of total debt>,
                "Income from Continuing Operations": <array of income from continuing operations>,
                "Cash from Operations": <array of cash from operations>
            }
        Write ONLY JSON data without codeblock to the standard output. The JSON data should be formatted as a single line without any leading or trailing whitespaces.
        write "END" in new line to finish the conversation.
        """

    def extract_financial_data(self, text):
        output = self.llm.create_completion(
            prompt=f"{self.system_prompt}\n{text}",
            echo=False,
            max_tokens=None,
            stream=True,
            stop=["END"],
        )

        answer = ""
        for item in output:
            answer += item["choices"][0]["text"]
            print(item["choices"][0]["text"], end="")
        return json.loads(answer)

Tuliskan selengkap-lengkapnya ya! Pada index.html saat extract data, tampilkan output secara realtime juga di htmlnya.
Untuk Chatbot, tampilkan chat juga secara realtime. Tambahkan juga tombol hapus, restart, dan hapus semua sesi ya.
Selain itu tambahkan juga history chat!
Pastikan juga chatnya tersimpan di storage ya! Tuliskan seprofessional mungkin chatbot saya, jadi fitur chatbot saya lengkap ya!!! dengan tampilan modern. Pokoknya buatkan fitur selengkap-lengkapnya seperti senior programmer!
Tambahkan juga settings ya, seperti pengaturan dark/light mode! Tuliskan lengkap tanpa terpotong apapun kodingannya sehingga memudahkan saya dalam mengcopynya!