Files
2026-07-13 13:39:38 +08:00

93 lines
3.1 KiB
Python

import asyncio
import os
from typing import Any
import aiohttp
from livekit.agents import (
DEFAULT_API_CONNECT_OPTIONS,
NOT_GIVEN,
APIConnectionError,
APIConnectOptions,
APIStatusError,
NotGivenOr,
utils,
)
from .errors import DIDException
from .log import logger
DEFAULT_API_URL = "https://api.d-id.com"
class DIDAPI:
def __init__(
self,
api_key: NotGivenOr[str] = NOT_GIVEN,
api_url: NotGivenOr[str] = NOT_GIVEN,
*,
conn_options: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS,
session: aiohttp.ClientSession | None = None,
) -> None:
did_api_key = api_key if utils.is_given(api_key) else os.getenv("DID_API_KEY")
if not did_api_key:
raise DIDException("DID_API_KEY must be set")
self._api_key = did_api_key
self._api_url = api_url if utils.is_given(api_url) else DEFAULT_API_URL
self._conn_options = conn_options
self._session = session or aiohttp.ClientSession()
async def join_session(
self,
*,
agent_id: str,
transport: dict[str, Any],
audio_config: dict[str, Any],
) -> str:
"""Dispatch a D-ID avatar worker into the room.
Returns the session id.
"""
payload: dict[str, Any] = {
"transport": transport,
"audio_config": audio_config,
}
response_data = await self._post(f"v2/agents/{agent_id}/sessions/join", payload)
return response_data["id"] # type: ignore
async def _post(self, endpoint: str, payload: dict[str, Any]) -> dict[str, Any]:
url = f"{self._api_url}/{endpoint}"
num_attempts = self._conn_options.max_retry + 1
for attempt in range(num_attempts):
try:
async with self._session.post(
url,
headers={
"Content-Type": "application/json",
"Authorization": f"Basic {self._api_key}",
},
json=payload,
timeout=aiohttp.ClientTimeout(sock_connect=self._conn_options.timeout),
) as response:
if not response.ok:
text = await response.text()
raise APIStatusError(
"D-ID API error",
status_code=response.status,
body=text,
)
return await response.json() # type: ignore
except APIStatusError:
raise
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
logger.warning(
f"D-ID API request failed (attempt {attempt + 1}/{num_attempts})",
extra={"error": str(e)},
)
if attempt == num_attempts - 1:
raise APIConnectionError(f"Failed to connect to D-ID API at {url}") from e
await asyncio.sleep(self._conn_options.retry_interval)
raise APIConnectionError(f"Failed to connect to D-ID API at {url}")