Further enhance and add more response AI tokens with a more natural gradio UI:

```
import g4f
from g4f.client import Client
import logging
import pyperclip  # Library to copy text to the clipboard
import os
import json
from typing import List, Dict
import uuid
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer
import asyncio
import gradio as gr

# Set up logging with rotation
from logging.handlers import RotatingFileHandler

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = RotatingFileHandler("assistant.log", maxBytes=5*1024*1024, backupCount=2)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

# Initialize the GPT client with the desired provider
client = Client(provider=g4f.Provider.Blackbox)

# File paths for conversation history, memory, and FAISS index
CONVERSATION_LOG_FILE = "conversation_log.json"
MEMORY_FILE = "memory.json"
FAISS_INDEX_FILE = "faiss.index"
MEMORY_METADATA_FILE = "memory_metadata.json"

# Initialize conversation history
messages: List[Dict[str, str]] = []

# Initialize memory
memory: Dict[str, List[Dict[str, str]]] = {}

# Initialize reasoning state
reasoning_state: Dict[str, Dict] = {}

# Initialize Sentence Transformer Model for Embeddings
embedding_model = SentenceTransformer('all-MiniLM-L6-v2')  # Lightweight and effective

# Initialize FAISS Index
def initialize_faiss():
    # Dimension should match the embedding model
    dimension = 384  # 'all-MiniLM-L6-v2' has 384-dimensional embeddings
    if os.path.exists(FAISS_INDEX_FILE):
        try:
            index = faiss.read_index(FAISS_INDEX_FILE)
            logger.info("FAISS index loaded.")
        except Exception as e:
            logger.error(f"Failed to load FAISS index: {e}")
            index = faiss.IndexFlatL2(dimension)
            logger.info("Initialized new FAISS index.")
    else:
        index = faiss.IndexFlatL2(dimension)
        logger.info("FAISS index initialized.")
    return index

faiss_index = initialize_faiss()

# Initialize Metadata Storage
def load_memory_metadata():
    if os.path.exists(MEMORY_METADATA_FILE):
        try:
            with open(MEMORY_METADATA_FILE, "r") as meta_file:
                metadata = json.load(meta_file)
            logger.info("Memory metadata loaded.")
        except Exception as e:
            logger.error(f"Failed to load memory metadata: {e}")
            metadata = []
    else:
        metadata = []
        logger.info("Memory metadata initialized.")
    return metadata

def save_memory_metadata():
    try:
        with open(MEMORY_METADATA_FILE, "w") as meta_file:
            json.dump(memory_metadata, meta_file, indent=4)
        logger.info("Memory metadata saved.")
    except Exception as e:
        logger.error(f"Failed to save memory metadata: {e}")

memory_metadata = load_memory_metadata()

# Load conversation history if it exists
def load_conversation_history():
    global messages
    if os.path.exists(CONVERSATION_LOG_FILE):
        try:
            with open(CONVERSATION_LOG_FILE, "r") as log_file:
                messages = json.load(log_file)
            logger.info("Conversation history loaded.")
        except Exception as e:
            logger.error(f"Failed to load conversation history: {e}")
            messages = []
    else:
        # Add system prompt with chain-of-thought instructions for internal processing
        system_prompt = {
            "role": "system",
            "content": (
                "You are an advanced AI assistant with the ability to reason through problems step-by-step using chain-of-thought. "
                "Maintain context over extended conversations, recall past interactions, and utilize stored memories to provide coherent and contextually relevant responses."
            ),
        }
        messages.append(system_prompt)
        logger.info("System prompt initialized.")

# Save conversation history
def save_conversation_history():
    try:
        with open(CONVERSATION_LOG_FILE, "w") as log_file:
            json.dump(messages, log_file, indent=4)
        logger.info("Conversation history saved.")
    except Exception as e:
        logger.error(f"Failed to save conversation history: {e}")

# Load memory if it exists
def load_memory():
    global memory, memory_metadata
    if os.path.exists(MEMORY_FILE):
        try:
            with open(MEMORY_FILE, "r") as memory_file:
                memory = json.load(memory_file)
            logger.info("Memory loaded.")
        except Exception as e:
            logger.error(f"Failed to load memory: {e}")
            memory = {}
    else:
        memory = {}
        logger.info("Memory initialized.")
    
    # memory_metadata already loaded above

# Save memory
def save_memory():
    try:
        with open(MEMORY_FILE, "w") as memory_file:
            json.dump(memory, memory_file, indent=4)
        logger.info("Memory saved.")
        save_memory_metadata()
    except Exception as e:
        logger.error(f"Failed to save memory: {e}")

# Function to add memory to FAISS
def add_memory_to_faiss(text: str, metadata: Dict):
    try:
        embedding = embedding_model.encode(text)
        faiss_index.add(np.array([embedding]).astype('float32'))
        faiss.write_index(faiss_index, FAISS_INDEX_FILE)
        memory_metadata.append(metadata)
        save_memory_metadata()
        logger.info("Memory added to FAISS index.")
    except Exception as e:
        logger.error(f"Failed to add memory to FAISS: {e}")

# Function to retrieve relevant memories based on user input
def retrieve_relevant_memories(user_input: str, top_k: int = 5) -> List[Dict]:
    try:
        embedding = embedding_model.encode(user_input)
        embedding = np.array([embedding]).astype('float32')
        distances, indices = faiss_index.search(embedding, top_k)
        relevant = []
        for idx in indices[0]:
            if idx < len(memory_metadata):
                relevant.append(memory_metadata[idx])
            else:
                logger.warning(f"FAISS returned out-of-bounds index: {idx}")
        return relevant
    except Exception as e:
        logger.error(f"Failed to retrieve relevant memories: {e}")
        return []

# Function to extract code blocks from text
def extract_code_block(text: str) -> str:
    """
    Extracts the code block from a text enclosed in triple backticks.
    """
    if "```" in text:
        parts = text.split("```")
        if len(parts) >= 3:
            # Case with language specified, e.g., ```python
            return parts[2].strip()
        elif len(parts) == 2:
            # Case without language specification
            return parts[1].strip()
    return ""

