File size: 9,461 Bytes
53ea588
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD 2-Clause License

"""Custom view frames."""

import json
from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum

from pipecat.frames.frames import DataFrame
from pydantic import BaseModel, Field

from nvidia_pipecat.frames.action import (
    StartActionFrame,
    StopActionFrame,
    UserActionFrame,
)


# Block types
class Block(BaseModel, ABC):
    """A block is a modular component that can be rendered in the UI.

    Multiple blocks can be rendered at a time.

    Args:
        id (str): A unique id for the block.
    """

    id: str

    @abstractmethod
    def get_type(self) -> str:
        """Get the type of the block.

        Type indicates what data is included in the block and is used to determine how to render the block in the UI.
        """


class HeaderBlock(Block):
    """A header block displays a header or subheader in the UI.

    Args:
        header (str): The text to display in the header.
        level (int): The level of the header, corresponding to HTML element tags for header (h1, h2, etc.).
            Must be between 1 and 6.
    """

    header: str
    level: int = Field(ge=1, le=6)

    def get_type(self) -> str:
        """Get the type of the block."""
        return "header"


class TextBlock(Block):
    """A text block displays text as a paragraph in the UI.

    Args:
        text (str): The text to display in the paragraph.
    """

    text: str

    def get_type(self) -> str:
        """Get the type of the block."""
        return "paragraph"


class Image(BaseModel):
    """Specifies the data needed to render an image. file and data fields are mutually exclusive.

    Args:
        url (str | None): The URL of the image.
        data (str | None): The image base64 data.
    """

    url: str | None = None
    data: str | None = None


class ImageBlock(Block):
    """An image block displays an image in the UI.

    Args:
        image (Image): The image to display.
        caption (str | None): The caption to display in place of the image if the image cannot be rendered.
    """

    image: Image
    caption: str | None = None

    def get_type(self) -> str:
        """Get the type of the block."""
        return "image"


class ImagePosition(str, Enum):
    """Enum for image position."""

    LEFT = "left"
    RIGHT = "right"


class ImageWithTextBlock(Block):
    """An image with text block displays an image and text in the UI.

    Args:
        image (Image): The image to display.
        text (str): The text to display next to the image.
        image_position (ImagePosition): The position of the image relative to the text.
    """

    image: Image
    text: str
    image_position: ImagePosition

    def get_type(self) -> str:
        """Get the type of the block."""
        return "paragraph_with_image"


class TableBlock(Block):
    """A table block displays a table in the UI.

    Args:
        headers (list[str]): The headers of the table.
        rows (list[list[str]]): The rows of the table.
    """

    headers: list[str]
    rows: list[list[str]]

    def get_type(self) -> str:
        """Get the type of the block."""
        return "table"


class Hint(BaseModel):
    """A hint is an example of how the user can interact with the system.

    Args:
        name (str): The name of the hint. This is used to identify the hint in the UI.
        text (str): The text of the hint. The text is what is displayed to the user.
    """

    name: str
    text: str


class HintCarouselBlock(Block):
    """A hint carousel block.

    The block rotates through a list of hints, displaying
    them one at a time to the user.

    Args:
        hints (list[Hint]): A list of hints to display.
    """

    hints: list[Hint]

    def get_type(self) -> str:
        """Get the type of the block."""
        return "hint_carousel"


class ButtonVariant(str, Enum):
    """Enum for button variant styles."""

    OUTLINED = "outlined"
    CONTAINED = "contained"
    TEXT = "text"


class Button(BaseModel):
    """Data format with information about a button.

    Args:
        id (str): The id of the button.
        active (bool): Whether the button is active. If false, the button will be grayed out and not clickable.
        toggled (bool): Whether the button is toggled. If true, the button will be toggled on.
        variant (ButtonVariant): The variant of the button.
        text (str): The text of the button.
    """

    id: str
    active: bool
    toggled: bool
    variant: ButtonVariant
    text: str


