This is a conversation between you and me. I want you to implement the changes and suggestions you made into my overall code. Output the full script.

i I want to build an AI Call Bot for a Manchester Parking Meet N Greet Service Business. This is the code for the main bot.py file in my application. Analyse it for any shortcomings or errors and improve it. Please:


import os
import sys
import json
from datetime import datetime, timedelta
import asyncio
import aiohttp
import pytz
import re
from urllib.parse import urlencode
from datetime import datetime, timedelta
import time
import datetime
from datetime import date, timedelta
from pipecat.frames.frames import TextFrame, EndFrame, LLMMessagesFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.llm_response import (
    LLMAssistantResponseAggregator,
    LLMUserResponseAggregator,
)
from pipecat.services.cartesia import CartesiaTTSService
from pipecat.services.deepgram import DeepgramTTSService, DeepgramSTTService
from pipecat.services.openai import OpenAILLMService, OpenAILLMContext
from pipecat.transports.network.fastapi_websocket import (
    FastAPIWebsocketTransport,
    FastAPIWebsocketParams,
)
from pipecat.vad.silero import SileroVADAnalyzer
from pipecat.serializers.twilio import TwilioFrameSerializer

from openai.types.chat import ChatCompletionToolParam

from loguru import logger
from dotenv import load_dotenv
from twilio.rest import Client

load_dotenv(override=True)
logger.remove(0)
logger.add(sys.stderr, level="DEBUG")

# Airtable configuration
AIRTABLE_API_KEY = os.getenv("AIRTABLE_API_KEY")
AIRTABLE_BASE_ID = os.getenv("AIRTABLE_BASE_ID")
AIRTABLE_ARRIVALS_TABLE = os.getenv("AIRTABLE_ARRIVALS_TABLE")
AIRTABLE_DEPARTURES_TABLE = os.getenv("AIRTABLE_DEPARTURES_TABLE")


async def start_find_booking(function_name, llm, context):
    await llm.push_frame(TextFrame("Let me check that booking for you."))


async def transfer_call(function_name, tool_call_id, arguments, llm, context, result_callback):
    call_sid = arguments.get("call_sid")

    account_sid = os.getenv("TWILIO_ACCOUNT_SID")
    auth_token = os.getenv("TWILIO_AUTH_TOKEN")
    client = Client(account_sid, auth_token)

    logger.debug(f"Transferring call {call_sid}")

    try:
        client.calls(call_sid).update(
            twiml=f'<Response><Dial>{os.getenv("TRANSFER_NUMBER")}</Dial></Response>'
        )
        result = "The call was transferred successfully, say goodbye to the customer."
        await result_callback(json.dumps({"success": result}))
    except Exception as error:
        logger.error(f"Error transferring call: {str(error)}")
        await result_callback(json.dumps({"error": str(error)}))


def get_current_time():
    # Get the current time in UTC
    utc_now = datetime.datetime.now(pytz.utc)

    # Convert to UK time (assuming that's the relevant timezone for Manchester Airport)
    uk_tz = pytz.timezone("Europe/London")
    uk_time = utc_now.astimezone(uk_tz)

    # Format the time as a string
    formatted_time = uk_time.strftime("%I:%M %p")  # e.g., "02:30 PM"

    return {"current_time": formatted_time, "timestamp": uk_time.isoformat()}


# Example of how to use in your main code:
async def handle_get_current_time(
    function_name, tool_call_id, arguments, llm, context, result_callback
):
    current_time_info = get_current_time()
    await result_callback(json.dumps(current_time_info))


async def whatsapp_message(function_name, tool_call_id, arguments, llm, context, result_callback):
    registration = arguments.get("registration")
    is_arrival = arguments.get("is_arrival", False)

    airtable_api_key = os.getenv("AIRTABLE_API_KEY")
    base_id = os.getenv("AIRTABLE_BASE_ID")
    table_name = (
        os.getenv("AIRTABLE_ARRIVALS_TABLE")
        if is_arrival
        else os.getenv("AIRTABLE_DEPARTURES_TABLE")
    )
    twilio_account_sid = os.getenv("TWILIO_ACCOUNT_SID")
    twilio_auth_token = os.getenv("TWILIO_AUTH_TOKEN")
    twilio_whatsapp_number = os.getenv("TWILIO_WHATSAPP_NUMBER")
    manager_whatsapp_group = os.getenv("MANAGER_WHATSAPP_GROUP")

    formatted_registration = registration.replace(" ", "").upper()

    airtable_url = (
        f"https://api.airtable.com/v0/{base_id}/{table_name}?"
        f'filterByFormula=UPPER({{Registration}})=UPPER("{formatted_registration}")&'
        f"cellFormat=string&timeZone=Europe/London&userLocale=en-gb"
    )

    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(
                airtable_url, headers={"Authorization": f"Bearer {airtable_api_key}"}
            ) as response:
                airtable_data = await response.json()

            if airtable_data["records"]:
                record = airtable_data["records"][0]["fields"]

                vehicle_make = record.get("Vehicle_Make", "N/A")
                name = record.get("Name", "N/A")
                contact_number = record.get("Contact_Number", "N/A")
                entry_date_time = record.get("Entry_Date_Time", "N/A")
                terminal = record.get("Terminal", "N/A")
                estimated_eta = record.get("Current_ETA", "N/A")

                booking_type = "Arrival (Pick-up)" if is_arrival else "Departure (Drop-off)"

                message = f"""
New {booking_type} Booking Requires Driver Assignment:
- Vehicle: {vehicle_make}
- Registration: {registration}
- Customer Name: {name}
- Contact Number: {contact_number}
- {"Arrival" if is_arrival else "Entry"} Date/Time: {entry_date_time}
- Estimated {"Landing Time" if is_arrival else "ETA"}: {estimated_eta}
- Terminal: {terminal}

Please assign a driver for this {"pick-up" if is_arrival else "drop-off"}.
"""

                logger.debug(f"WhatsApp message content: {message}")

                twilio_url = (
                    f"https://api.twilio.com/2010-04-01/Accounts/{twilio_account_sid}/Messages.json"
                )
                auth = aiohttp.BasicAuth(twilio_account_sid, twilio_auth_token)
                data = {
                    "From": f"whatsapp:{twilio_whatsapp_number}",
                    "To": f"whatsapp:{manager_whatsapp_group}",
                    "Body": message,
                }

                async with session.post(
                    twilio_url,
                    auth=auth,
                    data=urlencode(data),
                    headers={"Content-Type": "application/x-www-form-urlencoded"},
                ) as twilio_response:
                    twilio_data = await twilio_response.json()

                logger.info(f'WhatsApp message sent successfully: {twilio_data["sid"]}')

                await result_callback(
                    json.dumps(
                        {
                            "success": "Manager notified successfully.",
                            "messageId": twilio_data["sid"],
                            "isArrival": is_arrival,
                        }
                    )
                )
            else:
                logger.warning(f"No booking found for registration: {formatted_registration}")
                await result_callback(
                    json.dumps({"error": "No booking found for this registration number."})
                )
        except Exception as error:
            logger.error(f"Error in whatsappMessage function: {str(error)}")
            await result_callback(
                json.dumps({"error": "Failed to process the request.", "details": str(error)})
            )