# Function to retrieve the last memory entry of a specific type
def retrieve_last_memory_of_type(memory_type: str) -> str:
    """
    Retrieves the last memory entry of a specific type ('code', 'explanation', etc.).
    """
    # Iterate in reverse to find the latest relevant memory
    for entry in reversed(memory_metadata):
        text_lower = entry["text"].lower()
        if memory_type == "code" and "code" in text_lower:
            # Extract code block
            code_block = extract_code_block(entry["text"])
            if code_block:
                return code_block
        elif memory_type == "explanation" and "explanation" in text_lower:
            return entry["text"]
    return ""

# Update memory based on the latest assistant response
def update_memory(user_input: str, assistant_response: str):
    # Combine user input and assistant response for context
    combined_text = f"User: {user_input}\nAssistant: {assistant_response}"
    # Add to memory dictionary
    timestamp = uuid.uuid4().hex  # Unique identifier
    if timestamp not in memory:
        memory[timestamp] = []
    memory[timestamp].append({
        "user": user_input,
        "assistant": assistant_response
    })
    # Add to FAISS index
    add_memory_to_faiss(combined_text, {
        "id": timestamp,
        "text": combined_text
    })
    logger.info("Memory updated with semantic embeddings.")

# Initialize conversation and memory
load_conversation_history()
load_memory()

print("Welcome to the Enhanced Coding Assistant with Advanced Reasoning and Long-Term Memory! Type 'exit' to end the conversation.")

def copy_code_to_clipboard(assistant_response: str):
    """Extracts and copies the generated code to the clipboard."""
    try:
        parts = assistant_response.split("```")
        if len(parts) >= 3:
            # Case with language specified, e.g., ```python
            code_block = parts[2].strip()
        elif len(parts) == 2:
            # Case without language specification
            code_block = parts[1].strip()
        else:
            # No code block detected
            print("No code block detected in the response.")
            return
        pyperclip.copy(code_block)
        print("Code copied to clipboard!")
    except Exception as e:
        logger.error(f"Error while copying code to clipboard: {e}", exc_info=True)
        print("An error occurred while trying to copy the code.")

