Python/产品需求文档AI生成/application/components/chat.py

206 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
聊天相关组件
"""
import reflex as rx
from application.models import Type_, Message, Dialog
from application.state import ChatState
def render_input_bubble(input_: str) -> rx.Component:
"""
渲染输入气泡
:param content: 输入内容
:return: Component
"""
return rx.markdown(
input_,
color=rx.color("gray", 12), # 文字颜色
background_color=rx.color("gray", 2), # 背景颜色
display="inline-block", # 布局模式:自适应文本宽度
max_width="85%", # 最大宽度
padding_x="1.25em", # 水平内边距
padding_y="0.5em", # 垂直内边距
margin_left="auto", # 左侧外边距自动调整
margin_bottom="8px", # 底部外边距
border_radius="12px", # 圆角
)
def render_collapse_panel(dialog_id: str, message: Message) -> rx.Component:
"""
渲染折叠面板
:param dialog_id: 对话唯一标识
:param message: 消息实例
:return: Component
"""
return rx.box(
rx.cond(
message.is_open,
rx.hstack(
rx.text(
message.content,
font_size="0.90rem",
bold=True,
color=rx.color("gray", 10),
),
rx.spacer(),
rx.icon("chevron_up", size=16, color=rx.color("gray", 6)),
width="100%",
cursor="pointer",
on_click=ChatState.toggle_collapse_panel(dialog_id, message.id),
padding_x="1em",
padding_y="0.6em",
background_color=rx.color("gray", 2),
border_radius="8px",
), # 折叠面板打开
rx.hstack(
rx.hstack(
rx.text(
loading_prefix,
font_size="0.90rem",
bold=True,
color=rx.color("gray", 10),
),
loading_dot_group(message.is_streaming),
spacing="8", # 文字和圆点拉开一点间距,更美观
align_items="center",
),
rx.spacer(),
rx.icon("chevron_down", size=16, color=rx.color("gray", 6)),
width="100%",
cursor="pointer",
on_click=ChatState.toggle_collapse_panel(dialog_id, message.id),
padding_x="1em",
padding_y="0.6em",
background_color=rx.color("gray", 2),
border_radius="8px",
), # 折叠面板关闭
),
rx.cond(message.is_open, detail_box, rx.fragment()),
width="100%",
max_width="85%",
margin_bottom="10px",
key=message.id,
cursor="pointer",
)
def render_output_container(dialog_id: str, message: Message) -> rx.Component:
"""
渲染输出容器
:param dialog_id: 对话唯一标识
:param message: 消息实例
:return: Component
"""
return rx.cond(
message.type_ == Type_.TEXT,
rx.markdown(
message.content,
font_size="0.90rem",
color=rx.color("gray", 12), # 字体颜色
background_color="transparent", # 背景颜色:设置为透明以继承父元素背景颜色
display="block", # 布局模式:铺满
max_width="85%", # 最大宽度
padding="0", # 内边距
margin_right="auto", # 右侧外边距自动调整
margin_bottom="12px", # 底部外边距
key=message.id,
),
render_collapse_panel(dialog_id=dialog_id, message=message),
)
def render_dialog_container(dialog: Dialog) -> rx.Component:
"""
渲染对话容器
:param dialog: 对话实例
:return: Component
"""
return rx.box(
rx.box(
render_input_bubble(input_=dialog.input_),
text_align="right",
width="100%",
margin_bottom="8px",
),
rx.box(
rx.foreach(
dialog.output,
lambda message: render_output_container(
dialog_id=dialog.id, message=message
),
),
text_align="left",
width="100%",
margin_bottom="8px",
),
width="min(100%, 50em)", # 最大宽度父级元素最大宽度和50em中较小值
margin_x="auto", # 水平外边距:自动调整
key=dialog.id,
)
def dialog_list_component() -> rx.Component:
"""
对话列表组件
:return: Component
"""
return rx.auto_scroll(
rx.foreach(ChatState.get_current_chat_dialogs, dialog_item_component),
flex="1",
padding="8px",
overflow_y="auto",
)
def input_component() -> rx.Component:
"""
输入组件
"""
return rx.center(
rx.vstack(
rx.form(
rx.hstack(
rx.input(
name="input",
placeholder="请输入...",
flex="auto",
),
rx.button(
"发送",
type="submit",
loading=ChatState.get_current_chat_status, # 正在流式输出时按钮显示为 loading
disabled=ChatState.get_current_chat_status, # 正在流式输出时按钮禁用
),
max_width="50em",
margin="0 auto",
align_items="center",
),
on_submit=ChatState.process_input, # 处理输入,返回流式输出
reset_on_submit=True, # 提交后清空输入框
),
rx.text(
"抹茶兔兔工作室",
text_align="center",
font_size=".75em",
color=rx.color("mauve", 10),
),
width="100%",
padding_x="16px",
align="stretch",
),
position="sticky",
bottom="0",
left="0",
padding_y="16px",
backdrop_filter="auto",
backdrop_blur="lg",
border_top=f"1px solid {rx.color('mauve', 3)}",
background_color=rx.color("mauve", 2),
align="stretch",
width="100%",
) # rx.center 等价 rx.box(display="flex", align_items="center", justify_content="center")