async def find_booking_by_phone(
    function_name, tool_call_id, arguments, llm, context, result_callback
):
    phone_number = arguments.get("phone_number")
    is_arrival = arguments.get("is_arrival", False)

    logger.debug(f"Finding booking for phone number: {phone_number}, isArrival: {is_arrival}")

    table_name = AIRTABLE_ARRIVALS_TABLE if is_arrival else AIRTABLE_DEPARTURES_TABLE

    # Remove any non-digit characters and ensure the number starts with '44' or '0'
    formatted_phone_number = re.sub(r"\D", "", phone_number)
    if formatted_phone_number.startswith("44"):
        formatted_phone_number = "0" + formatted_phone_number[2:]
    elif not formatted_phone_number.startswith("0"):
        formatted_phone_number = "0" + formatted_phone_number

    url = (
        f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}?"
        f'filterByFormula=OR(SEARCH("{formatted_phone_number}",{{Contact_Number}}),SEARCH("{formatted_phone_number.replace("^0", "44")}",{{Contact_Number}}))&'
        f"cellFormat=string&timeZone=Europe/London&userLocale=en-gb"
    )

    headers = {"Authorization": f"Bearer {AIRTABLE_API_KEY}"}

    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(url, headers=headers) as response:
                data = await response.json()

            if data["records"]:
                record = data["records"][0]["fields"]

                booking_time = datetime.strptime(record["Entry_Date_Time"], "%d/%m/%Y %H:%M")
                booking_time = pytz.timezone("Europe/London").localize(booking_time)
                formatted_booking_time = booking_time.strftime("%B %d at %I:%M %p")

                result = {
                    "found": True,
                    "customerName": record.get("Name", "Not provided"),
                    "terminal": record.get("Terminal"),
                    "bookingTime": formatted_booking_time,
                    "contactNumber": record.get("Contact_Number"),
                    "allocatedCarPark": record.get("Allocated_Car_Park"),
                    "registration": record.get("Registration"),
                    "isArrival": is_arrival,
                }
                await result_callback(json.dumps(result))
            else:
                await result_callback(
                    json.dumps({"found": False, "error": "No booking found for this phone number."})
                )
        except Exception as error:
            logger.error(f"Error finding booking by phone: {str(error)}")
            await result_callback(
                json.dumps(
                    {"found": False, "error": "Failed to find booking.", "details": str(error)}
                )
            )


async def update_terminal(function_name, tool_call_id, arguments, llm, context, result_callback):
    registration = arguments.get("registration")
    terminal = arguments.get("terminal")
    is_arrival = arguments.get("is_arrival", False)

    timezone = pytz.timezone("Europe/London")
    current_time = datetime.now(timezone)
    logger.debug(
        f"Updating terminal for registration: {registration}, new terminal: {terminal}, isArrival: {is_arrival}, currentTime: {current_time.strftime('%Y-%m-%d %H:%M:%S')}"
    )

    table_name = AIRTABLE_ARRIVALS_TABLE if is_arrival else AIRTABLE_DEPARTURES_TABLE
    formatted_registration = registration.replace(" ", "").upper()

    url = (
        f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}?"
        f'filterByFormula=UPPER({{Registration}})=UPPER("{formatted_registration}")&'
        f"cellFormat=string&timeZone=Europe/London&userLocale=en-gb"
    )

    headers = {"Authorization": f"Bearer {AIRTABLE_API_KEY}"}

    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(url, headers=headers) as response:
                if response.status == 200:
                    data = await response.json()
                    if not data["records"]:
                        logger.warning(
                            f"No booking found for registration: {formatted_registration}"
                        )
                        await result_callback(
                            json.dumps({"error": "No booking found for this registration number."})
                        )
                        return

                    record = data["records"][0]
                    record_id = record["id"]

                    patch_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}"
                    patch_data = {
                        "records": [{"id": record_id, "fields": {"Terminal": terminal}}],
                        "typecast": True,
                    }

                    async with session.patch(
                        patch_url, headers=headers, json=patch_data
                    ) as patch_response:
                        if patch_response.status == 200:
                            patch_data = await patch_response.json()
                            logger.info(f"Terminal updated successfully. New terminal: {terminal}")
                            await result_callback(
                                json.dumps(
                                    {
                                        "success": "Terminal updated successfully.",
                                        "updatedRecord": patch_data["records"][0],
                                        "updatedTerminal": terminal,
                                        "isArrival": is_arrival,
                                    }
                                )
                            )
                        else:
                            error_text = await patch_response.text()
                            logger.error(f"Error updating terminal: {error_text}")
                            await result_callback(
                                json.dumps(
                                    {"error": "Failed to update terminal.", "details": error_text}
                                )
                            )
                else:
                    error_text = await response.text()
                    logger.error(f"Error response from Airtable: {response.status}")
                    await result_callback(
                        json.dumps(
                            {
                                "error": f"Failed to find booking. Status: {response.status}",
                                "details": error_text,
                            }
                        )
                    )
        except Exception as error:
            logger.error(f"Error updating terminal: {str(error)}")
            await result_callback(
                json.dumps({"error": "Failed to update terminal.", "details": str(error)})
            )


