Added Daily SIP room creation utility (#2560)

* Added Daily SIP room creation utility to configure() function

* Add sip_codecs to the DailyRoomSipParams
This commit is contained in:
Mark Backman
2025-09-02 11:12:04 -07:00
committed by GitHub
parent 0d8ab7abca
commit 0386599163
3 changed files with 137 additions and 29 deletions

View File

@@ -51,6 +51,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Added `sip_codecs` to the `DailyRoomSipParams`.
- Updated the `configure()` function in `pipecat.runner.daily` to include new
args to create SIP-enabled rooms. Additionally, added new args to control the
room and token expiration durations.
- `pipecat.frames.frames.KeypadEntry` is deprecated and has been moved to
`pipecat.audio.dtmf.types.KeypadEntry`.

View File

@@ -10,6 +10,10 @@ This module provides helper functions for creating and configuring Daily rooms
and authentication tokens. It automatically creates temporary rooms for
development or uses existing rooms specified via environment variables.
Functions:
- configure(): Create a standard or SIP-enabled Daily room, returning a DailyRoomConfig object.
Environment variables:
- DAILY_API_KEY - Daily API key for room/token creation (required)
@@ -22,39 +26,95 @@ Example::
from pipecat.runner.daily import configure
async with aiohttp.ClientSession() as session:
# Standard room
room_url, token = await configure(session)
# Use room_url and token with DailyTransport
# SIP-enabled room for phone calls
config = await configure(session, sip_caller_phone="+15551234567")
# config contains: room_url, token, sip_endpoint
"""
import os
import time
import uuid
from typing import Tuple
from typing import Dict, List, Optional
import aiohttp
from loguru import logger
from pydantic import BaseModel
from pipecat.transports.services.helpers.daily_rest import (
DailyRESTHelper,
DailyRoomParams,
DailyRoomProperties,
DailyRoomSipParams,
)
async def configure(aiohttp_session: aiohttp.ClientSession) -> Tuple[str, str]:
"""Configure Daily room URL and token from environment variables.
class DailyRoomConfig(BaseModel):
"""Configuration returned when creating a Daily room.
Attributes:
room_url: The Daily room URL for joining the meeting.
token: Authentication token for the bot to join the room.
sip_endpoint: SIP endpoint URI for phone connections (None for standard rooms).
"""
room_url: str
token: str
sip_endpoint: Optional[str] = None
def __iter__(self):
"""Enable tuple unpacking for backward compatibility.
Allows: room_url, token = await configure(session)
"""
yield self.room_url
yield self.token
async def configure(
aiohttp_session: aiohttp.ClientSession,
*,
room_exp_duration: Optional[float] = 2.0,
token_exp_duration: Optional[float] = 2.0,
sip_caller_phone: Optional[str] = None,
sip_enable_video: Optional[bool] = False,
sip_num_endpoints: Optional[int] = 1,
sip_codecs: Optional[Dict[str, List[str]]] = None,
) -> DailyRoomConfig:
"""Configure Daily room URL and token with optional SIP capabilities.
This function will either:
1. Use an existing room URL from DAILY_SAMPLE_ROOM_URL environment variable
1. Use an existing room URL from DAILY_SAMPLE_ROOM_URL environment variable (standard mode only)
2. Create a new temporary room automatically if no URL is provided
Args:
aiohttp_session: HTTP session for making API requests.
room_exp_duration: Room expiration time in hours.
token_exp_duration: Token expiration time in hours.
sip_caller_phone: Phone number or identifier for SIP display name.
When provided, enables SIP functionality and returns SipRoomConfig.
sip_enable_video: Whether video is enabled for SIP.
sip_num_endpoints: Number of allowed SIP endpoints.
sip_codecs: Codecs to support for audio and video. If None, uses Daily defaults.
Example: {"audio": ["OPUS"], "video": ["H264"]}
Returns:
Tuple containing the room URL and authentication token.
DailyRoomConfig: Object with room_url, token, and optional sip_endpoint.
Supports tuple unpacking for backward compatibility: room_url, token = await configure(session)
Raises:
Exception: If DAILY_API_KEY is not provided in environment variables.
Examples::
# Standard room
room_url, token = await configure(session)
# SIP-enabled room
sip_config = await configure(session, sip_caller_phone="+15551234567")
print(f"SIP endpoint: {sip_config.sip_endpoint}")
"""
# Check for required API key
api_key = os.getenv("DAILY_API_KEY")
@@ -64,8 +124,8 @@ async def configure(aiohttp_session: aiohttp.ClientSession) -> Tuple[str, str]:
"Get your API key from https://dashboard.daily.co/developers"
)
# Check for existing room URL
existing_room_url = os.getenv("DAILY_SAMPLE_ROOM_URL")
# Determine if SIP mode is enabled
sip_enabled = sip_caller_phone is not None
daily_rest_helper = DailyRESTHelper(
daily_api_key=api_key,
@@ -73,36 +133,75 @@ async def configure(aiohttp_session: aiohttp.ClientSession) -> Tuple[str, str]:
aiohttp_session=aiohttp_session,
)
if existing_room_url:
# Use existing room
print(f"Using existing Daily room: {existing_room_url}")
# Check for existing room URL (only in standard mode)
existing_room_url = os.getenv("DAILY_SAMPLE_ROOM_URL")
if existing_room_url and not sip_enabled:
# Use existing room (standard mode only)
logger.info(f"Using existing Daily room: {existing_room_url}")
room_url = existing_room_url
else:
# Create a new temporary room
room_name = f"pipecat-{uuid.uuid4().hex[:8]}"
print(f"Creating new Daily room: {room_name}")
# Calculate expiration time: current time + 2 hours
expiration_time = time.time() + (2 * 60 * 60) # 2 hours from now
# Create token and return standard format
expiry_time: float = token_exp_duration * 60 * 60
token = await daily_rest_helper.get_token(room_url, expiry_time)
return DailyRoomConfig(room_url=room_url, token=token)
# Create room properties with absolute timestamp
room_properties = DailyRoomProperties(
exp=expiration_time, # Absolute Unix timestamp
eject_at_room_exp=True,
# Create a new room
room_prefix = "pipecat-sip" if sip_enabled else "pipecat"
room_name = f"{room_prefix}-{uuid.uuid4().hex[:8]}"
logger.info(f"Creating new Daily room: {room_name}")
# Calculate expiration time
expiration_time = time.time() + (room_exp_duration * 60 * 60)
# Create room properties
room_properties = DailyRoomProperties(
exp=expiration_time,
eject_at_room_exp=True,
)
# Add SIP configuration if enabled
if sip_enabled:
sip_params = DailyRoomSipParams(
display_name=sip_caller_phone,
video=sip_enable_video,
sip_mode="dial-in",
num_endpoints=sip_num_endpoints,
codecs=sip_codecs,
)
room_properties.sip = sip_params
room_properties.enable_dialout = True # Enable outbound calls if needed
room_properties.start_video_off = not sip_enable_video # Voice-only by default
# Create room parameters
room_params = DailyRoomParams(name=room_name, properties=room_properties)
# Create room parameters
room_params = DailyRoomParams(name=room_name, properties=room_properties)
try:
room_response = await daily_rest_helper.create_room(room_params)
room_url = room_response.url
print(f"Created Daily room: {room_url}")
logger.info(f"Created Daily room: {room_url}")
# Create a meeting token for the room with an expiration 2 hours in the future
expiry_time: float = 2 * 60 * 60
token = await daily_rest_helper.get_token(room_url, expiry_time)
# Create meeting token
token_expiry_seconds = token_exp_duration * 60 * 60
token = await daily_rest_helper.get_token(room_url, token_expiry_seconds)
return (room_url, token)
if sip_enabled:
# Return SIP configuration object
sip_endpoint = room_response.config.sip_endpoint
logger.info(f"SIP endpoint: {sip_endpoint}")
return DailyRoomConfig(
room_url=room_url,
token=token,
sip_endpoint=sip_endpoint,
)
else:
# Return standard configuration
return DailyRoomConfig(room_url=room_url, token=token)
except Exception as e:
error_msg = f"Error creating Daily room: {e}"
logger.error(error_msg)
raise
# Keep this for backwards compatibility, but mark as deprecated

View File

@@ -10,7 +10,7 @@ Methods that wrap the Daily API to create rooms, check room URLs, and get meetin
"""
import time
from typing import Literal, Optional
from typing import Dict, List, Literal, Optional
from urllib.parse import urlparse
import aiohttp
@@ -25,12 +25,15 @@ class DailyRoomSipParams(BaseModel):
video: Whether video is enabled for SIP.
sip_mode: SIP connection mode, typically 'dial-in'.
num_endpoints: Number of allowed SIP endpoints.
codecs: Codecs to support for audio and video. If None, uses Daily defaults.
Example: {"audio": ["OPUS"], "video": ["H264"]}
"""
display_name: str = "sw-sip-dialin"
video: bool = False
sip_mode: str = "dial-in"
num_endpoints: int = 1
codecs: Optional[Dict[str, List[str]]] = None
class RecordingsBucketConfig(BaseModel):