[GH-ISSUE #13168] issue: Type Error When Executing a Custom Tool #103826

Closed
opened 2026-05-18 01:23:32 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @heinrihs-s on GitHub (Apr 23, 2025).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/13168

Check Existing Issues

  • I have searched the existing issues and discussions.
  • I am using the latest version of Open WebUI.

Installation Method

Docker

Open WebUI Version

v0.6.5

Ollama Version (if applicable)

0.6.5

Operating System

Docker

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have listed steps to reproduce the bug in detail.

Expected Behavior

When using the book_zoom_meeting() method, the system should:

Successfully validate the provided meeting parameters (topic, date, time, duration, etc.)
Format the start time correctly for the Zoom API
Make an API request to Zoom to create the new meeting
Return a formatted string containing the meeting details, including:

Topic
Date and time
Duration
Meeting ID
Join URL
Password status
Account information

The function should handle all input validation gracefully and return appropriate error messages for invalid inputs rather than throwing a type error. The core functionality should create a new Zoom meeting that appears in the user's Zoom account and can be accessed via the returned join URL.
This expected behavior would match the successful implementation of the existing get_upcoming_zoom_meetings() method, which correctly interacts with the Zoom API and returns formatted data.

Actual Behavior

After adding the Zoom booking functionality to my Zoom Meetings plugin, the system is throwing a type error when attempting to book a meeting.

Original Implementation (Working)

The original implementation (v0.1.0) provided only meeting listing functionality and worked properly:

# Helper functions included:
# - _get_zoom_token()
# - _fetch_upcoming_meetings()
# - _fmt_iso()

class Tools:
    def __init__(self):
        self.citation = True

    def get_upcoming_zoom_meetings(self, user: str = "me") -> str:
        # Implementation details...
        # Returns formatted meeting list

New Implementation (Error)

After adding the booking functionality, i am receiving a type error:

# Added new helper functions:
# - _validate_time_input()
# - _book_zoom_meeting()

# Added to Tools class:
def book_zoom_meeting(
    self,
    topic: str,
    date: str,
    time: str,
    duration: Union[int, str],
    agenda: str = "",
    user: str = "me",
    password: str = None
) -> str:
    # Implementation details...
    # Attempts to book a meeting and return formatted details

Changes Implemented

  1. Added type hint imports: Optional and Union from typing
  2. Added json module import
  3. Added new helper functions:
    • _validate_time_input(): For date/time validation and formatting
    • _book_zoom_meeting(): Core function to create Zoom meetings via API
  4. Added new public method to Tools class:
    • book_zoom_meeting(): User-facing method with parameter validation

Steps to Reproduce

Load the Keyless Zoom Meetings plugin v0.1.1
Attempt to book a meeting with the following parameters:

Topic: "Test Meeting"
Date: "2025-04-30"
Time: "14:00"
Duration: 30
Agenda: "Test meeting description"

works_well.py:

"""title:   Keyless Zoom Meetings
author:  spyci
author_url: https://github.com/open-webui
funding_url: https://github.com/open-webui
version: 0.1.0
"""

import base64
import datetime as _dt
import requests
from typing import Dict, Any, List

# --------------------------------------------------------------------------- #
# ⚙️  Hard-coded credentials supplied by the user (server-to-server OAuth)    #
# --------------------------------------------------------------------------- #

ACCOUNT_ID = "insertYours"
CLIENT_ID = "insertYours"
CLIENT_SECRET = "insertYours"

# --------------------------------------------------------------------------- #
# 🛠️  Helper functions                                                        #
# --------------------------------------------------------------------------- #


def _get_zoom_token() -> str:
    """
    Exchange the account credentials for a short-lived bearer token.
    """
    auth_header = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
    headers = {
        "Authorization": f"Basic {auth_header}",
        "Content-Type": "application/x-www-form-urlencoded",
    }
    payload = {
        "grant_type": "account_credentials",
        "account_id": ACCOUNT_ID,
    }
    resp = requests.post("https://zoom.us/oauth/token", headers=headers, data=payload)
    resp.raise_for_status()
    return resp.json()["access_token"]  # expires in ~1 hour


def _fetch_upcoming_meetings(
    token: str, user_id: str = "me", page_size: int = 30
) -> List[Dict[str, Any]]:
    """
    Retrieve *all* upcoming meetings for the given user (may paginate).
    """
    url = f"https://api.zoom.us/v2/users/{user_id}/meetings"
    headers = {"Authorization": f"Bearer {token}"}
    params = {"type": "upcoming", "page_size": page_size}

    meetings: List[Dict[str, Any]] = []
    while True:
        resp = requests.get(url, headers=headers, params=params)
        resp.raise_for_status()
        data = resp.json()
        meetings.extend(data.get("meetings", []))

        next_page = data.get("next_page_token")
        if not next_page:
            break
        params["next_page_token"] = next_page  # paginate

    return meetings


def _fmt_iso(iso: str, out: str = "%Y-%m-%d %I:%M %p") -> str:
    """
    Render Zoom’s ISO-8601 timestamp in a friendlier local form.
    """
    return _dt.datetime.fromisoformat(iso.replace("Z", "+00:00")).strftime(out)


# --------------------------------------------------------------------------- #
# 🧰  Public tool class (mirrors Keyless Weather style)                        #
# --------------------------------------------------------------------------- #


class Tools:
    def __init__(self):
        # Open WebUI checks this flag to decide whether to append sources
        self.citation = True

    def get_upcoming_zoom_meetings(self, user: str = "me") -> str:
        """
        Return a human-readable description of all upcoming Zoom meetings.

        :param user: Zoom user ID e.g. "me" or an email / userId.
                     Defaults to "me" for the authenticated account.
        :return:     Formatted markdown for the LLM to speak back.
        """
        # 1️⃣  Get bearer token
        try:
            token = _get_zoom_token()
        except Exception as e:
            return f"Error retrieving Zoom access token: {e}"

        # 2️⃣  Query meetings
        try:
            raw_meetings = _fetch_upcoming_meetings(token, user_id=user)
        except Exception as e:
            return f"Error fetching Zoom meetings: {e}"

        if not raw_meetings:
            return "No upcoming Zoom meetings found."

        # 3️⃣  Transform into an easy-to-render dict
        meetings_map = {
            _fmt_iso(m["start_time"]): {
                "topic": m["topic"],
                "duration": f"{m['duration']} min",
                "meeting_id": m["id"],
                "join_url": m["join_url"],
            }
            for m in raw_meetings
        }

        # 4️⃣  Summarise
        n = len(meetings_map)
        return f"""
Provide a table of the upcoming Zoom meetings for **{user}**:\
 {meetings_map}
Include one summary sentence noting there {'is' if n == 1 else 'are'} {n} upcoming meeting{'s' if n != 1 else ''} in total.
"""

not_working_anymore.py:

"""title:   Keyless Zoom Meetings
author:  spyci
author_url: https://github.com/open-webui
funding_url: https://github.com/open-webui
version: 0.1.1
"""

import base64
import datetime as _dt
import json
import requests
from typing import Dict, Any, List, Optional, Union

# --------------------------------------------------------------------------- #
# ⚙️  Hard-coded credentials supplied by the user (server-to-server OAuth)    #
# --------------------------------------------------------------------------- #

ACCOUNT_ID = "insertYours"
CLIENT_ID = "insertYours"
CLIENT_SECRET = "insertYours"

# --------------------------------------------------------------------------- #
# 🛠️  Helper functions                                                        #
# --------------------------------------------------------------------------- #


def _get_zoom_token() -> str:
    """
    Exchange the account credentials for a short-lived bearer token.
    """
    auth_header = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
    headers = {
        "Authorization": f"Basic {auth_header}",
        "Content-Type": "application/x-www-form-urlencoded",
    }
    payload = {
        "grant_type": "account_credentials",
        "account_id": ACCOUNT_ID,
    }
    resp = requests.post("https://zoom.us/oauth/token", headers=headers, data=payload)
    resp.raise_for_status()
    return resp.json()["access_token"]  # expires in ~1 hour


def _fetch_upcoming_meetings(
    token: str, user_id: str = "me", page_size: int = 30
) -> List[Dict[str, Any]]:
    """
    Retrieve *all* upcoming meetings for the given user (may paginate).
    """
    url = f"https://api.zoom.us/v2/users/{user_id}/meetings"
    headers = {"Authorization": f"Bearer {token}"}
    params = {"type": "upcoming", "page_size": page_size}

    meetings: List[Dict[str, Any]] = []
    while True:
        resp = requests.get(url, headers=headers, params=params)
        resp.raise_for_status()
        data = resp.json()
        meetings.extend(data.get("meetings", []))

        next_page = data.get("next_page_token")
        if not next_page:
            break
        params["next_page_token"] = next_page  # paginate

    return meetings


def _fmt_iso(iso: str, out: str = "%Y-%m-%d %I:%M %p") -> str:
    """
    Render Zoom's ISO-8601 timestamp in a friendlier local form.
    """
    return _dt.datetime.fromisoformat(iso.replace("Z", "+00:00")).strftime(out)


def _validate_time_input(date_str: str, time_str: str) -> Optional[str]:
    """
    Validate the date and time input and convert to ISO format.
    Returns None if validation fails.
    """
    try:
        # Combine date and time
        datetime_str = f"{date_str} {time_str}"
        # Try to parse the combined string
        dt_obj = _dt.datetime.strptime(datetime_str, "%Y-%m-%d %H:%M")
        # Return ISO format
        return dt_obj.isoformat() + "Z"
    except ValueError:
        return None


def _book_zoom_meeting(
    token: str,
    user_id: str,
    topic: str,
    start_time: str,
    duration: int,
    agenda: str = "",
    password: Optional[str] = None,
    settings: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """
    Book a new Zoom meeting with the specified parameters.
    """
    url = f"https://api.zoom.us/v2/users/{user_id}/meetings"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    
    # Default settings if none provided
    if settings is None:
        settings = {
            "host_video": True,
            "participant_video": False,
            "join_before_host": True,
            "mute_upon_entry": True,
            "waiting_room": False,
            "auto_recording": "none"
        }
    
    # Build payload
    payload = {
        "topic": topic,
        "type": 2,  # Scheduled meeting
        "start_time": start_time,
        "duration": duration,
        "timezone": "UTC",
        "agenda": agenda,
        "settings": settings
    }
    
    # Add optional password if provided
    if password:
        payload["password"] = password
    
    # Make API request
    resp = requests.post(url, headers=headers, data=json.dumps(payload))
    resp.raise_for_status()
    
    return resp.json()


# --------------------------------------------------------------------------- #
# 🧰  Public tool class (mirrors Keyless Weather style)                        #
# --------------------------------------------------------------------------- #


class Tools:
    def __init__(self):
        # Open WebUI checks this flag to decide whether to append sources
        self.citation = True

    def get_upcoming_zoom_meetings(self, user: str = "me") -> str:
        """
        Return a human-readable description of all upcoming Zoom meetings.

        :param user: Zoom user ID e.g. "me" or an email / userId.
                     Defaults to "me" for the authenticated account.
        :return:     Formatted markdown for the LLM to speak back.
        """
        # 1️⃣  Get bearer token
        try:
            token = _get_zoom_token()
        except Exception as e:
            return f"Error retrieving Zoom access token: {e}"

        # 2️⃣  Query meetings
        try:
            raw_meetings = _fetch_upcoming_meetings(token, user_id=user)
        except Exception as e:
            return f"Error fetching Zoom meetings: {e}"

        if not raw_meetings:
            return "No upcoming Zoom meetings found."

        # 3️⃣  Transform into an easy-to-render dict
        meetings_map = {
            _fmt_iso(m["start_time"]): {
                "topic": m["topic"],
                "duration": f"{m['duration']} min",
                "meeting_id": m["id"],
                "join_url": m["join_url"],
            }
            for m in raw_meetings
        }

        # 4️⃣  Summarise
        n = len(meetings_map)
        return f"""
Provide a table of the upcoming Zoom meetings for **{user}**:\
 {meetings_map}
Include one summary sentence noting there {'is' if n == 1 else 'are'} {n} upcoming meeting{'s' if n != 1 else ''} in total.
"""

    def book_zoom_meeting(
        self,
        topic: str,
        date: str,
        time: str,
        duration: Union[int, str],
        agenda: str = "",
        user: str = "me",
        password: str = None
    ) -> str:
        """
        Book a new Zoom meeting.

        :param topic:    Meeting title/topic (required)
        :param date:     Meeting date in YYYY-MM-DD format (required)
        :param time:     Meeting time in 24-hour format HH:MM (required)
        :param duration: Meeting duration in minutes (required)
        :param agenda:   Meeting description/agenda (optional)
        :param user:     Zoom user ID, defaults to "me" (optional)
        :param password: Meeting password (optional)
        :return:         Formatted markdown with meeting details or error message
        """
        # Validate inputs
        if not topic or not date or not time:
            return "Error: Meeting topic, date, and time are required."
        
        # Convert duration to integer if provided as string
        try:
            duration_minutes = int(duration)
        except (ValueError, TypeError):
            return "Error: Duration must be a valid number of minutes."
        
        # Validate and format the date/time
        start_time = _validate_time_input(date, time)
        if not start_time:
            return "Error: Invalid date or time format. Please use YYYY-MM-DD for date and HH:MM for time (24-hour format)."
        
        # Get token
        try:
            token = _get_zoom_token()
        except Exception as e:
            return f"Error retrieving Zoom access token: {e}"
        
        # Book the meeting
        try:
            meeting_data = _book_zoom_meeting(
                token=token,
                user_id=user,
                topic=topic,
                start_time=start_time,
                duration=duration_minutes,
                agenda=agenda,
                password=password
            )
            
            # Format the response
            meeting_time = _fmt_iso(meeting_data["start_time"])
            return f"""
Meeting successfully booked! Here are the details:

- **Topic**: {meeting_data["topic"]}
- **Time**: {meeting_time}
- **Duration**: {meeting_data["duration"]} minutes
- **Meeting ID**: {meeting_data["id"]}
- **Join URL**: {meeting_data["join_url"]}
- **Password**: {"(Protected)" if password else "(None)"}

The meeting has been created under the account for user: {user}
"""
        except Exception as e:
            return f"Error booking Zoom meeting: {str(e)}"

Logs & Screenshots

Image

Image

Additional Information

Possible Causes

Type mismatch in date/time conversion
Issue with parameter typing in the book_zoom_meeting() method
Problem with the API request payload formatting

Originally created by @heinrihs-s on GitHub (Apr 23, 2025). Original GitHub issue: https://github.com/open-webui/open-webui/issues/13168 ### Check Existing Issues - [x] I have searched the existing issues and discussions. - [x] I am using the latest version of Open WebUI. ### Installation Method Docker ### Open WebUI Version v0.6.5 ### Ollama Version (if applicable) 0.6.5 ### Operating System Docker ### Browser (if applicable) _No response_ ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have listed steps to reproduce the bug in detail. ### Expected Behavior When using the book_zoom_meeting() method, the system should: Successfully validate the provided meeting parameters (topic, date, time, duration, etc.) Format the start time correctly for the Zoom API Make an API request to Zoom to create the new meeting Return a formatted string containing the meeting details, including: Topic Date and time Duration Meeting ID Join URL Password status Account information The function should handle all input validation gracefully and return appropriate error messages for invalid inputs rather than throwing a type error. The core functionality should create a new Zoom meeting that appears in the user's Zoom account and can be accessed via the returned join URL. This expected behavior would match the successful implementation of the existing get_upcoming_zoom_meetings() method, which correctly interacts with the Zoom API and returns formatted data. ### Actual Behavior After adding the Zoom booking functionality to my Zoom Meetings plugin, the system is throwing a type error when attempting to book a meeting. ## Original Implementation (Working) The original implementation (v0.1.0) provided only meeting listing functionality and worked properly: ```python # Helper functions included: # - _get_zoom_token() # - _fetch_upcoming_meetings() # - _fmt_iso() class Tools: def __init__(self): self.citation = True def get_upcoming_zoom_meetings(self, user: str = "me") -> str: # Implementation details... # Returns formatted meeting list ``` ## New Implementation (Error) After adding the booking functionality, i am receiving a type error: ```python # Added new helper functions: # - _validate_time_input() # - _book_zoom_meeting() # Added to Tools class: def book_zoom_meeting( self, topic: str, date: str, time: str, duration: Union[int, str], agenda: str = "", user: str = "me", password: str = None ) -> str: # Implementation details... # Attempts to book a meeting and return formatted details ``` ## Changes Implemented 1. Added type hint imports: `Optional` and `Union` from typing 2. Added `json` module import 3. Added new helper functions: - `_validate_time_input()`: For date/time validation and formatting - `_book_zoom_meeting()`: Core function to create Zoom meetings via API 4. Added new public method to `Tools` class: - `book_zoom_meeting()`: User-facing method with parameter validation ### Steps to Reproduce Load the Keyless Zoom Meetings plugin v0.1.1 Attempt to book a meeting with the following parameters: Topic: "Test Meeting" Date: "2025-04-30" Time: "14:00" Duration: 30 Agenda: "Test meeting description" works_well.py: ``` """title: Keyless Zoom Meetings author: spyci author_url: https://github.com/open-webui funding_url: https://github.com/open-webui version: 0.1.0 """ import base64 import datetime as _dt import requests from typing import Dict, Any, List # --------------------------------------------------------------------------- # # ⚙️ Hard-coded credentials supplied by the user (server-to-server OAuth) # # --------------------------------------------------------------------------- # ACCOUNT_ID = "insertYours" CLIENT_ID = "insertYours" CLIENT_SECRET = "insertYours" # --------------------------------------------------------------------------- # # 🛠️ Helper functions # # --------------------------------------------------------------------------- # def _get_zoom_token() -> str: """ Exchange the account credentials for a short-lived bearer token. """ auth_header = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode() headers = { "Authorization": f"Basic {auth_header}", "Content-Type": "application/x-www-form-urlencoded", } payload = { "grant_type": "account_credentials", "account_id": ACCOUNT_ID, } resp = requests.post("https://zoom.us/oauth/token", headers=headers, data=payload) resp.raise_for_status() return resp.json()["access_token"] # expires in ~1 hour def _fetch_upcoming_meetings( token: str, user_id: str = "me", page_size: int = 30 ) -> List[Dict[str, Any]]: """ Retrieve *all* upcoming meetings for the given user (may paginate). """ url = f"https://api.zoom.us/v2/users/{user_id}/meetings" headers = {"Authorization": f"Bearer {token}"} params = {"type": "upcoming", "page_size": page_size} meetings: List[Dict[str, Any]] = [] while True: resp = requests.get(url, headers=headers, params=params) resp.raise_for_status() data = resp.json() meetings.extend(data.get("meetings", [])) next_page = data.get("next_page_token") if not next_page: break params["next_page_token"] = next_page # paginate return meetings def _fmt_iso(iso: str, out: str = "%Y-%m-%d %I:%M %p") -> str: """ Render Zoom’s ISO-8601 timestamp in a friendlier local form. """ return _dt.datetime.fromisoformat(iso.replace("Z", "+00:00")).strftime(out) # --------------------------------------------------------------------------- # # 🧰 Public tool class (mirrors Keyless Weather style) # # --------------------------------------------------------------------------- # class Tools: def __init__(self): # Open WebUI checks this flag to decide whether to append sources self.citation = True def get_upcoming_zoom_meetings(self, user: str = "me") -> str: """ Return a human-readable description of all upcoming Zoom meetings. :param user: Zoom user ID e.g. "me" or an email / userId. Defaults to "me" for the authenticated account. :return: Formatted markdown for the LLM to speak back. """ # 1️⃣ Get bearer token try: token = _get_zoom_token() except Exception as e: return f"Error retrieving Zoom access token: {e}" # 2️⃣ Query meetings try: raw_meetings = _fetch_upcoming_meetings(token, user_id=user) except Exception as e: return f"Error fetching Zoom meetings: {e}" if not raw_meetings: return "No upcoming Zoom meetings found." # 3️⃣ Transform into an easy-to-render dict meetings_map = { _fmt_iso(m["start_time"]): { "topic": m["topic"], "duration": f"{m['duration']} min", "meeting_id": m["id"], "join_url": m["join_url"], } for m in raw_meetings } # 4️⃣ Summarise n = len(meetings_map) return f""" Provide a table of the upcoming Zoom meetings for **{user}**:\ {meetings_map} Include one summary sentence noting there {'is' if n == 1 else 'are'} {n} upcoming meeting{'s' if n != 1 else ''} in total. """ ``` not_working_anymore.py: ``` """title: Keyless Zoom Meetings author: spyci author_url: https://github.com/open-webui funding_url: https://github.com/open-webui version: 0.1.1 """ import base64 import datetime as _dt import json import requests from typing import Dict, Any, List, Optional, Union # --------------------------------------------------------------------------- # # ⚙️ Hard-coded credentials supplied by the user (server-to-server OAuth) # # --------------------------------------------------------------------------- # ACCOUNT_ID = "insertYours" CLIENT_ID = "insertYours" CLIENT_SECRET = "insertYours" # --------------------------------------------------------------------------- # # 🛠️ Helper functions # # --------------------------------------------------------------------------- # def _get_zoom_token() -> str: """ Exchange the account credentials for a short-lived bearer token. """ auth_header = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode() headers = { "Authorization": f"Basic {auth_header}", "Content-Type": "application/x-www-form-urlencoded", } payload = { "grant_type": "account_credentials", "account_id": ACCOUNT_ID, } resp = requests.post("https://zoom.us/oauth/token", headers=headers, data=payload) resp.raise_for_status() return resp.json()["access_token"] # expires in ~1 hour def _fetch_upcoming_meetings( token: str, user_id: str = "me", page_size: int = 30 ) -> List[Dict[str, Any]]: """ Retrieve *all* upcoming meetings for the given user (may paginate). """ url = f"https://api.zoom.us/v2/users/{user_id}/meetings" headers = {"Authorization": f"Bearer {token}"} params = {"type": "upcoming", "page_size": page_size} meetings: List[Dict[str, Any]] = [] while True: resp = requests.get(url, headers=headers, params=params) resp.raise_for_status() data = resp.json() meetings.extend(data.get("meetings", [])) next_page = data.get("next_page_token") if not next_page: break params["next_page_token"] = next_page # paginate return meetings def _fmt_iso(iso: str, out: str = "%Y-%m-%d %I:%M %p") -> str: """ Render Zoom's ISO-8601 timestamp in a friendlier local form. """ return _dt.datetime.fromisoformat(iso.replace("Z", "+00:00")).strftime(out) def _validate_time_input(date_str: str, time_str: str) -> Optional[str]: """ Validate the date and time input and convert to ISO format. Returns None if validation fails. """ try: # Combine date and time datetime_str = f"{date_str} {time_str}" # Try to parse the combined string dt_obj = _dt.datetime.strptime(datetime_str, "%Y-%m-%d %H:%M") # Return ISO format return dt_obj.isoformat() + "Z" except ValueError: return None def _book_zoom_meeting( token: str, user_id: str, topic: str, start_time: str, duration: int, agenda: str = "", password: Optional[str] = None, settings: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """ Book a new Zoom meeting with the specified parameters. """ url = f"https://api.zoom.us/v2/users/{user_id}/meetings" headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } # Default settings if none provided if settings is None: settings = { "host_video": True, "participant_video": False, "join_before_host": True, "mute_upon_entry": True, "waiting_room": False, "auto_recording": "none" } # Build payload payload = { "topic": topic, "type": 2, # Scheduled meeting "start_time": start_time, "duration": duration, "timezone": "UTC", "agenda": agenda, "settings": settings } # Add optional password if provided if password: payload["password"] = password # Make API request resp = requests.post(url, headers=headers, data=json.dumps(payload)) resp.raise_for_status() return resp.json() # --------------------------------------------------------------------------- # # 🧰 Public tool class (mirrors Keyless Weather style) # # --------------------------------------------------------------------------- # class Tools: def __init__(self): # Open WebUI checks this flag to decide whether to append sources self.citation = True def get_upcoming_zoom_meetings(self, user: str = "me") -> str: """ Return a human-readable description of all upcoming Zoom meetings. :param user: Zoom user ID e.g. "me" or an email / userId. Defaults to "me" for the authenticated account. :return: Formatted markdown for the LLM to speak back. """ # 1️⃣ Get bearer token try: token = _get_zoom_token() except Exception as e: return f"Error retrieving Zoom access token: {e}" # 2️⃣ Query meetings try: raw_meetings = _fetch_upcoming_meetings(token, user_id=user) except Exception as e: return f"Error fetching Zoom meetings: {e}" if not raw_meetings: return "No upcoming Zoom meetings found." # 3️⃣ Transform into an easy-to-render dict meetings_map = { _fmt_iso(m["start_time"]): { "topic": m["topic"], "duration": f"{m['duration']} min", "meeting_id": m["id"], "join_url": m["join_url"], } for m in raw_meetings } # 4️⃣ Summarise n = len(meetings_map) return f""" Provide a table of the upcoming Zoom meetings for **{user}**:\ {meetings_map} Include one summary sentence noting there {'is' if n == 1 else 'are'} {n} upcoming meeting{'s' if n != 1 else ''} in total. """ def book_zoom_meeting( self, topic: str, date: str, time: str, duration: Union[int, str], agenda: str = "", user: str = "me", password: str = None ) -> str: """ Book a new Zoom meeting. :param topic: Meeting title/topic (required) :param date: Meeting date in YYYY-MM-DD format (required) :param time: Meeting time in 24-hour format HH:MM (required) :param duration: Meeting duration in minutes (required) :param agenda: Meeting description/agenda (optional) :param user: Zoom user ID, defaults to "me" (optional) :param password: Meeting password (optional) :return: Formatted markdown with meeting details or error message """ # Validate inputs if not topic or not date or not time: return "Error: Meeting topic, date, and time are required." # Convert duration to integer if provided as string try: duration_minutes = int(duration) except (ValueError, TypeError): return "Error: Duration must be a valid number of minutes." # Validate and format the date/time start_time = _validate_time_input(date, time) if not start_time: return "Error: Invalid date or time format. Please use YYYY-MM-DD for date and HH:MM for time (24-hour format)." # Get token try: token = _get_zoom_token() except Exception as e: return f"Error retrieving Zoom access token: {e}" # Book the meeting try: meeting_data = _book_zoom_meeting( token=token, user_id=user, topic=topic, start_time=start_time, duration=duration_minutes, agenda=agenda, password=password ) # Format the response meeting_time = _fmt_iso(meeting_data["start_time"]) return f""" Meeting successfully booked! Here are the details: - **Topic**: {meeting_data["topic"]} - **Time**: {meeting_time} - **Duration**: {meeting_data["duration"]} minutes - **Meeting ID**: {meeting_data["id"]} - **Join URL**: {meeting_data["join_url"]} - **Password**: {"(Protected)" if password else "(None)"} The meeting has been created under the account for user: {user} """ except Exception as e: return f"Error booking Zoom meeting: {str(e)}" ``` ### Logs & Screenshots ![Image](https://github.com/user-attachments/assets/9b3b2b89-7d21-4633-9619-65b38782d5f4) ![Image](https://github.com/user-attachments/assets/e9815d87-4af4-4b66-b110-8c2ee4049831) ### Additional Information Possible Causes Type mismatch in date/time conversion Issue with parameter typing in the book_zoom_meeting() method Problem with the API request payload formatting
GiteaMirror added the bug label 2026-05-18 01:23:32 -05:00
Author
Owner

@silentoplayz commented on GitHub (Apr 23, 2025):

Please adjust the title for this issue to be descriptive of the issue at a quick glance.

<!-- gh-comment-id:2824692401 --> @silentoplayz commented on GitHub (Apr 23, 2025): Please adjust the title for this issue to be descriptive of the issue at a quick glance.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#103826