class ButtonListBlock(Block):
    """A button list block displays a list of buttons.

    Args:
        buttons (list[Button]): A list of buttons to display.
    """

    buttons: list[Button]

    def get_type(self) -> str:
        """Get the type of the block."""
        return "button_list"


class SelectableOption(BaseModel):
    """Data format for a selectable option. Can include an image and/or text.

    Args:
        id (str): The id of the selectable option.
        image (Image | None): The image to display within the option.
        text (str): The text to display within the option.
        active (bool): Whether the option is active. If false, the option will be grayed out and not clickable.
        toggled (bool): Whether the option is toggled. If true, the option will be toggled on.
    """

    id: str
    image: Image | None = None
    text: str
    active: bool
    toggled: bool


class SelectableOptionsGridBlock(Block):
    """A selectable options grid block.

    This block displays a grid of interactive options to the user.
    A user may select and deselect any number of options as they choose.

    Args:
        options (list[SelectableOption]): A list of selectable options to display.
    """

    buttons: list[SelectableOption]

    def get_type(self) -> str:
        """Get the type of the block."""
        return "selectable_options_grid"


class TextInputBlock(Block):
    """A text input block displays a text input to the user.

    Args:
        id (str): The id of the text input.
        default_value (str): The default value of the text input.
        value (str): The value of the text input.
        label (str): The label of the text input.
        input_type (str): The type of the text input.
    """

    id: str
    default_value: str
    value: str
    label: str
    input_type: str

    def get_type(self) -> str:
        """Get the type of the block."""
        return "text_input"


@dataclass
class StartCustomViewFrame(StartActionFrame):
    """Sends data to the UI in the form of modular components to be rendered.

    Args:
        blocks (list[Block] | None): A list of modular components to display in the UI.
        style (Style | None): The style of the UI.
    """

    blocks: list[Block] | None = None

    def to_json(self) -> str:
        """Convert the frame to a JSON string.

        This is intended to be used for serializing the frame for transmission over a WebSocket connection to the UI.
        """
        the_json = {}
        if self.blocks:
            blocks_list_json = []
            for block in self.blocks:
                block_json = {
                    "id": block.id,
                    "type": block.get_type(),
                    "data": block.model_dump(exclude={"id"}, exclude_none=True),
                }
                blocks_list_json.append(block_json)
            the_json["blocks"] = blocks_list_json
        return json.dumps(the_json)


@dataclass
class StopCustomViewFrame(StopActionFrame):
    """A frame that stops the custom view from being rendered in the UI."""


@dataclass
class UIInterimTextInputFrame(UserActionFrame, DataFrame):
    """Interim text input frame from the UI.

    Receives data from the UI in the form of text inputted into a textbox
    by the user prior to submission.

    Args:
        component_id (str): The id of the text input which is currently being typed in.
        interim_input (str): The interim input of the text input.
    """

    component_id: str
    interim_input: str


@dataclass
class UITextInputFrame(UserActionFrame, DataFrame):
    """A frame that receives data from the UI in the form of a text inputted into a textbox.

    This is triggered when the user submits their input.

    Args:
        enter_pressed (bool): True if the enter key was pressed to submit the text input,
            false if the user navigated away from the text input.
        component_id (str): The id of the text input which was submitted.
        input (str): The input of the text input.
    """

    enter_pressed: bool
    component_id: str
    input: str


@dataclass
class UIButtonPressFrame(UserActionFrame, DataFrame):
    """A frame that receives data from the UI in the form of a button press.

    Args:
        component_id (str): The id of the button which was pressed.
    """

    component_id: str


@dataclass
class UISelectableOptionPressFrame(UserActionFrame, DataFrame):
    """A frame that receives data from the UI in the form of a selectable option press.

    Args:
        component_id (str): The id of the selectable option which was pressed.
        toggled (bool): Whether the option is toggled. If true, the option will be toggled on.
    """

    component_id: str
    toggled: bool