# Define AI Worker classes
class AIWorker:
    """Base class for AI Workers."""
    def __init__(self, name: str):
        self.name = name

    async def process(self, task: Dict, context: Dict) -> Dict:
        raise NotImplementedError("Each AIWorker must implement the process method.")

class CodeGeneratorWorker(AIWorker):
    """AI Worker specialized in generating code."""
    def __init__(self):
        super().__init__("CodeGenerator")

    async def process(self, task: Dict, context: Dict) -> Dict:
        try:
            user_input = task.get("input")
            prompt = (
                "You will be provided with a user request. "
                "Please generate the corresponding Python code within triple backticks. "
                "Use chain-of-thought internally to reason through the problem, "
                "but only output the final code without any explanations."
            )
            full_prompt = f"{prompt}\n\nUser Request: {user_input}"
            logger.info(f"{self.name} processing task: {user_input}")
            response = client.chat.completions.create(
                messages=[
                    {"role": "system", "content": full_prompt}
                ],
                model=g4f.models.default,
                # Adjust parameters to allow longer responses
                max_tokens=1500,  # Increased token limit
                temperature=0.7,  # Adjust temperature for creativity
            )
            if response.choices and len(response.choices) > 0:
                code = response.choices[0].message.content
                # Ensure only code block is returned
                code = extract_code_block(code)
            else:
                code = "I'm sorry, but I couldn't generate the code at this time."
                logger.warning("CodeGeneratorWorker: GPT response choices are empty.")
            return {"code": code}
        except Exception as e:
            logger.error(f"{self.name} encountered an error: {e}", exc_info=True)
            return {"code": "An error occurred while generating the code."}

class ExplanationWorker(AIWorker):
    """AI Worker specialized in explaining code or concepts."""
    def __init__(self):
        super().__init__("Explanation")

    async def process(self, task: Dict, context: Dict) -> Dict:
        try:
            code = task.get("code")
            if not code:
                return {"explanation": "No code provided for explanation."}
            prompt = (
                "You are provided with a Python code snippet. "
                "Use your chain-of-thought reasoning to understand the code, "
                "but only provide a concise explanation without including any internal reasoning steps."
            )
            full_prompt = f"{prompt}\n\nCode:\n```python\n{code}\n```"
            logger.info(f"{self.name} processing task.")
            response = client.chat.completions.create(
                messages=[
                    {"role": "system", "content": full_prompt}
                ],
                model=g4f.models.default,
                max_tokens=1000,  # Increased token limit
                temperature=0.7,
            )
            if response.choices and len(response.choices) > 0:
                explanation = response.choices[0].message.content
            else:
                explanation = "I'm sorry, but I couldn't provide an explanation at this time."
                logger.warning("ExplanationWorker: GPT response choices are empty.")
            return {"explanation": explanation}
        except Exception as e:
            logger.error(f"{self.name} encountered an error: {e}", exc_info=True)
            return {"explanation": "An error occurred while explaining the code."}

class DataRetrievalWorker(AIWorker):
    """AI Worker specialized in retrieving data or information."""
    def __init__(self):
        super().__init__("DataRetrieval")

    async def process(self, task: Dict, context: Dict) -> Dict:
        try:
            query = task.get("query")
            prompt = (
                "You are provided with a data retrieval request. "
                "Use your chain-of-thought reasoning to understand and process the request, "
                "but only provide the final detailed information without any internal explanations."
            )
            full_prompt = f"{prompt}\n\nData Request: {query}"
            logger.info(f"{self.name} processing task: {query}")
            response = client.chat.completions.create(
                messages=[
                    {"role": "system", "content": full_prompt}
                ],
                model=g4f.models.default,
                max_tokens=1500,  # Increased token limit
                temperature=0.7,
            )
            if response.choices and len(response.choices) > 0:
                data = response.choices[0].message.content
            else:
                data = "I'm sorry, but I couldn't retrieve the data at this time."
                logger.warning("DataRetrievalWorker: GPT response choices are empty.")
            return {"data": data}
        except Exception as e:
            logger.error(f"{self.name} encountered an error: {e}", exc_info=True)
            return {"data": "An error occurred while retrieving the data."}

