Module livekit.agents.beta.workflows.dob

Classes

class GetDOBResult (date_of_birth: date, time_of_birth: time | None = None)
Expand source code
@dataclass
class GetDOBResult:
    date_of_birth: date
    time_of_birth: time | None = None

GetDOBResult(date_of_birth: 'date', time_of_birth: 'time | None' = None)

Instance variables

var date_of_birth : datetime.date
var time_of_birth : datetime.time | None
class GetDOBTask (extra_instructions: str = '',
include_time: bool = False,
chat_ctx: NotGivenOr[llm.ChatContext] = NOT_GIVEN,
turn_detection: NotGivenOr[TurnDetectionMode | None] = NOT_GIVEN,
tools: NotGivenOr[list[llm.Tool | llm.Toolset]] = NOT_GIVEN,
stt: NotGivenOr[stt.STT | None] = NOT_GIVEN,
vad: NotGivenOr[vad.VAD | None] = NOT_GIVEN,
llm: NotGivenOr[llm.LLM | llm.RealtimeModel | None] = NOT_GIVEN,
tts: NotGivenOr[tts.TTS | None] = NOT_GIVEN,
allow_interruptions: NotGivenOr[bool] = NOT_GIVEN,
require_confirmation: NotGivenOr[bool] = NOT_GIVEN)
Expand source code
class GetDOBTask(AgentTask[GetDOBResult]):
    def __init__(
        self,
        extra_instructions: str = "",
        include_time: bool = False,
        chat_ctx: NotGivenOr[llm.ChatContext] = NOT_GIVEN,
        turn_detection: NotGivenOr[TurnDetectionMode | None] = NOT_GIVEN,
        tools: NotGivenOr[list[llm.Tool | llm.Toolset]] = NOT_GIVEN,
        stt: NotGivenOr[stt.STT | None] = NOT_GIVEN,
        vad: NotGivenOr[vad.VAD | None] = NOT_GIVEN,
        llm: NotGivenOr[llm.LLM | llm.RealtimeModel | None] = NOT_GIVEN,
        tts: NotGivenOr[tts.TTS | None] = NOT_GIVEN,
        allow_interruptions: NotGivenOr[bool] = NOT_GIVEN,
        require_confirmation: NotGivenOr[bool] = NOT_GIVEN,
    ) -> None:
        time_instructions = (
            ""
            if not include_time
            else (
                "Also ask for and capture the time of birth if the user knows it. "
                "The time is optional - if the user doesn't know it, proceed without it.\n"
            )
        )
        confirmation_instructions = (
            "Call `confirm_dob` after the user confirmed the date of birth is correct."
        )
        extra = extra_instructions if extra_instructions else ""

        super().__init__(
            instructions=Instructions(
                _BASE_INSTRUCTIONS.format(
                    modality_specific=_AUDIO_SPECIFIC,
                    time_instructions=time_instructions,
                    confirmation_instructions=(
                        confirmation_instructions if require_confirmation is not False else ""
                    ),
                    extra_instructions=extra,
                ),
                text=_BASE_INSTRUCTIONS.format(
                    modality_specific=_TEXT_SPECIFIC,
                    time_instructions=time_instructions,
                    confirmation_instructions=(
                        confirmation_instructions if require_confirmation is True else ""
                    ),
                    extra_instructions=extra,
                ),
            ),
            chat_ctx=chat_ctx,
            turn_detection=turn_detection,
            tools=tools or [],
            stt=stt,
            vad=vad,
            llm=llm,
            tts=tts,
            allow_interruptions=allow_interruptions,
        )

        self._include_time = include_time
        self._require_confirmation = require_confirmation
        self._current_dob: date | None = None
        self._current_time: time | None = None

        if include_time:
            self._tools.append(self._build_update_time_tool())

    async def on_enter(self) -> None:
        prompt = "Ask the user to provide their date of birth."
        if self._include_time:
            prompt = "Ask the user to provide their date of birth and, if they know it, their time of birth."
        self.session.generate_reply(instructions=prompt)

    @function_tool
    async def update_dob(
        self,
        year: int,
        month: int,
        day: int,
        ctx: RunContext,
    ) -> str | None:
        """Update the date of birth provided by the user. Given a spoken month and year (e.g., 'July 2030'), return its numerical representation (7/2030).

        Args:
            year: The birth year (e.g., 1990)
            month: The birth month (1-12)
            day: The birth day (1-31)
        """
        try:
            dob = date(year, month, day)
        except ValueError as e:
            raise ToolError(f"Invalid date: {e}") from None

        today = date.today()
        if dob > today:
            raise ToolError(
                f"Invalid date of birth: {dob.strftime('%B %d, %Y')} is in the future. "
                "Date of birth cannot be a future date."
            )

        self._current_dob = dob

        if not self._confirmation_required(ctx):
            if not self.done():
                self.complete(
                    GetDOBResult(
                        date_of_birth=self._current_dob,
                        time_of_birth=self._current_time,
                    )
                )
            return None

        confirm_tool = self._build_confirm_tool(dob=dob)
        current_tools = [t for t in self.tools if t.id != "confirm_dob"]
        current_tools.append(confirm_tool)
        await self.update_tools(current_tools)

        formatted_date = dob.strftime("%B %d, %Y")
        response = f"The date of birth has been updated to {formatted_date}"

        if self._current_time:
            formatted_time = self._current_time.strftime("%I:%M %p")
            response += f" at {formatted_time}"

        response += (
            "\nRepeat the date back to the user in a natural spoken format.\n"
            "Prompt the user for confirmation, do not call `confirm_dob` directly"
        )

        return response

    def _build_update_time_tool(self) -> llm.FunctionTool:
        @function_tool()
        async def update_time(hour: int, minute: int, ctx: RunContext) -> str | None:
            """Update the time of birth provided by the user.

            Args:
                hour: The birth hour (0-23)
                minute: The birth minute (0-59)
            """
            try:
                birth_time = time(hour, minute)
            except ValueError as e:
                raise ToolError(f"Invalid time: {e}") from None

            self._current_time = birth_time

            if not self._confirmation_required(ctx) and self._current_dob is not None:
                if not self.done():
                    self.complete(
                        GetDOBResult(
                            date_of_birth=self._current_dob,
                            time_of_birth=self._current_time,
                        )
                    )
                return None

            if self._confirmation_required(ctx):
                confirm_tool = self._build_confirm_tool(dob=self._current_dob)
                current_tools = [t for t in self.tools if t.id != "confirm_dob"]
                current_tools.append(confirm_tool)
                await self.update_tools(current_tools)

            formatted_time = birth_time.strftime("%I:%M %p")
            response = f"The time of birth has been updated to {formatted_time}"

            if self._current_dob:
                formatted_date = self._current_dob.strftime("%B %d, %Y")
                response = f"The date and time of birth has been updated to {formatted_date} at {formatted_time}"

            if self._confirmation_required(ctx):
                response += (
                    "\nRepeat the time back to the user in a natural spoken format.\n"
                    "Prompt the user for confirmation, do not call `confirm_dob` directly"
                )
            else:
                response += (
                    "\nThe date of birth has not been provided yet, ask the user to provide it."
                )

            return response

        return update_time

    def _build_confirm_tool(self, *, dob: date | None) -> llm.FunctionTool:
        # confirm tool is only injected after update_dob/update_time is called,
        # preventing the LLM from hallucinating a confirmation without user input
        captured_dob = dob
        captured_time = self._current_time

        @function_tool()
        async def confirm_dob() -> None:
            """Call after the user confirms the date of birth is correct."""
            if captured_dob != self._current_dob or captured_time != self._current_time:
                self.session.generate_reply(
                    instructions="The date of birth has changed since confirmation was requested, ask the user to confirm the updated date."
                )
                return

            if self._current_dob is None:
                self.session.generate_reply(
                    instructions="No date of birth was provided yet, ask the user to provide it."
                )
                return

            if not self.done():
                self.complete(
                    GetDOBResult(
                        date_of_birth=self._current_dob,
                        time_of_birth=self._current_time,
                    )
                )

        return confirm_dob

    @function_tool(flags=ToolFlag.IGNORE_ON_ENTER)
    async def decline_dob_capture(self, reason: str) -> None:
        """Handles the case when the user explicitly declines to provide a date of birth.

        Args:
            reason: A short explanation of why the user declined to provide the date of birth
        """
        if not self.done():
            self.complete(ToolError(f"couldn't get the date of birth: {reason}"))

    def _confirmation_required(self, ctx: RunContext) -> bool:
        if is_given(self._require_confirmation):
            return self._require_confirmation
        return ctx.speech_handle.input_details.modality == "audio"