async def update_eta(function_name, tool_call_id, arguments, llm, context, result_callback):
    customer_eta = arguments.get("customer_eta")
    registration = arguments.get("registration")
    is_arrival = arguments.get("is_arrival", False)

    timezone = pytz.timezone("Europe/London")
    current_time = datetime.now(timezone)
    logger.debug(
        f"Updating ETA for registration: {registration}, customerETA: {customer_eta}, isArrival: {is_arrival}, currentTime: {current_time.strftime('%Y-%m-%d %H:%M:%S')}"
    )

    table_name = AIRTABLE_ARRIVALS_TABLE if is_arrival else AIRTABLE_DEPARTURES_TABLE
    formatted_registration = registration.replace(" ", "").upper()

    url = (
        f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}?"
        f'filterByFormula=UPPER({{Registration}})=UPPER("{formatted_registration}")&'
        f"cellFormat=string&timeZone=Europe/London&userLocale=en-gb"
    )

    headers = {"Authorization": f"Bearer {AIRTABLE_API_KEY}"}

    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(url, headers=headers) as response:
                if response.status == 200:
                    data = await response.json()
                    if not data["records"]:
                        logger.warning(
                            f"No booking found for registration: {formatted_registration}"
                        )
                        await result_callback(
                            json.dumps({"error": "No booking found for this registration number."})
                        )
                        return

                    record = data["records"][0]
                    record_id = record["id"]

                    eta_time = parse_eta(customer_eta, current_time, timezone)
                    if not eta_time:
                        await result_callback(
                            json.dumps(
                                {
                                    "error": 'Invalid time format provided. Please use format like "30 minutes", "2 hours", or a specific time like "4:30 PM".'
                                }
                            )
                        )
                        return

                    patch_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}"
                    patch_data = {
                        "records": [
                            {
                                "id": record_id,
                                "fields": {
                                    "Current_ETA": eta_time.strftime("%Y-%m-%dT%H:%M:%S.000Z")
                                },
                            }
                        ],
                        "typecast": True,
                    }

                    async with session.patch(
                        patch_url, headers=headers, json=patch_data
                    ) as patch_response:
                        if patch_response.status == 200:
                            patch_data = await patch_response.json()
                            logger.info(
                                f"ETA updated successfully. New ETA: {eta_time.strftime('%Y-%m-%d %H:%M:%S')}"
                            )
                            await result_callback(
                                json.dumps(
                                    {
                                        "success": "ETA updated successfully.",
                                        "updatedRecord": patch_data["records"][0],
                                        "formattedETA": eta_time.strftime("%B %d at %I:%M %p"),
                                        "isArrival": is_arrival,
                                    }
                                )
                            )
                        else:
                            error_text = await patch_response.text()
                            logger.error(f"Error updating ETA: {error_text}")
                            await result_callback(
                                json.dumps(
                                    {
                                        "error": "Failed to update ETA.",
                                        "details": error_text,
                                    }
                                )
                            )
                else:
                    error_text = await response.text()
                    logger.error(f"Error response from Airtable: {response.status}")
                    await result_callback(
                        json.dumps(
                            {
                                "error": f"Failed to find booking. Status: {response.status}",
                                "details": error_text,
                            }
                        )
                    )
        except Exception as error:
            logger.error(f"Error updating ETA: {str(error)}")
            await result_callback(
                json.dumps({"error": "Failed to update ETA.", "details": str(error)})
            )


def parse_eta(eta_string, current_time, timezone):
    # Try parsing as relative time
    relative_regex = r"(\d+)\s*(minutes?|hours?)"
    match = re.match(relative_regex, eta_string, re.IGNORECASE)
    if match:
        value = int(match.group(1))
        unit = "hours" if match.group(2).lower().startswith("hour") else "minutes"
        return current_time + timedelta(**{unit: value})

    # Try parsing as exact time
    try:
        parsed_time = datetime.strptime(eta_string, "%I:%M %p").time()
        eta_date = current_time.date()
        if parsed_time < current_time.time():
            eta_date += timedelta(days=1)
        return timezone.localize(datetime.combine(eta_date, parsed_time))
    except ValueError:
        pass

    # If both parsing methods fail, return None
    return None


async def update_registration(
    function_name, tool_call_id, arguments, llm, context, result_callback
):
    old_registration = arguments.get("old_registration")
    new_registration = arguments.get("new_registration")
    is_arrival = arguments.get("is_arrival", False)

    timezone = pytz.timezone("Europe/London")
    current_time = datetime.now(timezone)
    logger.debug(
        f"Updating registration from: {old_registration} to: {new_registration}, isArrival: {is_arrival}, currentTime: {current_time.strftime('%Y-%m-%d %H:%M:%S')}"
    )

    table_name = AIRTABLE_ARRIVALS_TABLE if is_arrival else AIRTABLE_DEPARTURES_TABLE
    formatted_old_registration = old_registration.replace(" ", "").upper()
    formatted_new_registration = new_registration.replace(" ", "").upper()

    url = (
        f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}?"
        f'filterByFormula=UPPER({{Registration}})=UPPER("{formatted_old_registration}")&'
        f"cellFormat=string&timeZone=Europe/London&userLocale=en-gb"
    )

    headers = {"Authorization": f"Bearer {AIRTABLE_API_KEY}"}

    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(url, headers=headers) as response:
                if response.status == 200:
                    data = await response.json()
                    if not data["records"]:
                        logger.warning(
                            f"No booking found for registration: {formatted_old_registration}"
                        )
                        await result_callback(
                            json.dumps({"error": "No booking found for this registration number."})
                        )
                        return

                    record = data["records"][0]
                    record_id = record["id"]

                    patch_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}"
                    patch_data = {
                        "records": [
                            {
                                "id": record_id,
                                "fields": {"Registration": formatted_new_registration},
                            }
                        ],
                        "typecast": True,
                    }

                    async with session.patch(
                        patch_url, headers=headers, json=patch_data
                    ) as patch_response:
                        if patch_response.status == 200:
                            patch_data = await patch_response.json()
                            logger.info(
                                f"Registration updated successfully. New registration: {formatted_new_registration}"
                            )
                            await result_callback(
                                json.dumps(
                                    {
                                        "success": "Registration updated successfully.",
                                        "updatedRecord": patch_data["records"][0],
                                        "oldRegistration": formatted_old_registration,
                                        "newRegistration": formatted_new_registration,
                                        "isArrival": is_arrival,
                                    }
                                )
                            )
                        else:
                            error_text = await patch_response.text()
                            logger.error(f"Error updating registration: {error_text}")
                            await result_callback(
                                json.dumps(
                                    {
                                        "error": "Failed to update registration.",
                                        "details": error_text,
                                    }
                                )
                            )
                else:
                    error_text = await response.text()
                    logger.error(f"Error response from Airtable: {response.status}")
                    await result_callback(
                        json.dumps(
                            {
                                "error": f"Failed to find booking. Status: {response.status}",
                                "details": error_text,
                            }
                        )
                    )
        except Exception as error:
            logger.error(f"Error updating registration: {str(error)}")
            await result_callback(
                json.dumps({"error": "Failed to update registration.", "details": str(error)})
            )


