with this code it shows the tool usage 3 times and also uses the tool 3 times. find the issue and fix it:
import gradio as gr
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langchain_core.tools import tool
from langchain_community.tools import DuckDuckGoSearchRun
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
import base64
from io import BytesIO
from PIL import Image, ImageDraw
import os

# Import the traceable decorator
from langsmith import traceable

# Set up environment variables for LangSmith tracing
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "<TRUFFLEHOG_REDACTED_LANGSMITH>"  # Using your actual API key
os.environ["LANGCHAIN_PROJECT"] = "m"

# Define the web search tool using the @tool decorator
@tool
def search_web(query: str) -> str:
    """Use this tool to search the web for real-time information based on a query."""
    print(f"Searching: {query}")
    search = DuckDuckGoSearchRun()
    result = search.run(query)
    print(f"Search result: {result}")
    return result

@tool
def generate_image(prompt: str) -> str:
    """Generate an image based on the prompt and return the image path."""
    print(f"Generating image: {prompt}")
    # Create an image with the prompt text
    image = Image.new('RGB', (400, 200), color='blue')
    draw = ImageDraw.Draw(image)
    draw.text((10, 10), prompt, fill='white')
    image_path = 'generated_image.png'
    image.save(image_path)
    print(f"Image saved to {image_path}")
    return image_path  # Returns the path to the generated image

# Initialize the ChatOllama model
llm_text = ChatOllama(
    model="llama3.1",
    temperature=0.8,
)

# List of tools
tools = [search_web, generate_image]

# Use the provided prompt
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
                "You are a helpful assistant with access to the following tools:\n"
                "1. search_web: Use this to search the web for real-time information.\n"
                "2. generate_image: Use this to generate images based on text prompts.\n\n"
                "When the user asks for information, decide whether to use a tool or provide the answer directly.\n"
                "When using a tool, provide the necessary input arguments.\n"
                "You can use multiple tools if needed."
        ),
        ("placeholder", "{chat_history}"),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),
    ]
)

# Create the agent with tool calling capabilities
agent = create_tool_calling_agent(llm_text, tools, prompt=prompt)

# Enable return_intermediate_steps
agent_executor = AgentExecutor(
    agent=agent, tools=tools, verbose=True,
    return_intermediate_steps=True, max_iterations=3
)

# Add the @traceable decorator to the function
@traceable
def vision_chat(message, vision_history, img):
    """Handle the vision chat interactions."""
    # ... (unchanged code for vision_chat)
    pass  # Placeholder to indicate unchanged code

@traceable
def search_chat(user_message, history):
    """Handle the search chat interactions with tool usage."""
    print(f"\nReceived message: {user_message}")
    # Initialize conversation history
    model_messages = []

    # Process history
    for msg in history:
        if msg["role"] == "user":
            model_messages.append(HumanMessage(content=msg["content"]))
        elif msg["role"] == "assistant":
            # Ignore tool outputs in model_messages
            if isinstance(msg["content"], str):
                model_messages.append(AIMessage(content=msg["content"]))
            else:
                # Skip Gradio component messages (like images)
                pass

    # Add current user message
    model_messages.append(HumanMessage(content=user_message))

    # Use the agent executor to process the message
    try:
        print("\nRunning agent executor...")
        result = agent_executor({"input": user_message, "chat_history": model_messages})
        response = result["output"]
        print(f"Agent response: {response}")

        # Extract the tools used from intermediate steps
        intermediate_steps = result.get("intermediate_steps", [])
        tools_used = []
        # Process each tool usage and add tool outputs to history
        for action, observation in intermediate_steps:
            tools_used.append(action.tool)
            if action.tool == "generate_image":
                # Assume observation is the image path
                image_path = observation.strip()
                if os.path.exists(image_path):
                    tool_message = {
                        "role": "assistant",
                        "content": gr.Image(value=image_path),
                        "metadata": {"title": f"🛠️ Used tool: {action.tool}"}
                    }
                    history.append(tool_message)
                else:
                    # If image not found, display an error
                    tool_message = {
                        "role": "assistant",
                        "content": f"Failed to generate image.",
                        "metadata": {"title": f"🛠️ Used tool: {action.tool}"}
                    }
                    history.append(tool_message)
            else:
                # For other tools, display the observation as text
                tool_message = {
                    "role": "assistant",
                    "content": observation,
                    "metadata": {"title": f"🛠️ Used tool: {action.tool}"}
                }
                history.append(tool_message)

        # Then, add the assistant's final answer
        assistant_message = {
            "role": "assistant",
            "content": response,
        }
        history.append(assistant_message)

        # Return updated history
        return history

    except Exception as e:
        print(f"Error generating response: {e}")
        error_message = {"role": "assistant", "content": f"An error occurred: {str(e)}"}
        history.append(error_message)
        return history

# Create the tabbed interface
with gr.Blocks() as demo:
    gr.Markdown("# AI Assistant with Vision and Search Capabilities")

    with gr.Tabs():
        # Vision Chat Tab (unchanged)
        with gr.Tab("Vision Chat"):
            vision_chatbot = gr.ChatInterface(
                vision_chat,
                additional_inputs=[gr.Image(label="Upload Image", type="pil")],
                title="Chat with Images",
                description="Upload an image and chat about it using vision capabilities"
            )

        # Search Chat Tab (using gr.Chatbot)
        with gr.Tab("Search Chat"):
            # Initialize the chatbot with type="messages"
            search_chatbot = gr.Chatbot(label="Assistant", elem_id="search_chatbot", type="messages")
            msg = gr.Textbox(show_label=False, placeholder="Type your message...")
            send_btn = gr.Button("Send")

            # Define the event handler
            def user_input(user_message, chat_history):
                if chat_history is None:
                    chat_history = []
                new_message = {"role": "user", "content": user_message}
                chat_history.append(new_message)
                updated_history = search_chat(user_message, chat_history)
                return "", updated_history

            send_btn.click(
                user_input,
                inputs=[msg, search_chatbot],
                outputs=[msg, search_chatbot],
            )

            gr.Markdown("Chat with the assistant that can search the web and generate images")

if __name__ == "__main__":
    demo.launch()