Please ensure this gui isnt making unnecessary API calls and add an api call counter. also check that it's extracting code correctly for the extracted code box.import sys
import re
import threading
import io
import contextlib
from PyQt6.QtCore import QObject, QThread, pyqtSignal, Qt, QTimer
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit,
    QPushButton, QSlider, QTextEdit, QProgressBar, QMessageBox
)
from gradio_client import Client


class Worker(QObject):
    finished = pyqtSignal()  # Signal to indicate the worker is done
    result_ready = pyqtSignal(str, str)  # Signal to send the response and code back to the GUI
    execution_result = pyqtSignal(str)  # Signal to send the execution result back
    save_code_finished = pyqtSignal()  # Signal to indicate code saving is finished

    def __init__(self, client, inputs, top_p, temperature, retries=5):
        super().__init__()
        self.client = client
        self.inputs = inputs
        self.top_p = top_p
        self.temperature = temperature
        self.retries = retries

    def run(self):
        try_count = 0
        while try_count < self.retries:
            try:
                result = self.client.predict(
                    inputs=self.inputs,
                    top_p=self.top_p,
                    temperature=self.temperature,
                    chat_counter=0,
                    chatbot=[],
                    api_name="/predict"
                )

                # Extract the relevant parts of the response
                response_text = result[0][0][1]
                code = self.extract_code(response_text)

                # Emit the result to the main thread
                self.result_ready.emit(response_text, code)

                # Execute the code and capture the output or any errors
                exec_output = self.execute_code(code)
                self.execution_result.emit(exec_output)

                if "error" not in exec_output.lower():
                    break  
            except Exception as e:
                self.result_ready.emit(f"Error: {str(e)}", "")
                self.execution_result.emit(f"Execution error: {str(e)}")

            try_count += 1

        self.finished.emit()

    def extract_code(self, text):
        pattern = r'```python(.*?)```'
        matches = re.findall(pattern, text, re.DOTALL)
        if matches:
            return "\n".join(match.strip() for match in matches)
        else:
            return "No code found."

    def execute_code(self, code):
        if code == "No code found.":
            return "No code to execute."

        try:
            output = io.StringIO()
            with contextlib.redirect_stdout(output):
                exec(code, globals())
            return output.getvalue() if output.getvalue() else "Code executed without output."
        except Exception as e:
            return f"Execution error: {str(e)}"

    def save_code(self, filename, code):
        try:
            with open(filename, 'w') as f:
                f.write(code)
            self.save_code_finished.emit()
        except Exception as e:
            return f"Failed to save code: {str(e)}"


class ChatGPTApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("ChatGPT API Interface")
        self.resize(800, 600)

        self.client = Client("yuntian-deng/ChatGPT")
        self.initUI()

    def initUI(self):
        layout = QVBoxLayout()

        self.input_field = QLineEdit(self)
        self.input_field.setPlaceholderText("Type your message here...")
        layout.addWidget(self.input_field)

        # Top-p slider
        self.top_p_slider = QSlider(Qt.Orientation.Horizontal)
        self.top_p_slider.setRange(0, 100)
        self.top_p_slider.setValue(100)
        layout.addWidget(QLabel("Top-p (nucleus sampling):"))
        layout.addWidget(self.top_p_slider)

        # Temperature slider
        self.temperature_slider = QSlider(Qt.Orientation.Horizontal)
        self.temperature_slider.setRange(0, 100)
        self.temperature_slider.setValue(100)
        layout.addWidget(QLabel("Temperature:"))
        layout.addWidget(self.temperature_slider)

        # Filename input field
        self.filename_field = QLineEdit(self)
        self.filename_field.setPlaceholderText("Enter filename to save code (e.g., output.py)")
        layout.addWidget(self.filename_field)

        # Prediction button
        self.predict_button = QPushButton("Predict", self)
        self.predict_button.clicked.connect(self.start_prediction_thread)
        layout.addWidget(self.predict_button)

        # Save and execute button
        self.save_execute_button = QPushButton("Save and Execute Code", self)
        self.save_execute_button.clicked.connect(self.save_and_execute_code)
        layout.addWidget(self.save_execute_button)

        # Response, Code, and Execution result areas
        self.response_area = QTextEdit(self)
        self.response_area.setReadOnly(True)
        layout.addWidget(QLabel("Response:"))
        layout.addWidget(self.response_area)

        self.code_area = QTextEdit(self)
        self.code_area.setReadOnly(True)
        layout.addWidget(QLabel("Extracted Code:"))
        layout.addWidget(self.code_area)

        self.execution_area = QTextEdit(self)
        self.execution_area.setReadOnly(True)
        layout.addWidget(QLabel("Execution Result:"))
        layout.addWidget(self.execution_area)

        self.debug_console = QTextEdit(self)
        self.debug_console.setReadOnly(True)
        layout.addWidget(QLabel("Debug Console:"))
        layout.addWidget(self.debug_console)

        # Progress bar and flashing light
        self.progress_bar = QProgressBar(self)
        self.progress_bar.setRange(0, 0)  # Indeterminate
        layout.addWidget(self.progress_bar)
        
        self.flashing_lights_label = QLabel("Processing...")
        layout.addWidget(self.flashing_lights_label)
        self.flashing_lights_timer = QTimer()
        self.flashing_lights_timer.timeout.connect(self.toggle_flashing_lights)
        self.flashing_lights_timer.start(500)

        self.setLayout(layout)

    def toggle_flashing_lights(self):
        current_color = self.flashing_lights_label.styleSheet()
        if current_color == "color: red;":
            self.flashing_lights_label.setStyleSheet("color: black;")
        else:
            self.flashing_lights_label.setStyleSheet("color: red;")

    def start_prediction_thread(self):
        inputs = self.input_field.text()
        top_p = self.top_p_slider.value() / 100.0
        temperature = self.temperature_slider.value() / 100.0

        self.thread = QThread()
        self.worker = Worker(self.client, inputs, top_p, temperature)

        self.worker.moveToThread(self.thread)

        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.worker.result_ready.connect(self.update_ui)
        self.worker.execution_result.connect(self.update_execution_result)

        self.progress_bar.setValue(0)  # Start the progress bar
        self.progress_bar.setRange(0, 0)  # Indeterminate mode
        self.flashing_lights_timer.start()  # Start flashing light indicator
        self.thread.start()

    def save_and_execute_code(self):
        code = self.code_area.toPlainText()
        filename = self.filename_field.text()
        
        if not filename:
            QMessageBox.warning(self, "Warning", "Please enter a filename to save the code.")
            return
        
        self.thread = QThread()
        self.worker = Worker(self.client, '', 0, 0)  # Dummy input, we don't need prediction here
        self.worker.moveToThread(self.thread)

        self.thread.started.connect(lambda: self.worker.save_code(filename, code))
        self.worker.save_code_finished.connect(self.on_code_saved)

        self.thread.finished.connect(self.thread.deleteLater)
        self.worker.finished.connect(self.worker.deleteLater)

        self.thread.start()

    def on_code_saved(self):
        QMessageBox.information(self, "Success", "Code saved successfully!")
        
    def update_ui(self, response_text, code):
        self.response_area.setPlainText(response_text)
        self.code_area.setPlainText(code)
        self.debug_console.append(f"New code received:\n{code}\n")
        self.progress_bar.setRange(0, 1)  # Stop indeterminate progress
        self.flashing_lights_timer.stop()

    def update_execution_result(self, execution_result):
        self.execution_area.setPlainText(execution_result)
        self.debug_console.append(f"Execution result:\n{execution_result}\n")
        self.progress_bar.setRange(0, 1)  # Stop indeterminate progress
        self.flashing_lights_timer.stop()

    def reset_textbox(self):
        self.input_field.clear()
        self.response_area.clear()
        self.code_area.clear()
        self.execution_area.clear()
        self.debug_console.clear()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = ChatGPTApp()
    window.show()
    sys.exit(app.exec())