class DepartureFlow:
    def __init__(self):
        self.booking_found = False
        self.eta_updated = False
        self.instructions_given = False
        self.staff_notified = False

    def check_completion(self):
        return all(
            [self.booking_found, self.eta_updated, self.instructions_given, self.staff_notified]
        )


# In your main processing loop:
departure_flow = DepartureFlow()

# After finding booking:
departure_flow.booking_found = True

# After updating ETA:
departure_flow.eta_updated = True

# After giving instructions:
departure_flow.instructions_given = True

# After notifying staff:
departure_flow.staff_notified = True

# Before ending conversation:
if not departure_flow.check_completion():
    # Prompt AI to complete missing steps
    pass


async def update_phone_number(
    function_name, tool_call_id, arguments, llm, context, result_callback
):
    registration = arguments.get("registration")
    phone_number = arguments.get("phone_number")
    is_arrival = arguments.get("is_arrival", False)

    timezone = pytz.timezone("Europe/London")
    current_time = datetime.now(timezone)
    logger.debug(
        f"Updating phone number for registration: {registration}, new number: {phone_number}, isArrival: {is_arrival}, currentTime: {current_time.strftime('%Y-%m-%d %H:%M:%S')}"
    )

    table_name = AIRTABLE_ARRIVALS_TABLE if is_arrival else AIRTABLE_DEPARTURES_TABLE
    formatted_registration = registration.replace(" ", "").upper()

    url = (
        f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}?"
        f'filterByFormula=UPPER({{Registration}})=UPPER("{formatted_registration}")&'
        f"cellFormat=string&timeZone=Europe/London&userLocale=en-gb"
    )

    headers = {"Authorization": f"Bearer {AIRTABLE_API_KEY}"}

    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(url, headers=headers) as response:
                if response.status == 200:
                    data = await response.json()
                    if not data["records"]:
                        logger.warning(
                            f"No booking found for registration: {formatted_registration}"
                        )
                        await result_callback(
                            json.dumps({"error": "No booking found for this registration number."})
                        )
                        return

                    record = data["records"][0]
                    record_id = record["id"]

                    patch_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}"
                    patch_data = {
                        "records": [{"id": record_id, "fields": {"Contact_Number": phone_number}}],
                        "typecast": True,
                    }

                    async with session.patch(
                        patch_url, headers=headers, json=patch_data
                    ) as patch_response:
                        if patch_response.status == 200:
                            patch_data = await patch_response.json()
                            logger.info(
                                f"Phone number updated successfully. New number: {phone_number}"
                            )
                            await result_callback(
                                json.dumps(
                                    {
                                        "success": "Phone number updated successfully.",
                                        "updatedRecord": patch_data["records"][0],
                                        "updatedPhoneNumber": phone_number,
                                        "isArrival": is_arrival,
                                    }
                                )
                            )
                        else:
                            error_text = await patch_response.text()
                            logger.error(f"Error updating phone number: {error_text}")
                            await result_callback(
                                json.dumps(
                                    {
                                        "error": "Failed to update phone number.",
                                        "details": error_text,
                                    }
                                )
                            )
                else:
                    error_text = await response.text()
                    logger.error(f"Error response from Airtable: {response.status}")
                    await result_callback(
                        json.dumps(
                            {
                                "error": f"Failed to find booking. Status: {response.status}",
                                "details": error_text,
                            }
                        )
                    )
        except Exception as error:
            logger.error(f"Error updating phone number: {str(error)}")
            await result_callback(
                json.dumps({"error": "Failed to update phone number.", "details": str(error)})
            )


async def find_booking(function_name, tool_call_id, arguments, llm, context, result_callback):
    registration = arguments.get("registration", "")
    is_arrival = arguments.get("is_arrival", False)

    logger.debug(f"Raw input - registration: {registration}, isArrival: {is_arrival}")

    # Remove any non-alphanumeric characters and convert to uppercase
    formatted_registration = "".join(char for char in registration if char.isalnum()).upper()

    logger.debug(f"Formatted registration: {formatted_registration}")

    # Rest of the function remains the same...

    table_name = AIRTABLE_ARRIVALS_TABLE if is_arrival else AIRTABLE_DEPARTURES_TABLE

    url = (
        f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}?"
        + f'filterByFormula=UPPER({{Registration}})="{formatted_registration}"&'
        + "cellFormat=string&timeZone=Europe/London&userLocale=en-gb"
    )

    headers = {"Authorization": f"Bearer {AIRTABLE_API_KEY}"}

    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(url, headers=headers) as response:
                if response.status == 200:
                    data = await response.json()
                    if data["records"]:
                        record = data["records"][0]["fields"]

                        try:
                            booking_time = datetime.strptime(
                                record["Entry_Date_Time"], "%d/%m/%Y %H:%M"
                            )
                            booking_time = pytz.timezone("Europe/London").localize(booking_time)
                            formatted_booking_time = booking_time.strftime("%B %d at %I:%M %p")
                        except ValueError:
                            logger.error(f"Error parsing booking time: {record['Entry_Date_Time']}")
                            formatted_booking_time = "Date format error"

                        contact_number = record.get("Contact_Number", "Not provided")
                        if contact_number != "Not provided":
                            contact_number = " ".join(
                                [
                                    contact_number[i : i + 4]
                                    for i in range(0, len(contact_number), 4)
                                ]
                            )

                        result = {
                            "found": True,
                            "customerName": record.get("Name", "Not provided"),
                            "terminal": record.get("Terminal", "Not provided"),
                            "bookingTime": formatted_booking_time,
                            "contactNumber": contact_number,
                            "allocatedCarPark": record.get("Allocated_Car_Park", "Not provided"),
                            "registration": formatted_registration,
                        }

                        await result_callback(json.dumps(result))
                    else:
                        logger.warning(
                            f"No booking found for registration: {formatted_registration}"
                        )
                        await result_callback(
                            json.dumps(
                                {
                                    "found": False,
                                    "error": f"No booking found for registration {formatted_registration}.",
                                }
                            )
                        )
                else:
                    logger.error(f"Error response from Airtable: {response.status}")
                    await result_callback(
                        json.dumps(
                            {
                                "found": False,
                                "error": f"Failed to find booking. Status: {response.status}",
                            }
                        )
                    )
        except Exception as error:
            logger.error(f"Error finding booking: {str(error)}")
            await result_callback(
                json.dumps(
                    {"found": False, "error": f"Failed to find booking. Error: {str(error)}"}
                )
            )