class PruningWorker(AIWorker):
    """AI Worker specialized in code optimization."""
    def __init__(self):
        super().__init__("Pruning")

    async def process(self, task: Dict, context: Dict) -> Dict:
        try:
            code = task.get("code")
            if not code:
                return {"optimized_code": "No code provided for optimization."}
            prompt = (
                "You are provided with a Python code snippet that uses the Quicksort algorithm. "
                "Use your chain-of-thought reasoning to analyze and optimize the code for better performance. "
                "Provide only the optimized code within triple backticks without any additional explanations."
            )
            full_prompt = f"{prompt}\n\nOriginal Code:\n```python\n{code}\n```"
            logger.info(f"{self.name} processing task.")
            response = client.chat.completions.create(
                messages=[
                    {"role": "system", "content": full_prompt}
                ],
                model=g4f.models.default,
                max_tokens=1500,  # Increased token limit
                temperature=0.7,
            )
            if response.choices and len(response.choices) > 0:
                optimized_code = response.choices[0].message.content
                optimized_code = extract_code_block(optimized_code)
            else:
                optimized_code = "I'm sorry, but I couldn't optimize the code at this time."
                logger.warning("PruningWorker: GPT response choices are empty.")
            return {"optimized_code": optimized_code}
        except Exception as e:
            logger.error(f"{self.name} encountered an error: {e}", exc_info=True)
            return {"optimized_code": "An error occurred while optimizing the code."}

# Initialize AI Workers
ai_workers = {
    "code_generator": CodeGeneratorWorker(),
    "explanation": ExplanationWorker(),
    "data_retrieval": DataRetrievalWorker(),
    "pruning": PruningWorker()
}

async def dispatch_tasks_async(tasks: List[Dict], context: Dict) -> Dict:
    """
    Dispatches tasks to the appropriate AI Workers asynchronously and aggregates the results.
    """
    results = {}
    tasks_coroutines = []
    for task in tasks:
        worker_type = task.get("worker")
        if worker_type in ai_workers:
            worker = ai_workers[worker_type]
            tasks_coroutines.append(worker.process(task, context))
        else:
            logger.warning(f"No AI Worker found for type: {worker_type}")
    try:
        tasks_results = await asyncio.gather(*tasks_coroutines, return_exceptions=True)
        for result in tasks_results:
            if isinstance(result, Exception):
                logger.error(f"Error in AI Worker processing: {result}", exc_info=True)
            elif isinstance(result, dict):
                results.update(result)
    except Exception as e:
        logger.error(f"Error during task dispatching: {e}", exc_info=True)
    return results

# Incremental Reasoning Function with Multi-Step Problem Solving
async def incremental_reasoning_async(user_input: str, context: Dict) -> Dict:
    """
    Performs incremental reasoning by breaking down the user input into tasks,
    dispatching them to AI Workers, and aggregating the results.
    """
    reasoning_id = str(uuid.uuid4())
    reasoning_state[reasoning_id] = {
        "steps": [],
        "results": {}
    }
    
    # Example: For demonstration, we'll categorize based on keywords
    # In a real implementation, use NLP to parse and categorize the tasks
    tasks = []
    lower_input = user_input.lower()
    
    # Multi-step optimization for Quicksort
    if "optimize" in lower_input and "quicksort" in lower_input:
        tasks.append({"worker": "pruning", "code": user_input})
    
    # Code generation
    if "code" in lower_input or "generate" in lower_input:
        tasks.append({"worker": "code_generator", "input": user_input})
    
    # Code explanation
    if "explain" in lower_input:
        # Retrieve the last generated code if exists
        last_code = retrieve_last_memory_of_type("code")
        if last_code:
            tasks.append({"worker": "explanation", "code": last_code})
        else:
            logger.info("No recent code found for explanation.")
    
    # Data retrieval
    if "data" in lower_input or "information" in lower_input:
        tasks.append({"worker": "data_retrieval", "query": user_input})

    # Dispatch tasks to AI Workers
    logger.info(f"Dispatching {len(tasks)} tasks for reasoning ID: {reasoning_id}")
    results = await dispatch_tasks_async(tasks, context)
    
    reasoning_state[reasoning_id]["steps"].extend(tasks)
    reasoning_state[reasoning_id]["results"].update(results)
    
    return results