Abstract base class for generic types.

On Python 3.12 and newer, generic classes implicitly inherit from Generic when they declare a parameter list after the class's name::

class Mapping[KT, VT]:
    def __getitem__(self, key: KT) -> VT:
        ...
    # Etc.

On older versions of Python, however, generic classes have to explicitly inherit from Generic.

After a class has been declared to be generic, it can then be used as follows::

def lookup_name[KT, VT](mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
    try:
        return mapping[key]
    except KeyError:
        return default

Ancestors

  • livekit.agents.voice.agent.AgentTask
  • livekit.agents.voice.agent.Agent
  • typing.Generic

Methods

async def decline_dob_capture(self, reason: str) ‑> None
Expand source code
@function_tool(flags=ToolFlag.IGNORE_ON_ENTER)
async def decline_dob_capture(self, reason: str) -> None:
    """Handles the case when the user explicitly declines to provide a date of birth.

    Args:
        reason: A short explanation of why the user declined to provide the date of birth
    """
    if not self.done():
        self.complete(ToolError(f"couldn't get the date of birth: {reason}"))

Handles the case when the user explicitly declines to provide a date of birth.

Args

reason
A short explanation of why the user declined to provide the date of birth
async def on_enter(self) ‑> None
Expand source code
async def on_enter(self) -> None:
    prompt = "Ask the user to provide their date of birth."
    if self._include_time:
        prompt = "Ask the user to provide their date of birth and, if they know it, their time of birth."
    self.session.generate_reply(instructions=prompt)

Called when the task is entered

async def update_dob(self, year: int, month: int, day: int, ctx: RunContext) ‑> str | None
Expand source code
@function_tool
async def update_dob(
    self,
    year: int,
    month: int,
    day: int,
    ctx: RunContext,
) -> str | None:
    """Update the date of birth provided by the user. Given a spoken month and year (e.g., 'July 2030'), return its numerical representation (7/2030).

    Args:
        year: The birth year (e.g., 1990)
        month: The birth month (1-12)
        day: The birth day (1-31)
    """
    try:
        dob = date(year, month, day)
    except ValueError as e:
        raise ToolError(f"Invalid date: {e}") from None

    today = date.today()
    if dob > today:
        raise ToolError(
            f"Invalid date of birth: {dob.strftime('%B %d, %Y')} is in the future. "
            "Date of birth cannot be a future date."
        )

    self._current_dob = dob

    if not self._confirmation_required(ctx):
        if not self.done():
            self.complete(
                GetDOBResult(
                    date_of_birth=self._current_dob,
                    time_of_birth=self._current_time,
                )
            )
        return None

    confirm_tool = self._build_confirm_tool(dob=dob)
    current_tools = [t for t in self.tools if t.id != "confirm_dob"]
    current_tools.append(confirm_tool)
    await self.update_tools(current_tools)

    formatted_date = dob.strftime("%B %d, %Y")
    response = f"The date of birth has been updated to {formatted_date}"

    if self._current_time:
        formatted_time = self._current_time.strftime("%I:%M %p")
        response += f" at {formatted_time}"

    response += (
        "\nRepeat the date back to the user in a natural spoken format.\n"
        "Prompt the user for confirmation, do not call `confirm_dob` directly"
    )

    return response

Update the date of birth provided by the user. Given a spoken month and year (e.g., 'July 2030'), return its numerical representation (7/2030).

Args

year
The birth year (e.g., 1990)
month
The birth month (1-12)
day
The birth day (1-31)