async def run_bot(websocket_client, stream_sid):
    async with aiohttp.ClientSession() as session:
        try:
            transport = FastAPIWebsocketTransport(
                websocket=websocket_client,
                params=FastAPIWebsocketParams(
                    audio_out_enabled=True,
                    add_wav_header=False,
                    vad_enabled=True,
                    vad_analyzer=SileroVADAnalyzer(),
                    vad_audio_passthrough=True,
                    serializer=TwilioFrameSerializer(stream_sid),
                ),
            )

            llm = OpenAILLMService(
                api_key=os.getenv("GROQ_API_KEY"),
                base_url="https://api.groq.com/openai/v1",
                model="llama-3.1-70b-versatile",
            )
            llm.register_function("find_booking", find_booking)
            llm.register_function("update_terminal", update_terminal)
            llm.register_function("update_registration", update_registration)
            llm.register_function("update_phone_number", update_phone_number)
            llm.register_function("transfer_call", transfer_call)
            llm.register_function("whatsapp_message", whatsapp_message)
            llm.register_function("find_booking_by_phone", find_booking_by_phone)
            llm.register_function("update_eta", update_eta)
            llm.register_function("get_current_time", handle_get_current_time)

            stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))

            # tts = DeepgramTTSService(
            #     aiohttp_session=session,
            #     api_key=os.getenv("DEEPGRAM_API_KEY"),
            #     voice="aura-helios-en",
            #     encoding="linear16",  # or "mulaw" or "alaw" for streaming
            #     sample_rate=16000,  # choose an appropriate sample rate
            #     container="none",  # This is the key change
            # )

            tts = CartesiaTTSService(
                api_key=os.getenv("CARTESIA_API_KEY"),
                voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22",  # British Lady
            )

            tools = [
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "find_booking",
                        "description": "Find booking information for Manchester Airport Parking",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "registration": {
                                    "type": "string",
                                    "description": "The vehicle registration number",
                                },
                                "is_arrival": {
                                    "type": "boolean",
                                    "description": "True if the customer is arriving, False if departing",
                                },
                            },
                            "required": ["registration", "is_arrival"],
                        },
                    },
                ),
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "update_terminal",
                        "description": "Update the terminal for a booking",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "registration": {
                                    "type": "string",
                                    "description": "The vehicle registration number",
                                },
                                "terminal": {
                                    "type": "string",
                                    "description": "The new terminal number",
                                },
                                "is_arrival": {
                                    "type": "boolean",
                                    "description": "True if the customer is arriving, False if departing",
                                },
                            },
                            "required": ["registration", "terminal", "is_arrival"],
                        },
                    },
                ),
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "update_registration",
                        "description": "Update the registration number for a booking",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "old_registration": {
                                    "type": "string",
                                    "description": "The current vehicle registration number",
                                },
                                "new_registration": {
                                    "type": "string",
                                    "description": "The new vehicle registration number",
                                },
                                "is_arrival": {
                                    "type": "boolean",
                                    "description": "True if the customer is arriving, False if departing",
                                },
                            },
                            "required": ["old_registration", "new_registration", "is_arrival"],
                        },
                    },
                ),
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "update_phone_number",
                        "description": "Update the phone number for a booking",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "registration": {
                                    "type": "string",
                                    "description": "The vehicle registration number",
                                },
                                "phone_number": {
                                    "type": "string",
                                    "description": "The new phone number",
                                },
                                "is_arrival": {
                                    "type": "boolean",
                                    "description": "True if the customer is arriving, False if departing",
                                },
                            },
                            "required": ["registration", "phone_number", "is_arrival"],
                        },
                    },
                ),
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "transfer_call",
                        "description": "Transfer the current call to a human agent",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "call_sid": {
                                    "type": "string",
                                    "description": "The unique identifier for the current call",
                                },
                            },
                            "required": ["call_sid"],
                        },
                    },
                ),
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "whatsapp_message",
                        "description": "Send a WhatsApp message to the manager about a booking",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "registration": {
                                    "type": "string",
                                    "description": "The vehicle registration number",
                                },
                                "is_arrival": {
                                    "type": "boolean",
                                    "description": "True if the customer is arriving, False if departing",
                                },
                            },
                            "required": ["registration", "is_arrival"],
                        },
                    },
                ),
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "find_booking_by_phone",
                        "description": "Find a booking using the customer's phone number",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "phone_number": {
                                    "type": "string",
                                    "description": "The customer's phone number",
                                },
                                "is_arrival": {
                                    "type": "boolean",
                                    "description": "True if the customer is arriving, False if departing",
                                },
                            },
                            "required": ["phone_number", "is_arrival"],
                        },
                    },
                ),
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "update_eta",
                        "description": "Update the estimated time of arrival (ETA) for a booking",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "registration": {
                                    "type": "string",
                                    "description": "The vehicle registration number",
                                },
                                "customer_eta": {
                                    "type": "string",
                                    "description": "The customer's estimated time of arrival. Can be a relative time (e.g., '30 minutes' or '2 hours') or an exact time (e.g., '4:30 PM')",
                                },
                                "is_arrival": {
                                    "type": "boolean",
                                    "description": "True if the customer is arriving, False if departing",
                                },
                            },
                            "required": ["registration", "customer_eta", "is_arrival"],
                        },
                    },
                ),
                ChatCompletionToolParam(
                    type="function",
                    function={
                        "name": "get_current_time",
                        "description": "Get the current time in UK timezone",
                        "parameters": {
                            "type": "object",
                            "properties": {},  # This function doesn't require any parameters
                            "required": [],
                        },
                    },
                ),
            ]

            messages = [
                {
                    "role": "system",
                    "content": """
*Role:*

You are Josh, an AI assistant for Manchester Airport Parking. Your primary role is to assist customers with parking reservations for car drop-offs and pick-ups.

**Main Objective:**

Handle customer inquiries efficiently and professionally, following specific guidelines and conversation flows to ensure a seamless experience.

*General Guidelines:*
    - Professionalism: Maintain a professional and helpful tone throughout the conversation.
    - Clarity: Keep responses concise and under 500 characters.
    - Confidentiality: Do not share raw function data with the customer.
    - Verification: Always confirm details before proceeding.
    - Avoid Repetition: Do not repeat yourself unless asked by the customer.
    - Self-Disclosure: Do not disclose that you are an AI or imply that you are human.
    - Special Characters: Avoid using special characters to facilitate audio conversion.
    - Formatting:
        - Registration Numbers: Confirm by repeating back clearly, spelling out letters and numbers.
        - Phone Numbers: Format with dashes to signal pauses (e.g., "0742-111-7301").
        - Dates and Times: Pronounce completely and slowly
        
*Conversation Flow:*
    For Drop-Offs:

        Step 1. Greeting and Understand Intent:

        Step 2. Obtain and Confirm Registration Number:
        - Ask for the car registration number.
        - Repeat it back to confirm.
        - Example: "Sure, could I get your car registration number, please?" "Just to confirm, that's Y-A-19-K-X-T?"

        Step 3. Find the Booking:
        - Inform the customer you're retrieving their booking.
        - Use the function: find_booking(registration, is_arrival = false).
        - Example: "Okay, perfect. Give me a moment while I find your booking."

        Step 4. Confirm Booking Details One by One:
        - Name
        - Booking Time
        - Terminal Number
        - Contact Phone Number
        - Example: "I've found your booking. The name we have is {name}, is that correct?"
        - Do not mention allocated car park at this stage.
        - If any of the details are wrong and require updating, use the respective functions.

        Step 5. Ask for ETA:
        - Request the estimated time of arrival.
        - Suggest checking their navigation system for accuracy.
        - Example: "Could you tell me what time you expect to arrive at Terminal {terminal}? For accuracy, could you check your navigation system?"

        Step 6. Update ETA:
        - Use the function: update_eta(registration, customer_eta, is_arrival=false).
        - Inform the customer you're updating the system.
        - Example: "Alright, perfect. I'll update that in our system."

        Step 7. Provide Instructions:
        - Use get_current_time() to check the current time.
        - Instruct the customer on where to go upon arrival.
        - Example: "When you arrive at Terminal {terminal}, please head to Level 0 of the {allocated_car_park}. A driver will be waiting there and will call you when they're close by."

        Step 8. Notify Staff:
        - Do the following immediately after providing instructions.
        - Use the function: whatsapp_message(registration, is_arrival=false).
        - Inform the customer you're notifying the team.
        - Example: "I'll notify our staff to be ready for your arrival."

        Step 9. Conclude the Call:
        - Ask if there's anything else you can assist with.
        - Thank the customer.
        - Example: "Is there anything else I can assist you with today? Safe travels, and we'll see you soon!"

*If Customer Requests a Transfer:*
    - Use the function: transfer_call(call_sid).
    - Inform the customer you're transferring them to a human agent.
    - Example: "I'll transfer you to a human agent who can assist you further."

*Function Usage:*
  - Functions List:
        - find_booking(registration, is_arrival)
        - update_eta(registration, customer_eta, is_arrival)
        - whatsapp_message(registration, is_arrival)
        - find_booking_by_phone(phone_number, is_arrival)
        - update_terminal(registration, terminal, is_arrival)
        - update_registration(old_registration, new_registration, is_arrival)
        - update_phone_number(registration, phone_number, is_arrival)
        - transfer_call(call_sid)
        - get_current_time()

*Instructions:*
    - Use exact function names as listed.
    - Include all required parameters.
    - Do not proceed until each function call is complete.
    - If unsure about any details, ask the customer for clarification.
    - Do not guess parameter values.
    - Do not share raw function data or outputs with the customer.    
      
**Example Conversation for Drop-Off:**

AI: "Hello! Welcome to Manchester Airport Parking. Are you calling to drop off a car for us to park or have you landed and want us to bring your car to the airport for collection?"

Customer: "Hi, yeah I'm calling about a drop-off."

AI: "Sure, could I get your car registration number, please?"

Customer: "Yeah, it's YA19KXT."

AI: "Just to confirm, that's Y-A-one-nine-K-X-T?"

Customer: "Yes, that's correct."

AI: "Okay, perfect. Give me a moment while I find your booking."
(Uses find_booking(registration="YA19KXT", is_arrival=False))

AI: "I've found your booking. The name we have is John Smith, is that correct?"

Customer: "Yes, that's correct."

AI: "Great. The booking time we have is 4:00 PM today, is that correct?"

Customer: "Yes, it's correct."

AI: "You're departing from Terminal 2, is that right?"

Customer: "Yes."

AI: "And is 0742-111-7301 still the best number to reach you at?"

Customer: "Yes."

AI: "Perfect. Could you tell me what time you expect to arrive at Terminal 2? For accuracy, could you check your navigation system?"

Customer: "It says I'll arrive at 3:36 PM."

AI: "Alright, perfect. I'll update that in our system."
(Uses update_eta(registration="YA19KXT", customer_eta="3:36 PM", is_arrival=False))

AI: "When you arrive at Terminal 2, please head to Level 0 of the Multi-Storey Car Park. A driver will be waiting there and will call you when they're close by."

AI: "I'll notify our staff to be ready for your arrival."
(Uses whatsapp_message(registration="YA19KXT", is_arrival=False))

AI: "Is there anything else I can assist you with today?"

Customer: "No, that's all. Thank you."

AI: "You're welcome. Safe travels, and we'll see you soon!"
                                """,
                }
            ]

            tma_in = LLMUserResponseAggregator(messages)
            tma_out = LLMAssistantResponseAggregator(messages)

            pipeline = Pipeline(
                [
                    transport.input(),  # Websocket input from client
                    stt,  # Speech-To-Text
                    tma_in,  # User responses
                    llm,  # LLM
                    tts,  # Text-To-Speech
                    transport.output(),  # Websocket output to client
                    tma_out,  # LLM responses
                ]
            )

            task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))

            @transport.event_handler("on_client_connected")
            async def on_client_connected(transport, client):
                # Kick off the conversation.
                await tts.say(
                    "Hello! Welcome to Manchester Airport Parking. Are you calling to drop off a car for us to park or have you landed and want us to bring your car to the airport for collection??"
                )

            @transport.event_handler("on_client_disconnected")
            async def on_client_disconnected(transport, client):
                await task.queue_frames([TextFrame("Goodbye!")])

            runner = PipelineRunner(handle_sigint=False)

            await runner.run(task)

        except Exception as e:
            logger.error(f"Error in run_bot: {str(e)}")
        finally:
            # Ensure all tasks are properly cancelled and resources are cleaned up
            if "runner" in locals():
                await runner.stop()
            if "pipeline" in locals():
                await pipeline.stop()


