Function Calling Acts As if worked but doesn't execute

I have an OpenAI assistant that calls a function to send emails. When I call it with the code below it says it sent the email but it doesn’t actually send it. It works to send the email in the playground and through another method that I call it through so I know it is an issue with how I’m calling it in my code below. Any ideas on what could be causing this?

import json
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse
from openai import OpenAI
import os
import time

app = Flask(__name__)

# Configure OpenAI client
client = OpenAI(api_key='My_KEY')

@app.route('/whatsapp', methods=['POST'])
def whatsapp():
    # Extract the message text and sender ID from the incoming HTTP request
    incoming_msg = request.values.get('Body', '').strip()
    sender = request.values.get('From', None)
    
    print(f"Message from {sender}: {incoming_msg}")  # Log the incoming message for debugging
    
    # Initialize the Twilio MessagingResponse
    resp = MessagingResponse()
    
    if incoming_msg:
        try:
            # Create a new Thread and Run for the Assistant
            my_thread = client.beta.threads.create()
            my_thread_message = client.beta.threads.messages.create(
                thread_id=my_thread.id,
                role="user",
                content=incoming_msg
            )
            my_run = client.beta.threads.runs.create(
                thread_id=my_thread.id,
                assistant_id="My_Assistant"  # Replace with your Assistant's ID
            )
            
            # Log the thread ID and run details in JSON format
            log_data = {
                "thread_id": my_thread.id,
                "run_id": my_run.id,
                "status": my_run.status,
            }
            log_and_print(log_data)
            
            # Periodically check the run's status until it's completed or requires action
            while True:
                updated_run = client.beta.threads.runs.retrieve(
                    thread_id=my_thread.id,
                    run_id=my_run.id
                )
                
                # Log the run details in JSON format including thread_id
                log_data = {
                    "thread_id": my_thread.id,
                    "run_id": updated_run.id,
                    "status": updated_run.status,
                    "created_at": updated_run.created_at,
                    "completed_at": updated_run.completed_at,
                    # Add more attributes as needed
                }
                log_and_print(log_data)
                
                if updated_run.status == "completed":
                    break  # Exit the loop if the run is completed
                elif updated_run.status == "requires_action":
                    # Handle the requires_action status here (e.g., execute a tool)
                    execute_all_tools(updated_run, my_thread)
                time.sleep(5.0)  # Wait for half a second before checking again
            
            # After the run is completed, retrieve all messages in the thread
            all_messages = client.beta.threads.messages.list(thread_id=my_thread.id)
            
            # Extracting the first assistant's message
            assistant_reply = "I couldn't process your request. Please try again."
            for message in all_messages.data:
                if message.role == "assistant":
                    assistant_reply = message.content[0].text.value
                    break  # Take the first assistant's message as the reply
            
            # Send the assistant's response back via WhatsApp
            msg = resp.message(assistant_reply)
        except Exception as e:
            error_message = f"Error: {str(e)}"
            log_and_print(error_message)
            msg = resp.message("There was an error processing your request. Please try again later.")
    else:
        # Fallback message if no text was received
        msg = resp.message("I didn't understand that. Could you say it differently?")
    
    return str(resp)

def log_and_print(log_data):
    # Log the data in JSON format
    log_json = json.dumps(log_data, indent=4)
    print(log_json)
    # Optionally, write to a log file if needed
    # with open('app.log', 'a') as log_file:
    #     log_file.write(log_json + '\n')

def execute_all_tools(run, thread):
    # Implement the logic to execute all available tools based on the "requires_action" status
    # You can access the required tool details from the run object
    tool_calls = run.required_action.submit_tool_outputs.tool_calls

    for tool_call in tool_calls:
        name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)
        
        # Log tool execution details as a JSON object
        log_data = {
            "thread_id": thread.id,
            "run_id": run.id,
            "status": run.status,
            "tool_name": name,
            "tool_arguments": arguments,
        }
        log_and_print(log_data)
        
        # Execute the tool and obtain the responses
        responses = execute_tool_logic(tool_call)  # Replace with your tool execution logic
        
        # Log the tool execution completion
        log_completion_data = {
            "thread_id": thread.id,
            "run_id": run.id,
            "status": "completed",  # Assuming the tool execution is successful
            "tool_name": name,
        }
        log_and_print(log_completion_data)
        
        # Submit the tool outputs to continue the conversation
        run = client.beta.threads.runs.submit_tool_outputs(
            thread_id=thread.id,
            run_id=run.id,
            tool_outputs=[
                {
                    "tool_call_id": tool_call.id,
                    "output": json.dumps(responses),
                }
            ],
        )

def execute_tool_logic(tool_call):
    # Implement the actual logic to execute the tool based on the tool_call information
    # Replace this with your tool execution code
    responses = {}  # Replace with the actual tool output
    
    # Log that the tool is being executed
    log_execution_data = {
        "tool_execution": "started",
        "tool_name": tool_call.function.name,
    }
    log_and_print(log_execution_data)
    
    # Execute the tool logic and obtain responses
    # Replace the following line with your tool execution code
    # responses = your_tool_execution_function(tool_call)
    
    # Log that the tool execution is completed
    log_completion_data = {
        "tool_execution": "completed",
        "tool_name": tool_call.function.name,
    }
    log_and_print(log_completion_data)
    
    return responses



if __name__ == "__main__":
    from werkzeug.serving import run_simple
    run_simple('localhost', 5000, app)