Module livekit.agents.beta.workflows.name

Classes

class GetNameResult (first_name: str | None = None,
middle_name: str | None = None,
last_name: str | None = None)
Expand source code
@dataclass
class GetNameResult:
    first_name: str | None = None
    middle_name: str | None = None
    last_name: str | None = None

GetNameResult(first_name: 'str | None' = None, middle_name: 'str | None' = None, last_name: 'str | None' = None)

Instance variables

var first_name : str | None
var last_name : str | None
var middle_name : str | None
class GetNameTask (first_name: bool = True,
last_name: bool = False,
middle_name: bool = False,
name_format: NotGivenOr[str] = NOT_GIVEN,
verify_spelling: bool = False,
extra_instructions: str = '',
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 GetNameTask(AgentTask[GetNameResult]):
    def __init__(
        self,
        first_name: bool = True,
        last_name: bool = False,
        middle_name: bool = False,
        name_format: NotGivenOr[str] = NOT_GIVEN,
        verify_spelling: bool = False,
        extra_instructions: str = "",
        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:
        if not (first_name or middle_name or last_name):
            raise ValueError("At least one of first_name, middle_name, or last_name must be True")
        self._collect_first_name = first_name
        self._collect_last_name = last_name
        self._collect_middle_name = middle_name
        self._verify_spelling = verify_spelling
        self._require_confirmation = require_confirmation

        if is_given(name_format):
            self._name_format = name_format
        else:
            parts = []
            if first_name:
                parts.append("{first_name}")
            if middle_name:
                parts.append("{middle_name}")
            if last_name:
                parts.append("{last_name}")
            self._name_format = " ".join(parts)

        spelling_instructions = (
            ""
            if not verify_spelling
            else (
                "After receiving the name, always verify the spelling by asking the user to confirm "
                "or spell out the name letter by letter. "
                "When confirming, spell out each name part letter by letter to the user. "
            )
        )
        confirmation_instructions = (
            "Call `confirm_name` after the user confirmed the name is correct."
        )
        extra = extra_instructions if extra_instructions else ""

        super().__init__(
            instructions=Instructions(
                _BASE_INSTRUCTIONS.format(
                    name_format=self._name_format,
                    modality_specific=_AUDIO_SPECIFIC,
                    spelling_instructions=spelling_instructions,
                    confirmation_instructions=(
                        confirmation_instructions if require_confirmation is not False else ""
                    ),
                    extra_instructions=extra,
                ),
                text=_BASE_INSTRUCTIONS.format(
                    name_format=self._name_format,
                    modality_specific=_TEXT_SPECIFIC,
                    spelling_instructions=spelling_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._first_name: str = ""
        self._middle_name: str = ""
        self._last_name: str = ""

    async def on_enter(self) -> None:
        self.session.generate_reply(
            instructions=f"Ask the user for their name, follow this order '{self._name_format}' but do not mention the format."
        )

    @function_tool()
    async def update_name(
        self,
        ctx: RunContext,
        first_name: str | None = None,
        middle_name: str | None = None,
        last_name: str | None = None,
    ) -> str | None:
        """Update the name provided by the user.

        Args:
            first_name: The user's first name.
            middle_name: The user's middle name, if collected.
            last_name: The user's last name, if collected.
        """
        errors: list[str] = []
        if self._collect_first_name and not (first_name and first_name.strip()):
            errors.append("first name is required but was not provided")
        if self._collect_middle_name and not (middle_name and middle_name.strip()):
            errors.append("middle name is required but was not provided")
        if self._collect_last_name and not (last_name and last_name.strip()):
            errors.append("last name is required but was not provided")

        if errors:
            raise ToolError(f"Incomplete name: {'; '.join(errors)}")

        self._first_name = first_name.strip() if first_name else ""
        self._middle_name = middle_name.strip() if middle_name else ""
        self._last_name = last_name.strip() if last_name else ""

        full_name = self._name_format.format(
            first_name=self._first_name,
            middle_name=self._middle_name,
            last_name=self._last_name,
        ).strip()

        if not self._confirmation_required(ctx):
            if not self.done():
                self.complete(
                    GetNameResult(
                        first_name=self._first_name if self._collect_first_name else None,
                        middle_name=self._middle_name if self._collect_middle_name else None,
                        last_name=self._last_name if self._collect_last_name else None,
                    )
                )
            return None

        confirm_tool = self._build_confirm_tool(
            first_name=self._first_name,
            middle_name=self._middle_name,
            last_name=self._last_name,
        )
        current_tools = [t for t in self.tools if t.id != "confirm_name"]
        current_tools.append(confirm_tool)
        await self.update_tools(current_tools)

        if self._verify_spelling:
            return (
                f"The name has been updated to {full_name}\n"
                f"Spell out the name letter by letter for verification: {full_name}\n"
                f"Prompt the user for confirmation, do not call `confirm_name` directly"
            )

        return (
            f"The name has been updated to {full_name}\n"
            f"Repeat the name back to the user and prompt for confirmation, "
            f"do not call `confirm_name` directly"
        )

    def _build_confirm_tool(
        self, *, first_name: str, middle_name: str, last_name: str
    ) -> llm.FunctionTool:
        @function_tool()
        async def confirm_name() -> None:
            """Call after the user confirms the name is correct."""
            if (
                first_name != self._first_name
                or middle_name != self._middle_name
                or last_name != self._last_name
            ):
                self.session.generate_reply(
                    instructions="The name has changed since confirmation was requested, ask the user to confirm the updated name."
                )
                return

            if not self.done():
                self.complete(
                    GetNameResult(
                        first_name=self._first_name if self._collect_first_name else None,
                        middle_name=self._middle_name if self._collect_middle_name else None,
                        last_name=self._last_name if self._collect_last_name else None,
                    )
                )

        return confirm_name

    @function_tool(flags=ToolFlag.IGNORE_ON_ENTER)
    async def decline_name_capture(self, reason: str) -> None:
        """Handles the case when the user explicitly declines to provide their name.

        Args:
            reason: A short explanation of why the user declined to provide their name
        """
        if not self.done():
            self.complete(ToolError(f"couldn't get the name: {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_name_capture(self, reason: str) ‑> None
Expand source code
@function_tool(flags=ToolFlag.IGNORE_ON_ENTER)
async def decline_name_capture(self, reason: str) -> None:
    """Handles the case when the user explicitly declines to provide their name.

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

Handles the case when the user explicitly declines to provide their name.

Args

reason
A short explanation of why the user declined to provide their name
async def on_enter(self) ‑> None
Expand source code
async def on_enter(self) -> None:
    self.session.generate_reply(
        instructions=f"Ask the user for their name, follow this order '{self._name_format}' but do not mention the format."
    )

Called when the task is entered

async def update_name(self,
ctx: RunContext,
first_name: str | None = None,
middle_name: str | None = None,
last_name: str | None = None) ‑> str | None
Expand source code
@function_tool()
async def update_name(
    self,
    ctx: RunContext,
    first_name: str | None = None,
    middle_name: str | None = None,
    last_name: str | None = None,
) -> str | None:
    """Update the name provided by the user.

    Args:
        first_name: The user's first name.
        middle_name: The user's middle name, if collected.
        last_name: The user's last name, if collected.
    """
    errors: list[str] = []
    if self._collect_first_name and not (first_name and first_name.strip()):
        errors.append("first name is required but was not provided")
    if self._collect_middle_name and not (middle_name and middle_name.strip()):
        errors.append("middle name is required but was not provided")
    if self._collect_last_name and not (last_name and last_name.strip()):
        errors.append("last name is required but was not provided")

    if errors:
        raise ToolError(f"Incomplete name: {'; '.join(errors)}")

    self._first_name = first_name.strip() if first_name else ""
    self._middle_name = middle_name.strip() if middle_name else ""
    self._last_name = last_name.strip() if last_name else ""

    full_name = self._name_format.format(
        first_name=self._first_name,
        middle_name=self._middle_name,
        last_name=self._last_name,
    ).strip()

    if not self._confirmation_required(ctx):
        if not self.done():
            self.complete(
                GetNameResult(
                    first_name=self._first_name if self._collect_first_name else None,
                    middle_name=self._middle_name if self._collect_middle_name else None,
                    last_name=self._last_name if self._collect_last_name else None,
                )
            )
        return None

    confirm_tool = self._build_confirm_tool(
        first_name=self._first_name,
        middle_name=self._middle_name,
        last_name=self._last_name,
    )
    current_tools = [t for t in self.tools if t.id != "confirm_name"]
    current_tools.append(confirm_tool)
    await self.update_tools(current_tools)

    if self._verify_spelling:
        return (
            f"The name has been updated to {full_name}\n"
            f"Spell out the name letter by letter for verification: {full_name}\n"
            f"Prompt the user for confirmation, do not call `confirm_name` directly"
        )

    return (
        f"The name has been updated to {full_name}\n"
        f"Repeat the name back to the user and prompt for confirmation, "
        f"do not call `confirm_name` directly"
    )

Update the name provided by the user.

Args

first_name
The user's first name.
middle_name
The user's middle name, if collected.
last_name
The user's last name, if collected.