if __name__ == "__main__":
    print("This script should be imported and used by server.py, not run directly.")

Analysis of the Provided bot.py Code:

The goal of your bot.py script is to build an AI Call Bot for a Manchester Parking Meet & Greet Service Business. The bot interacts with customers, retrieves and updates booking information from Airtable, communicates via Twilio, and leverages an LLM for natural language understanding.

Below is a detailed analysis of the code, highlighting potential shortcomings, errors, and suggestions for improvements.

1. Redundant and Unused Imports
Issue:

The code contains multiple redundant and potentially unused imports. For example:
import datetime is imported multiple times.
from datetime import datetime, timedelta is imported twice.
There are imports like import time and from datetime import date, timedelta that may not be used.
Improvement:

Remove duplicate and unused imports to clean up the code and improve readability.
Action:


# Remove redundant imports
import os
import sys
import json
from datetime import datetime, timedelta
import asyncio
import aiohttp
import pytz
import re
from urllib.parse import urlencode
from loguru import logger
from dotenv import load_dotenv
from twilio.rest import Client

# ... rest of the imports related to pipecat, OpenAI, etc.
2. Inefficient Use of aiohttp.ClientSession
Issue:

Each function that makes HTTP requests creates its own aiohttp.ClientSession, which can be inefficient and may lead to a large number of open connections if many requests are made simultaneously.
Improvement:

