Merge pull request #856 from pipecat-ai/mb/daily-rest-helpers

Remove default 5 min exp time for created rooms, add docstrings
This commit is contained in:
Mark Backman
2024-12-13 12:08:58 -05:00
committed by GitHub
2 changed files with 137 additions and 8 deletions

View File

@@ -13,6 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Tamil) and PlayHT (Afrikans, Albanian, Amharic, Arabic, Bengali, Croatian, Tamil) and PlayHT (Afrikans, Albanian, Amharic, Arabic, Bengali, Croatian,
Galician, Hebrew, Mandarin, Serbian, Tagalog, Urdu, Xhosa). Galician, Hebrew, Mandarin, Serbian, Tagalog, Urdu, Xhosa).
### Changed
- Changed: Room expiration (`exp`) in `DailyRoomProperties` is now optional
(None) by default instead of automatically setting a 5-minute expiration
time. You must explicitly set expiration time if desired.
### Deprecated ### Deprecated
- `AWSTTSService` is now deprecated, use `PollyTTSService` instead. - `AWSTTSService` is now deprecated, use `PollyTTSService` instead.

View File

@@ -4,23 +4,29 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
""" """Daily REST Helpers.
Daily REST Helpers
Methods that wrap the Daily API to create rooms, check room URLs, and get meeting tokens. Methods that wrap the Daily API to create rooms, check room URLs, and get meeting tokens.
""" """
import aiohttp
import time import time
from typing import Literal, Optional
from urllib.parse import urlparse from urllib.parse import urlparse
from pydantic import Field, BaseModel, ValidationError import aiohttp
from typing import Literal, Optional from pydantic import BaseModel, Field, ValidationError
class DailyRoomSipParams(BaseModel): class DailyRoomSipParams(BaseModel):
"""SIP configuration parameters for Daily rooms.
Attributes:
display_name: Name shown for the SIP endpoint
video: Whether video is enabled for SIP
sip_mode: SIP connection mode, typically 'dial-in'
num_endpoints: Number of allowed SIP endpoints
"""
display_name: str = "sw-sip-dialin" display_name: str = "sw-sip-dialin"
video: bool = False video: bool = False
sip_mode: str = "dial-in" sip_mode: str = "dial-in"
@@ -28,7 +34,19 @@ class DailyRoomSipParams(BaseModel):
class DailyRoomProperties(BaseModel, extra="allow"): class DailyRoomProperties(BaseModel, extra="allow"):
exp: float = Field(default_factory=lambda: time.time() + 5 * 60) """Properties for configuring a Daily room.
Attributes:
exp: Optional Unix epoch timestamp for room expiration (e.g., time.time() + 300 for 5 minutes)
enable_chat: Whether chat is enabled in the room
enable_emoji_reactions: Whether emoji reactions are enabled
eject_at_room_exp: Whether to remove participants when room expires
enable_dialout: Whether SIP dial-out is enabled
sip: SIP configuration parameters
sip_uri: SIP URI information returned by Daily
"""
exp: Optional[float] = None
enable_chat: bool = False enable_chat: bool = False
enable_emoji_reactions: bool = False enable_emoji_reactions: bool = False
eject_at_room_exp: bool = True eject_at_room_exp: bool = True
@@ -38,6 +56,11 @@ class DailyRoomProperties(BaseModel, extra="allow"):
@property @property
def sip_endpoint(self) -> str: def sip_endpoint(self) -> str:
"""Get the SIP endpoint URI if available.
Returns:
str: SIP endpoint URI or empty string if not available
"""
if not self.sip_uri: if not self.sip_uri:
return "" return ""
else: else:
@@ -45,12 +68,32 @@ class DailyRoomProperties(BaseModel, extra="allow"):
class DailyRoomParams(BaseModel): class DailyRoomParams(BaseModel):
"""Parameters for creating a Daily room.
Attributes:
name: Optional custom name for the room
privacy: Room privacy setting ('private' or 'public')
properties: Room configuration properties
"""
name: Optional[str] = None name: Optional[str] = None
privacy: Literal["private", "public"] = "public" privacy: Literal["private", "public"] = "public"
properties: DailyRoomProperties = Field(default_factory=DailyRoomProperties) properties: DailyRoomProperties = Field(default_factory=DailyRoomProperties)
class DailyRoomObject(BaseModel): class DailyRoomObject(BaseModel):
"""Represents a Daily room returned by the API.
Attributes:
id: Unique room identifier
name: Room name
api_created: Whether room was created via API
privacy: Room privacy setting ('private' or 'public')
url: Full URL for joining the room
created_at: Timestamp of room creation in ISO 8601 format (e.g., "2019-01-26T09:01:22.000Z").
config: Room configuration properties
"""
id: str id: str
name: str name: str
api_created: bool api_created: bool
@@ -61,6 +104,16 @@ class DailyRoomObject(BaseModel):
class DailyRESTHelper: class DailyRESTHelper:
"""Helper class for interacting with Daily's REST API.
Provides methods for creating, managing, and accessing Daily rooms.
Args:
daily_api_key: Your Daily API key
daily_api_url: Daily API base URL (e.g. "https://api.daily.co/v1")
aiohttp_session: Async HTTP session for making requests
"""
def __init__( def __init__(
self, self,
*, *,
@@ -73,13 +126,40 @@ class DailyRESTHelper:
self.aiohttp_session = aiohttp_session self.aiohttp_session = aiohttp_session
def get_name_from_url(self, room_url: str) -> str: def get_name_from_url(self, room_url: str) -> str:
"""Extract room name from a Daily room URL.
Args:
room_url: Full Daily room URL
Returns:
str: Room name portion of the URL
"""
return urlparse(room_url).path[1:] return urlparse(room_url).path[1:]
async def get_room_from_url(self, room_url: str) -> DailyRoomObject: async def get_room_from_url(self, room_url: str) -> DailyRoomObject:
"""Get room details from a Daily room URL.
Args:
room_url: Full Daily room URL
Returns:
DailyRoomObject: DailyRoomObject instance for the room
"""
room_name = self.get_name_from_url(room_url) room_name = self.get_name_from_url(room_url)
return await self._get_room_from_name(room_name) return await self._get_room_from_name(room_name)
async def create_room(self, params: DailyRoomParams) -> DailyRoomObject: async def create_room(self, params: DailyRoomParams) -> DailyRoomObject:
"""Create a new Daily room.
Args:
params: Room configuration parameters
Returns:
DailyRoomObject: DailyRoomObject instance for the created room
Raises:
Exception: If room creation fails or response is invalid
"""
headers = {"Authorization": f"Bearer {self.daily_api_key}"} headers = {"Authorization": f"Bearer {self.daily_api_key}"}
json = {**params.model_dump(exclude_none=True)} json = {**params.model_dump(exclude_none=True)}
async with self.aiohttp_session.post( async with self.aiohttp_session.post(
@@ -101,6 +181,19 @@ class DailyRESTHelper:
async def get_token( async def get_token(
self, room_url: str, expiry_time: float = 60 * 60, owner: bool = True self, room_url: str, expiry_time: float = 60 * 60, owner: bool = True
) -> str: ) -> str:
"""Generate a meeting token for user to join a Daily room.
Args:
room_url: Daily room URL
expiry_time: Token validity duration in seconds (default: 1 hour)
owner: Whether token has owner privileges
Returns:
str: Meeting token
Raises:
Exception: If token generation fails or room URL is missing
"""
if not room_url: if not room_url:
raise Exception( raise Exception(
"No Daily room specified. You must specify a Daily room in order a token to be generated." "No Daily room specified. You must specify a Daily room in order a token to be generated."
@@ -124,10 +217,29 @@ class DailyRESTHelper:
return data["token"] return data["token"]
async def delete_room_by_url(self, room_url: str) -> bool: async def delete_room_by_url(self, room_url: str) -> bool:
"""Delete a room using its URL.
Args:
room_url: Daily room URL
Returns:
bool: True if deletion was successful
"""
room_name = self.get_name_from_url(room_url) room_name = self.get_name_from_url(room_url)
return await self.delete_room_by_name(room_name) return await self.delete_room_by_name(room_name)
async def delete_room_by_name(self, room_name: str) -> bool: async def delete_room_by_name(self, room_name: str) -> bool:
"""Delete a room using its name.
Args:
room_name: Name of the room to delete
Returns:
bool: True if deletion was successful
Raises:
Exception: If deletion fails (excluding 404 Not Found)
"""
headers = {"Authorization": f"Bearer {self.daily_api_key}"} headers = {"Authorization": f"Bearer {self.daily_api_key}"}
async with self.aiohttp_session.delete( async with self.aiohttp_session.delete(
f"{self.daily_api_url}/rooms/{room_name}", headers=headers f"{self.daily_api_url}/rooms/{room_name}", headers=headers
@@ -139,6 +251,17 @@ class DailyRESTHelper:
return True return True
async def _get_room_from_name(self, room_name: str) -> DailyRoomObject: async def _get_room_from_name(self, room_name: str) -> DailyRoomObject:
"""Internal method to get room details by name.
Args:
room_name: Name of the room
Returns:
DailyRoomObject: DailyRoomObject instance for the room
Raises:
Exception: If room is not found or response is invalid
"""
headers = {"Authorization": f"Bearer {self.daily_api_key}"} headers = {"Authorization": f"Bearer {self.daily_api_key}"}
async with self.aiohttp_session.get( async with self.aiohttp_session.get(
f"{self.daily_api_url}/rooms/{room_name}", headers=headers f"{self.daily_api_url}/rooms/{room_name}", headers=headers