# Gradio Interface Function with Advanced Markdown Visualization and Increased Response Tokens
async def chat_interface(user_input: str):
    if user_input.lower() == "exit":
        response = "Exiting chat..."
        save_conversation_history()  # Save the conversation before exiting
        save_memory()  # Save memory before exiting
        return response

    # Update the conversation history with the user's message
    messages.append({"role": "user", "content": user_input})

    # Retrieve relevant memories based on user input
    relevant_memories = retrieve_relevant_memories(user_input)
    if relevant_memories:
        memory_prompt = "\n".join([f"Earlier you mentioned: {entry['text']}" for entry in relevant_memories])
        messages.append({"role": "system", "content": f"Relevant past interactions:\n{memory_prompt}"})
        logger.info(f"Retrieved {len(relevant_memories)} relevant memories.")

    # Perform incremental reasoning asynchronously
    reasoning_results = await incremental_reasoning_async(user_input, reasoning_state)

    # Generate assistant response based on reasoning results
    assistant_response = ""
    if "code" in reasoning_results:
        assistant_response += f"Here is the code you requested:\n```python\n{reasoning_results['code']}\n```\n\n"
    if "optimized_code" in reasoning_results:
        assistant_response += f"Optimized Code:\n```python\n{reasoning_results['optimized_code']}\n```\n\n"
    if "explanation" in reasoning_results:
        assistant_response += f"**Explanation:**\n{reasoning_results['explanation']}\n\n"
    if "data" in reasoning_results:
        assistant_response += f"**Data:**\n{reasoning_results['data']}\n\n"
    
    # If no specific tasks were dispatched, fall back to general response
    if not assistant_response:
        try:
            response = client.chat.completions.create(
                messages=messages,
                model="gpt-4o-mini",
                max_tokens=2000,  # Increased token limit for longer responses
                temperature=0.7,
            )
            if response.choices and len(response.choices) > 0:
                assistant_response = response.choices[0].message.content
            else:
                assistant_response = "I'm sorry, but I couldn't generate a response at this time."
                logger.warning("Fallback GPT response choices are empty.")
        except Exception as e:
            assistant_response = "An error occurred while generating a response."
            logger.error(f"Fallback GPT error: {e}", exc_info=True)

    # Update the conversation history with GPT's response
    messages.append({"role": "assistant", "content": assistant_response})
    
    # Log the assistant's response
    logger.info(f"Bot: {assistant_response}")

    # Update memory with the latest interaction
    update_memory(user_input, assistant_response)

    # Handle code copying (optional: can be done via UI buttons in Gradio)
    if "```" in assistant_response:  # Check if the response contains code
        # For web UI, implement a button or other mechanism
        # For simplicity, we'll skip automatic copying
        pass

    return assistant_response

# Launch Gradio Interface with Advanced Markdown Visualization
def launch_gradio():
    interface = gr.Interface(
        fn=chat_interface,
        inputs=gr.Textbox(label="You:", placeholder="Type your message here...", lines=2),
        outputs=gr.Markdown(label="Bot:"),  # Use Markdown for advanced visualization
        title="Enhanced Coding Assistant",
        description="Interact with an AI assistant that can generate, optimize, and explain code, as well as retrieve information. Supports advanced Markdown formatting for rich responses.",
        theme="default",
        allow_flagging="never",
        live=True  # Enable live updates
    )
    interface.queue(concurrency_count=10)  # Handle multiple requests
    interface.launch()

# Run the Gradio interface when the script is executed
if __name__ == "__main__":
    try:
        launch_gradio()
    except KeyboardInterrupt:
        print("\nChatbot terminated by user.")
        logger.info("Chatbot terminated by user via KeyboardInterrupt.")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        logger.error(f"Unexpected error: {e}", exc_info=True)