Create a single aiohttp.ClientSession instance and pass it to the functions that need it. This approach is more efficient and recommended in the aiohttp documentation.
Action:

Instantiate ClientSession in the main function and pass it as a parameter to functions that perform HTTP requests.

async def run_bot(websocket_client, stream_sid):
    async with aiohttp.ClientSession() as session:
        # Pass 'session' to functions that need it
        await some_function(..., session)
Update all functions to accept session as a parameter:

async def whatsapp_message(..., session):
    # Use the passed 'session' instead of creating a new one
3. Repetition in API Calls to Airtable and Twilio
Issue:

There is repetitive code in functions that interact with Airtable and Twilio, such as constructing URLs, headers, and handling responses.
Improvement:

Abstract the repeated logic into helper functions or classes to reduce code duplication and improve maintainability.
Action:

Create helper functions for Airtable API interactions:

async def airtable_get_record(session, table_name, filter_formula):
    url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{table_name}"
    headers = {"Authorization": f"Bearer {AIRTABLE_API_KEY}"}
    params = {
        "filterByFormula": filter_formula,
        "cellFormat": "string",
        "timeZone": "Europe/London",
        "userLocale": "en-gb",
    }
    async with session.get(url, headers=headers, params=params) as response:
        response.raise_for_status()
        return await response.json()
Similarly, create a helper function for Twilio API interactions if needed.
4. Error Handling and Logging
Issue:

Exception handling is too broad in some places, using except Exception as error, which might catch unintended exceptions.
Error messages may not provide sufficient context.
Sensitive information might be logged unintentionally.
Improvement:

Catch specific exceptions where possible.
Enhance logging messages with contextual information.
Ensure that sensitive data (e.g., API keys, personal information) is not included in logs.
Action:

Modify exception handling:

try:
    # Code that may raise an exception
except aiohttp.ClientResponseError as e:
    logger.error(f"HTTP Error: {e.status} {e.message}")
    # Handle specific HTTP error
except Exception as error:
    logger.exception("An unexpected error occurred.")
    # Handle other exceptions
5. Inconsistent Variable Naming and Formatting
Issue:

Variable naming conventions are inconsistent. For example, vehicle_make and Vehicle_Make are mixed (snake_case vs. CamelCase).
String formatting is inconsistent, sometimes using f-strings and other times using string concatenation.
Improvement:

Adopt a consistent naming convention (e.g., snake_case for variables and functions) throughout the code.
Use f-strings consistently for readability.
Action:

Rename variables for consistency:

vehicle_make = record.get("Vehicle_Make", "N/A")
Use f-strings:

logger.debug(f"Booking found for registration: {formatted_registration}")
6. Parsing Dates and Times
Issue:

Functions that parse dates and times (e.g., parse_eta) may not handle all possible user inputs and may fail silently.
The parse_eta function has limited parsing capabilities.
Improvement:

Use a robust date parsing library like dateutil.parser or dateparser to handle a broader range of date and time formats.
Add error handling for parsing failures.
Action:


from dateutil import parser

def parse_eta(eta_string, current_time, timezone):
    try:
        parsed_eta = parser.parse(eta_string, fuzzy=True, default=current_time)
        if parsed_eta.tzinfo is None:
            parsed_eta = timezone.localize(parsed_eta)
        return parsed_eta
    except (parser.ParserError, ValueError) as e:
        logger.error(f"Error parsing ETA: {e}")
        return None
7. Functionality of DepartureFlow Class
Issue:

The DepartureFlow class is defined but not properly implemented or used in the code. There's a placeholder comment indicating incomplete implementation.
Improvement:

Implement the DepartureFlow class if it's needed for managing conversation state, or remove it to reduce confusion.
Action:

If not required, remove the class:

# Remove the DepartureFlow class and related code if not used
8. LLM Configuration and Model Usage
Issue:

The LLM is initialized with api_key=os.getenv("GROQ_API_KEY") and model="llama-3.1-70b-versatile", which may not be compatible with the OpenAILLMService.
The llm service seems to be mismatched with the provided API key and model.
Improvement:

Verify that the LLM service is correctly configured with the appropriate API key and model.
If using OpenAI's service, ensure the API key and model name are valid.
If using a different LLM service, use the correct client or SDK.
Action:

Update the LLM initialization:

llm = OpenAILLMService(
    api_key=os.getenv("OPENAI_API_KEY"),
    model="gpt-3.5-turbo",  # or another valid OpenAI model
)
9. Asynchronous Function Definitions
Issue:

Some functions are defined as async but may not contain any await expressions.
Improvement:

If a function doesn't perform any asynchronous operations, it doesn't need to be async.
Alternatively, ensure that all asynchronous operations within async functions are properly awaited.
Action:

Review each async function and adjust accordingly.

def get_current_time():
    # This function can remain synchronous if it doesn't perform async operations
10. Redundant Code and Comments
Issue:

There are commented-out code sections and placeholder comments that don't contribute to the current functionality.
Improvement:

Remove unnecessary comments and commented-out code to improve clarity.
Action:

Remove unused code:

# tts = DeepgramTTSService(
#     ...
# )

# Remove the commented-out DeepgramTTSService initialization if not used
11. Input Validation and Sanitization
Issue:

Inputs from users (e.g., registration numbers, phone numbers) may not be properly validated or sanitized, potentially leading to errors or security issues.
Improvement:

Implement input validation and sanitization to ensure inputs are in the expected format and to prevent injection attacks or errors.
Action:

Add validation for registration numbers:

def sanitize_registration_number(registration):
    return re.sub(r'\W+', '', registration).upper()
Use this function before processing registration numbers.
12. Potential Security Concerns
Issue:

Sensitive information might be exposed in error messages or logs.
The use of eval or unsafe parsing methods that can introduce security vulnerabilities.
Improvement:

Ensure that error messages do not expose sensitive data.
Use safe methods for parsing and executing code.
Action:

Avoid including sensitive data in logs:

logger.error("An error occurred while updating the phone number.")
13. Concurrency and Shared Resources
Issue:

If the bot handles multiple users simultaneously, shared resources might cause issues if not properly managed.
Improvement:

Ensure that shared resources (e.g., sessions, context variables) are handled in a thread-safe or asynchronous manner.
Action:

Use context variables or appropriate synchronization mechanisms if needed.
14. Error Messages Returned to Users
Issue:

Functions often return generic error messages to users, which might not be helpful.
Improvement:

Provide more user-friendly error messages, and guide the user on how to proceed.
Action:


await result_callback(json.dumps({
    "error": "We couldn't find your booking. Please check your registration number and try again."
}))
15. Comments and Documentation
Issue:

The code lacks docstrings and detailed comments, making it harder to understand the purpose and functionality of functions.
Improvement:

Add docstrings to all functions and classes to describe their purpose, parameters, and return values.
Action:


async def find_booking(function_name, tool_call_id, arguments, llm, context, result_callback):
    """
    Finds a booking in Airtable based on the provided registration number.

    Args:
        function_name (str): The name of the function being called.
        tool_call_id (str): The identifier for the tool call.
        arguments (dict): A dictionary of arguments passed to the function.
        llm: The LLM instance.
        context: The context of the conversation.
        result_callback: A callback function to return the result.

    Returns:
        None
    """
    # Function implementation
16. Type Annotations
Issue:

The code lacks type annotations, which can improve code readability and help with debugging.
Improvement:

Add type hints to function signatures and variable declarations where appropriate.
Action:


from typing import Any, Dict, Callable

async def find_booking(
    function_name: str,
    tool_call_id: str,
    arguments: Dict[str, Any],
    llm: Any,
    context: Any,
    result_callback: Callable[[str], Awaitable[None]],
) -> None:
    # Function implementation
17. Consistency in Asynchronous Programming
Issue:

There's inconsistency in how and when async and await are used, which could lead to unexpected behavior.
Improvement:

Ensure that all asynchronous functions that perform I/O operations use await when calling asynchronous functions.
Use synchronous functions where appropriate.
Action:


async def update_phone_number(...):
    # Ensure all I/O operations are awaited
    response = await session.get(url, headers=headers)
18. Optimizing Function Calls and Performance
Issue:

Some functions may make unnecessary API calls or perform redundant computations.
Improvement:

Optimize functions to reduce the number of external API calls.
Use caching where possible and appropriate.
Action:

Consider implementing caching for repeated data requests if the data doesn't change often.
19. Ensuring Compliance with Third-Party API Rate Limits
Issue:

Frequent API calls may exceed the rate limits of services like Airtable, Twilio, or OpenAI.
Improvement:

Monitor the rate of API calls and implement throttling or request queuing mechanisms if necessary.
Action:

Use asyncio's rate limiting mechanisms or third-party libraries to manage API call rates.
20. Testing and Validation
Issue:

The code lacks unit tests and integration tests to verify functionality and prevent regressions.
Improvement:

Write tests for critical functions to ensure they work as expected under various conditions.
Action:

Use a testing framework like unittest or pytest to write tests.
Summary of Suggested Improvements:

Consolidate and manage aiohttp.ClientSession instances efficiently.
Abstract repetitive API call logic into helper functions.
Improve error handling by catching specific exceptions and enhancing log messages.
Ensure consistent variable naming and formatting.
Use robust date parsing libraries for handling dates and times.
Properly implement or remove unused classes like DepartureFlow.
Verify and correct the configuration of LLM services and models.
Add input validation and sanitization to prevent errors and security issues.
Enhance documentation by adding docstrings and comments.
Use type annotations to improve code clarity.
Ensure asynchronous functions are used correctly.
Optimize function calls and be mindful of API rate limits.
Write tests to validate functionality.
Example of a Refactored Function (find_booking):


async def find_booking(
    function_name: str,
    tool_call_id: str,
    arguments: Dict[str, Any],
    llm: Any,
    context: Any,
    result_callback: Callable[[str], Awaitable[None]],
    session: aiohttp.ClientSession,
) -> None:
    """
    Find a booking in Airtable based on the vehicle registration number.

    Args:
        function_name (str): Name of the function being called.
        tool_call_id (str): Identifier for the tool call.
        arguments (dict): Arguments passed to the function.
        llm: Language model instance.
        context: Contextual information.
        result_callback: Callback to return the result.
        session: aiohttp client session for making HTTP requests.

    Returns:
        None
    """
    registration = arguments.get("registration", "")
    is_arrival = arguments.get("is_arrival", False)
    logger.debug(f"Raw input - registration: {registration}, isArrival: {is_arrival}")

    formatted_registration = sanitize_registration_number(registration)
    logger.debug(f"Formatted registration: {formatted_registration}")

    table_name = AIRTABLE_ARRIVALS_TABLE if is_arrival else AIRTABLE_DEPARTURES_TABLE
    filter_formula = f'UPPER({{Registration}})="{formatted_registration}"'

    try:
        data = await airtable_get_record(session, table_name, filter_formula)
        if data.get("records"):
            record = data["records"][0]["fields"]
            # Process the record and prepare the response
            await result_callback(json.dumps({
                "found": True,
                "customerName": record.get("Name", "Not provided"),
                # ... other fields
            }))
        else:
            logger.warning(f"No booking found for registration: {formatted_registration}")
            await result_callback(json.dumps({
                "found": False,
                "error": f"No booking found for registration {formatted_registration}.",
            }))
    except Exception as error:
        logger.exception("Error finding booking.")
        await result_callback(json.dumps({
            "found": False,
            "error": "Failed to find booking due to an internal error.",
        }))
Final Notes:

Testing the Bot: After implementing the improvements, thoroughly test the bot to ensure all functionalities are working correctly and efficiently.
Documentation: Update any related documentation or README files to reflect changes and to assist future developers.
Continuous Improvement: Regularly review and refactor the codebase to keep it maintainable and up-to-date with best practices.
By addressing these issues and implementing the suggested improvements, your AI Call Bot will be more robust, efficient, and maintainable.