This commit is contained in:
liubiren 2026-07-27 20:13:11 +08:00
parent 3f52e7bd61
commit 998a0fc572
9 changed files with 359 additions and 296 deletions

View File

@ -1,8 +1,8 @@
"""empty message
Revision ID: a7968da75b72
Revision ID: 4269426de639
Revises:
Create Date: 2026-07-26 23:23:19.558164
Create Date: 2026-07-27 10:10:56.902466
"""
from typing import Sequence, Union
@ -12,7 +12,7 @@ import sqlalchemy as sa
import sqlmodel
# revision identifiers, used by Alembic.
revision: str = 'a7968da75b72'
revision: str = '4269426de639'
down_revision: Union[str, Sequence[str], None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

View File

@ -28,7 +28,9 @@ app = rx.App(
"outline": "none",
"box-shadow": "none",
},
"*::placeholder": {
".rt-TextAreaInput::placeholder, .rt-TextFieldInput::placeholder": {
"font-family": "var(--font-family)",
"font-size": "var(--prismui-font-size-2)",
"color": "var(--prismui-color-3)",
"opacity": 1,
}, # 覆盖占位符样式

View File

@ -3,10 +3,10 @@
领域模型
"""
from datetime import datetime
from typing import Dict, List
from pydantic_ai._uuid import uuid7
from typing import Dict
from pydantic import BaseModel, Field
from dataclasses import dataclass
from pydantic_ai._uuid import uuid7
class Thought(BaseModel):
@ -25,7 +25,7 @@ class Dialog(BaseModel):
id: str = Field(default_factory=lambda: str(uuid7()), description="对话唯一标识")
user_prompt: str = Field(..., description="用户提示词")
thoughts: List[Thought] = Field(default_factory=list, description="思考列表")
thoughts: Dict[int, Thought] = Field(default_factory=dict, description="思考列表")
result_output: str = Field(default="", description="结果输出")
is_thinking: bool = Field(
default=False, description="正在思考True 表示正在思考False 表示未正在思考"

View File

@ -3,6 +3,7 @@
会话页面
"""
import reflex as rx
from typing import Dict
from application.domain_models import (
Conversation,
@ -14,143 +15,160 @@ from application.states import ConversationState, AuthState
def conversation_history_item(
item: ConversationHistoryItem,
conversation: ConversationHistoryItem,
) -> rx.Component:
"""
会话历史项
:param item: 会话历史项
:param conversation: 会话历史项
:return: Component
"""
# 会话激活状态
is_actived: bool = item.id == ConversationState.conversation_id
# 高亮:若为当前会话或显示更多则高亮
highlight: bool = (
conversation.id == ConversationState.shown_more_conversation_id
) | (conversation.id == ConversationState.conversation_id)
return rx.list.item(
rx.vstack(
rx.hstack(
# 对话描述
rx.text(
item.description,
conversation.description,
flex=1,
height="22px",
line_height="22px",
padding_right="4px",
padding_right="18px",
color="var(--prismui-color-2)",
overflow="hidden",
text_overflow="ellipsis",
white_space="nowrap",
),
# 更多按钮
rx.box(
rx.popover.root(
# 触发事件:点击更多按钮
rx.popover.trigger(
rx.box(
rx.icon(
"ellipsis",
width="14px",
height="14px",
color="var(--prismui-color-2)",
),
cursor="pointer",
)
),
# 气泡卡片
rx.popover.content(
rx.vstack(
rx.box(
position="absolute",
top="-12px",
left="50%",
transform="translateX(-50%) rotate(45deg)",
width="8px",
height="8px",
background_color="var(--prismui-background-color-1)",
),
rx.popover.close(
rx.box(
"删除",
width="100%",
height="24px",
padding="4px",
border_radius="var(--prismui-border-radius-1)",
line_height="16px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-1)",
style={
"_hover": {
"background_color": "var(--prismui-background-color-6)",
}
},
# 点击事件:删除对话
on_click=lambda: ConversationState.delete_conversation(
item.id
),
),
),
position="relative",
width="100%",
),
side="bottom",
side_offset=8,
position="relative",
align="center",
padding="8px",
border_radius="var(--prismui-border-radius-1)",
box_shadow="0 2px 12px rgba(0,0,0,0.1)",
font_family="var(--prismui-font-family)",
overflow="visible",
),
open_delay=0,
),
min_width="14px",
cursor="pointer",
style={
"opacity": rx.cond(
is_actived, "1", "0"
), # 若当前会话已激活则不透明,否则透明
"transition": "opacity 0.18s ease",
"pointer_events": rx.cond(is_actived, "auto", "none"),
},
overflow="hidden",
),
display="flex",
align_items="center",
width="100%",
margin_bottom="8px",
cursor="pointer",
),
rx.hstack(
rx.spacer(),
# 创建时间
rx.text(
"1",
margin_bottom="8px",
conversation.created_at,
line_height="20px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-8)",
),
justify_content="space-between",
align_items="center",
width="100%",
height="20px",
margin_bottom="8px",
),
# 点击事件:切换会话
on_click=lambda: ConversationState.switch_conversation(conversation.id),
width="100%",
),
# 更多按钮:点击更多按钮显示气泡卡片,可删除会话
rx.box(
rx.popover.root(
rx.popover.trigger(
rx.box(
rx.icon(
"ellipsis",
width="14px",
height="14px",
color="var(--prismui-color-2)",
),
cursor="pointer",
)
),
rx.popover.content(
rx.vstack(
rx.box(
position="absolute",
top="-6px",
left="50%",
transform="translateX(-50%) rotate(45deg)",
width="8px",
height="8px",
background_color="var(--prismui-background-color-1)",
),
rx.popover.close(
rx.box(
"删除",
# 点击事件:删除对话
on_click=lambda: ConversationState.delete_conversation(
conversation.id
),
display="flex",
align_items="center",
justify_content="center",
width="100%",
height="24px",
border_radius="var(--prismui-border-radius-1)",
line_height="16px",
font_family="var(--prismui-font-family)",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-1)",
style={
"_hover": {
"background_color": "var(--prismui-background-color-6)",
}, # 鼠标悬停显示背景颜色
},
cursor="pointer",
),
),
position="relative",
width="100%",
),
side="bottom",
side_offset=8,
position="relative",
align="center",
padding="4px 8px",
border_radius="var(--prismui-border-radius-1)",
box_shadow="var(--prismui-box-shadow-6)",
overflow="visible",
),
on_open_change=lambda is_shown: ConversationState.set_shown_more_conversation_id(
conversation.id, is_shown
),
open_delay=0,
),
position="absolute",
top="27px",
right="16px",
transform="translateY(-50%)",
z_index=99,
min_width="14px",
cursor="pointer",
opacity=rx.cond(highlight, "1", "0"),
transition="opacity 0.18s ease",
pointer_events=rx.cond(highlight, "auto", "none"),
),
display="flex",
position="relative",
align_items="flex-start",
width="100%",
padding="16px",
margin_bottom="8px",
background=rx.cond(
is_actived,
"linear-gradient(to right, #f3efff, #f3efff33, #e2f1fd33, #e2f1fd)",
highlight,
"var(--prismui-background-3)",
"var(--prismui-background-color-1)",
),
border_radius="var(--prismui-border-radius-3)",
box_shadow=rx.cond(is_actived, "2px 2px 8px #e9e9e9", "none"),
box_shadow=rx.cond(highlight, "var(--prismui-box-shadow-7)", "none"),
cursor="pointer",
style={
"&:hover": {
"background": "linear-gradient(to right, #f3efff, #f3efff33, #e2f1fd33, #e2f1fd)",
"box_shadow": "2px 2px 8px #e9e9e9",
"background": "var(--prismui-background-3)",
"box_shadow": "var(--prismui-box-shadow-7)",
}, # 鼠标悬停背景颜色和阴影
"&:hover > div > div:last-child": {
"opacity": "1 !important",
"pointer_events": "auto !important",
}, # 鼠标悬停强制显示右侧三点按钮
"&:hover > div:last-child": {
"opacity": "1",
"pointer_events": "auto",
}, # 鼠标悬停显示更多按钮
},
cursor="pointer",
# 点击事件:切换会话
on_click=ConversationState.switch_conversation(item.id),
)
@ -223,12 +241,11 @@ def greeting() -> rx.Component:
src="/logo.png",
width="64px",
height="64px",
object_fit="contain",
),
rx.box(
"棱镜球",
font_size="32px",
font_weight="700",
font_size="calc(var(--prismui-font-size-3) * 2)",
font_weight="var(--prismui-font-weight-3)",
letter_spacing="1px",
),
display="flex",
@ -248,14 +265,12 @@ def greeting() -> rx.Component:
),
width="100%",
line_height="1.5",
font_size="var(--devui-font-size)",
),
display="flex",
flex_direction="column",
width="100%",
align_items="center",
gap="12px",
color="var(--prismui-color-text)",
),
# 演示案例
rx.vstack(
@ -263,23 +278,22 @@ def greeting() -> rx.Component:
rx.text(
"演示案例",
line_height="24px",
font_size="16px",
font_weight="700",
font_size="var(--prismui-font-size-3)",
font_weight="var(--prismui-font-weight-3)",
),
display="flex",
width="100%",
justify_content="space-between",
align_items="center",
width="100%",
margin_bottom="16px",
),
rx.hstack(
rx.box(
"智能客服",
padding="10px 16px",
background_color="var(--devui-dividing-line)",
border_radius="var(--devui-border-radius-full)",
font_size="var(--devui-font-size)",
color="var(--devui-aide-text)",
background_color="var(--prismui-background-color-6)",
border_radius="var(--prismui-border-radius-9)",
color="var(--prismui-color-10)",
cursor="pointer",
),
display="flex",
@ -289,26 +303,26 @@ def greeting() -> rx.Component:
),
width="100%",
padding="24px",
background_color="var(--devui-base-bg)",
border_radius="24px",
background_color="var(--prismui-background-color-1)",
border_radius="var(--prismui-border-radius-7)",
),
display="flex",
flex_direction="column",
align_items="center",
gap="24px",
width="100%",
min_height="0",
margin="auto 0",
align_items="center",
gap="24px",
color="var(--prismui-color-text)",
),
display="flex",
flex="1",
flex_direction="column",
justify_content="flex-start",
gap="24px",
width="100%",
max_width="1200px",
padding="0 12px",
justify_content="flex-start",
gap="24px",
overflow="auto",
style={
"&::-webkit-scrollbar": {
@ -318,20 +332,20 @@ def greeting() -> rx.Component:
)
def thought(thought: Thought):
def thought(thought):
"""
思考节点
思考卡片
:param thought: 思考节点实例
:param thought: 思考实例
:return: Component
"""
return rx.vstack(
rx.match(
thought.type,
thought[1].type,
(
"thinking",
rx.text(
thought.content,
thought[1].content,
line_height="22px",
font_size="var(--prismui-font-size-2)",
color="var(--prismui-color-3)",
@ -354,28 +368,26 @@ def dialog(dialog: Dialog) -> rx.Component:
is_thinking = dialog.is_thinking
# 思考折叠面板展开状态
is_expanded = dialog.is_expanded
# 思考节点
thoughts = dialog.thoughts
return rx.vstack(
# 问题和操作栏
rx.hstack(
rx.spacer(),
rx.vstack(
# 问题
# 用户提示词
rx.text(
dialog.user_prompt,
max_width="600px",
padding="12px 16px",
background_color="var(--prismui-background-color-3)",
border_radius="var(--prismui-border-radius-3)",
line_height="1.5",
color="var(--prismui-color-text)",
word_wrap="break-word",
word_break="break-all",
white_space="pre-line",
color="var(--prismui-color-text)",
),
# 操作栏
rx.hstack(
# 再次发送
rx.box(
rx.icon(
"rotate-ccw",
@ -396,11 +408,10 @@ def dialog(dialog: Dialog) -> rx.Component:
margin_top="8px",
),
),
width="100%",
margin_top="8px",
align_items="flex-start",
gap="4px",
font_size="var(--devui-font-size)",
width="100%",
margin_top="8px",
),
# 思考折叠面板
rx.vstack(
@ -437,7 +448,7 @@ def dialog(dialog: Dialog) -> rx.Component:
rx.box(
rx.auto_scroll(
rx.foreach(
thoughts,
dialog.thoughts,
thought,
),
width="100%",
@ -464,35 +475,43 @@ def dialog(dialog: Dialog) -> rx.Component:
"p": lambda text: rx.text(
text,
margin_bottom="8px",
line_height="1.5",
word_wrap="break-word",
word_break="break-all",
white_space="pre-line",
),
"h2": lambda text: rx.text(
text,
margin_bottom="8px",
line_height="1.5",
font_weight="var(--prismui-font-weight-3)",
word_wrap="break-word",
word_break="break-all",
white_space="pre-line",
line_height="24px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-1)",
),
"strong": lambda text: rx.text(
text,
display="inline",
font_weight="var(--prismui-font-weight-1)",
font_weight="var(--prismui-font-weight-3)",
),
"ul": lambda children: rx.vstack(
children, margin_bottom="8px", gap="4px"
),
"li": lambda text: rx.text(
text,
display="list-item",
list_style_type="disc",
list_style_position="outside",
margin="0 0 4px 16px",
line_height="1.5",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-1)",
word_wrap="break-word",
word_break="break-all",
white_space="pre-line",
line_height="24px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-1)",
style={
"display": "list-item",
"list_style_type": "disc",
"list_style_position": "inside",
},
style={},
),
"hr": lambda _: rx.divider(margin="8px 0"),
},
width="100%",
margin_top="8px",
@ -503,21 +522,19 @@ def dialog(dialog: Dialog) -> rx.Component:
)
def dialogs() -> rx.Component:
def dialogs_showing() -> rx.Component:
"""
对话列表
对话列表展示
:return: Component
"""
# 获取当前会话的对话历史
# 当前会话的对话列表
dialogs = ConversationState.dialogs
# 若运行字典为空则品牌图标、名称和介绍、预设用户提示词等,否则遍历运行项
# 若对话列表为空则显示欢迎,否则显示对话列表
return rx.cond(
dialogs.length() == 0,
# 欢迎
greeting(),
# 遍历对话项
rx.vstack(
rx.auto_scroll(
rx.foreach(
@ -539,54 +556,53 @@ def dialogs() -> rx.Component:
)
def create_conversation_button() -> rx.Component:
def create_conversation() -> rx.Component:
"""
新建会话按钮
创建会话
:return: Component
"""
return rx.hstack(
rx.el.style(
"""
.rt-TooltipArrow polygon {
fill: #ffffff !important;
fill: var(--prismui-background-color-1) !important;
}
.rt-TooltipText {
color: var(--prismui-color-text) !important;
font-family: var(--prismui-font-family) !important;
color: var(--prismui-color-1) !important;
opacity: 1 !important;
}
"""
),
rx.spacer(),
rx.dialog.root(
# 触发事件:点击图标
rx.dialog.trigger(
rx.box(
rx.tooltip(
rx.icon(
"plus",
size=14,
color="var(--prismui-color-text)",
width="14px",
height="14px",
style={
"_hover": {
"color": "var(--devui-brand)",
"color": "var(--prismui-color-5)",
}
},
),
content="新建会话",
background_color="#ffffff",
box_shadow="0 2px 12px 0 rgba(37, 43, 58, .24)",
color="#252b3a",
side="top",
side_offset=9,
background_color="var(--prismui-background-color-1)",
box_shadow="var(--prismui-box-shadow-3)",
),
display="flex",
width="24px",
height="24px",
background_color="var(--devui-base-bg)",
box_shadow="0 1px 8px #1919190f",
border_radius="var(--devui-border-radius-full)",
justify_content="center",
align_items="center",
width="24px",
height="24px",
background_color="var(--prismui-background-color-1)",
box_shadow="var(--prismui-box-shadow-8)",
border_radius="var(--prismui-border-radius-9)",
cursor="pointer",
)
),
@ -623,103 +639,104 @@ def create_conversation_button() -> rx.Component:
)
def operation() -> rx.Component:
def user_prompt_sending() -> rx.Component:
"""
操作区
用户提示词发送
:return: Component
"""
return rx.vstack(
# 自定义输入组件
rx.form(
rx.box(
rx.vstack(
rx.hstack(
rx.text_area(
name="question",
placeholder="请输入您的问题按Enter换行",
vertical_align="middle",
width="100%",
height="64px",
padding="4px 0",
background_color="var(--devui-base-bg)",
font_size="var(--devui-font-size)",
color="var(--prismui-color-text)",
style={
"border": "none !important",
"outline": "none !important",
"boxShadow": "none !important",
},
),
width="100%",
padding="0 16px",
),
rx.hstack(
rx.spacer(),
rx.button(
rx.icon(
"send",
margin_right="4px",
width="12px",
height="12px",
),
rx.text("发送"),
position="relative",
display="inline-flex",
padding="0 12px",
background_color="var(--devui-primary)",
border="none",
border_radius="20pxvar(--devui-border-radius-4)",
align_items="center",
justify_content="center",
white_space="nowrap",
inline_height="1.5",
font_size="var(--devui-font-size)",
color="var(--devui-light-text)",
overflow="hidden",
cursor="pointer",
type="submit",
loading=ConversationState.running_status,
disabled=ConversationState.running_status,
),
width="100%",
justify_content="flex-end",
align_items="center",
height="32px",
padding="0 16px",
),
),
rx.vstack(
rx.text_area(
# 值绑定用户提示词
value=ConversationState.user_prompt,
# 输入事件:设置用户提示词
on_change=ConversationState.set_user_prompt,
placeholder="请输入您的问题按Enter换行",
vertical_align="middle",
width="100%",
height="64px",
padding="4px 0",
background_color="var(--prismui-background-color-1)",
border="none !important",
outline="none !important",
box_shadow="none !important",
style={
"* textarea::placeholder": {
"fontFamily": "var(--font-family)",
"fontSize": "var(--devui-font-size)",
"color": "var(--placeholder)",
"opacity": 1,
".rt-TextAreaInput": {
"border": "none !important",
"outline": "none !important",
"box-shadow": "none !important",
},
},
),
display="flex",
flex_direction="column",
rx.hstack(
rx.spacer(),
rx.button(
rx.icon(
"send",
margin_right="4px",
width="15px",
height="15px",
color="var(--prismui-color-9)",
),
rx.text(
"发送",
line_height="21px",
color="var(--prismui-color-9)",
),
loading=ConversationState.running_status,
# 是否禁用绑定用户提示词发送禁用状态
disabled=ConversationState.is_user_prompt_sending_disabled,
# 点击事件:发送用户提示词
on_click=ConversationState.handle_user_prompt,
display="inline-flex",
position="relative",
justify_content="center",
align_items="center",
padding="0 12px",
background_color=rx.cond(
ConversationState.is_user_prompt_sending_disabled,
"var(--prismui-color-11)",
"var(--prismui-color-5)",
),
border="none",
border_radius="var(--prismui-border-radius-5)",
inline_height="1.5",
font_size="var(--prismui-font-size-2)",
white_space="nowrap",
overflow="hidden",
cursor=rx.cond(
ConversationState.is_user_prompt_sending_disabled,
"not-allowed",
"pointer",
),
),
width="100%",
justify_content="flex-end",
align_items="center",
height="32px",
),
width="100%",
padding="12px 0",
background_color="var(--devui-base-bg)",
border="none",
border_radius="16px",
box_shadow="0 1px 8px 0 var(--mc-box-shadow)",
on_submit=ConversationState.run,
reset_on_submit=True,
padding="12px 16px",
background_color="var(--prismui-background-color-1)",
border_radius="var(--prismui-border-radius-6)",
box_shadow="var(--prismui-box-shadow-9)",
),
# 底部文案
# 免责声明
rx.text(
"内容由大模型生成,无法确保准确性和完整性,仅供参考",
margin_top="8px",
width="100%",
text_align="center",
font_size="12px",
color="var(--devui-aide-text)",
margin_top="8px",
line_height="18px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-10)",
),
display="flex",
flex_direction="column",
width="100%",
max_width="1200px",
padding="0 12px 12px",
align_items="center",
border="none",
)
@ -774,12 +791,12 @@ def conversation() -> rx.Component:
conversation_history(is_conversation_history_shown),
rx.box(
rx.vstack(
# 对话历史
dialogs(),
# 新建对话按钮
create_conversation_button(),
# 操作区
operation(),
# 对话列表展示
dialogs_showing(),
# 创建对话
create_conversation(),
# 用户提示词发送
user_prompt_sending(),
display="flex",
flex_flow="column",
width="100%",

View File

@ -224,7 +224,7 @@ def login_popup() -> rx.Component:
"登录解锁更多功能",
margin_bottom="24px",
line_height="32px",
font_size="var(--prismui-font-size-3)",
font_size="var(--prismui-font-size-4)",
font_weight="var(--prismui-font-weight-3)",
),
# 邮箱输入框
@ -281,7 +281,11 @@ def login_popup() -> rx.Component:
"var(--prismui-color-4)",
"var(--prismui-color-5)",
),
cursor="pointer",
cursor=rx.cond(
AuthState.is_captcha_sending_disabled,
"not-allowed",
"pointer",
),
),
width="100%",
height="45px",
@ -390,7 +394,7 @@ def login_popup() -> rx.Component:
padding="20px",
background_color="var(--prismui-background-color-1)",
border="var(--prismui-border-2)",
border_radius="var(--prismui-border-radius-5)",
border_radius="var(--prismui-border-radius-6)",
),
rx.text(
"加微信好友",
@ -407,7 +411,7 @@ def login_popup() -> rx.Component:
),
background="var(--prismui-background-2)",
border="1px solid #2222220f",
border_radius="var(--prismui-border-radius-5)",
border_radius="var(--prismui-border-radius-6)",
box_shadow="0 4px 64px 0 #0000001a",
overflow="hidden",
),

View File

@ -77,10 +77,15 @@ class ConversationState(rx.State):
# 会话历史展示状态True表示展示False表示隐藏
is_conversation_history_shown: bool = False
# 显示更多的会话唯一标识
shown_more_conversation_id: str = ""
# 会话创建状态True表示正在创建False表示未正在创建
is_conversation_creating: bool = False
# 用户提示词
user_prompt: str = ""
async def resume(self, user_id: str) -> None:
"""
恢复当前用户会话状态
@ -112,22 +117,6 @@ class ConversationState(rx.State):
"""
self.is_conversation_history_shown = not self.is_conversation_history_shown
@staticmethod
def format_conversation_created_at(created_at: datetime) -> str:
"""
格式化会话创建时间
:param created_at: 会话创建时间
:return: 格式化后的会话创建时间
"""
match (datetime.now().date() - created_at.date()).days:
case 0:
formated_created_at = f"今天 {created_at.strftime('%H:%M')}"
case 1:
formated_created_at = f"昨天 {created_at.strftime('%H:%M')}"
case _:
formated_created_at = created_at.strftime("%Y-%m-%d %H:%M")
return formated_created_at
@rx.var
def conversation_history_items(self) -> List[ConversationHistoryItem]:
"""
@ -138,17 +127,34 @@ class ConversationState(rx.State):
for conversation in reversed(
self.conversations.values()
): # 按照会话唯一标识倒序排序
# 格式化会话创建时间
match (datetime.now().date() - conversation.created_at.date()).days:
case 0:
created_at = f"{conversation.created_at.strftime('%H:%M')}"
case 1:
created_at = f"昨天 {conversation.created_at.strftime('%H:%M')}"
case _:
created_at = conversation.created_at.strftime("%Y-%m-%d %H:%M")
items.append(
ConversationHistoryItem(
id=conversation.id,
description=conversation.description,
created_at=self.format_conversation_created_at(
created_at=conversation.created_at
),
created_at=created_at,
)
)
return items
@rx.event
def set_shown_more_conversation_id(
self, conversation_id: str, is_shown: bool
) -> None:
"""
设置显示更多的会话唯一标识
:return: None
"""
self.shown_more_conversation_id = conversation_id if is_shown else ""
@rx.event
async def delete_conversation(self, conversation_id: str) -> None:
"""
@ -212,6 +218,27 @@ class ConversationState(rx.State):
"""
self.is_conversation_creating = not self.is_conversation_creating
@rx.event
def set_user_prompt(self, user_prompt: str) -> None:
"""
设置用户提示词
:param user_prompt: 用户提示词
:return: None
"""
self.user_prompt = user_prompt.strip()
@rx.var
def is_user_prompt_sending_disabled(self) -> bool:
"""
用户提示词发送禁用状态
:return: bool
"""
# 当前会话
conversation = self.conversations.get(self.conversation_id)
if not conversation:
return True
return conversation.is_running or not self.user_prompt
@rx.var
def running_status(self) -> bool:
"""
@ -227,7 +254,7 @@ class ConversationState(rx.State):
@rx.var
def dialogs(self) -> List[Dialog]:
"""
获取当前会话的对话列表
当前会话的对话列表
:return: 当前会话的对话列表
"""
# 当前会话
@ -237,17 +264,18 @@ class ConversationState(rx.State):
return list(conversation.dialogs.values())
@rx.event
async def run(self, form_data: dict[str, Any]) -> AsyncGenerator[None]:
async def handle_user_prompt(self) -> AsyncGenerator[None]:
"""
运行
:param form_data: 表单数据
:return: AsyncGenerator
处理用户提示词
:return: AsyncGenerator[None]
"""
# 解析用户提示词
user_prompt = form_data["user_prompt"].strip()
if not user_prompt:
if not self.user_prompt:
return
# 清空前端用户提示词
user_prompt = self.user_prompt
self.user_prompt = ""
# 当前会话
conversation = self.conversations[self.conversation_id]
# 将当前会话的运行状态设置为正在运行

View File

@ -4,7 +4,7 @@
"""
from datetime import datetime, timedelta
from random import choices
from typing import Dict, List, Tuple
from typing import Dict, List
from pydantic import TypeAdapter
from pydantic_ai import ModelMessage, ModelMessagesTypeAdapter
@ -106,8 +106,8 @@ class DialogRecord(SQLModel, table=True):
)
conversation_id: str = Field(..., index=True, description="会话唯一标识")
user_prompt: str = Field(..., description="用户提示词")
thoughts: List[ThoughtRecord] = Field(
default_factory=List, sa_type=JSON, description="思考列表"
thoughts: Dict[int, ThoughtRecord] = Field(
default_factory=dict, sa_type=JSON, description="思考列表"
)
result_output: str = Field(default="", description="结果输出")
@ -123,7 +123,7 @@ class ResultRecord(SQLModel, table=True):
# 思考领域模型类型适配器
ThoughtTypeAdapter = TypeAdapter(List[Thought])
ThoughtTypeAdapter = TypeAdapter(Dict[int, Thought])
class DatabaseState(rx.State):
@ -282,7 +282,7 @@ class DatabaseState(rx.State):
async def create_dialog_record(
self, conversation_id: str, user_prompt: str
) -> Dialog:
) -> Dict[str, Dialog]:
"""
创建对话记录
:param conversation_id: 会话唯一标识
@ -297,12 +297,12 @@ class DatabaseState(rx.State):
session.add(record)
await session.commit()
await session.refresh(record)
return Dialog(id=record.id, user_prompt=user_prompt)
return {record.id: Dialog(id=record.id, user_prompt=user_prompt)}
async def complete_dialog_record(
self,
id: str,
thoughts: List[Thought],
thoughts: Dict[int, Thought],
result_output: str,
) -> None:
"""

View File

@ -7,10 +7,12 @@
--prismui-background-color-6: #f2f2f3;
--prismui-background-1: linear-gradient(to bottom, #D0C9FF 0%, #E6D6F0 8%, #F1DBEA 12%, #C8DCFB 40%, #ABC6F6 60%, #87AEFE 90%);
--prismui-background-2: linear-gradient(180deg, #fffffff2, #f8fafff2 99%);
--prismui-background-3: linear-gradient(to right, #f3efff, #f3efff33, #e2f1fd33, #e2f1fd);
--prismui-font-family: HuaweiFont,Helvetica,Arial,PingFangSC-Regular,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Microsoft JhengHei;
--prismui-font-size-1: 12px;
--prismui-font-size-2: 14px;
--prismui-font-size-3: 20px;
--prismui-font-size-3: 16px;
--prismui-font-size-4: 20px;
--prismui-font-weight-1: normal;
--prismui-font-weight-2: 500;
--prismui-font-weight-3: 700;
@ -21,17 +23,27 @@
--prismui-color-5: #5e7ce0;
--prismui-color-6: #f66f6a;
--prismui-color-7: #f2f2f3;
--prismui-color-8: #aeaeae;
--prismui-color-9: #ffffff;
--prismui-color-10: #71757f;
--prismui-color-11: #beccfa;
--prismui-border-1: 1px solid #060a260f;
--prismui-border-radius-1: 4px;
--prismui-border-radius-2: 6px;
--prismui-border-radius-3: 8px;
--prismui-border-radius-4: 12px;
--prismui-border-radius-5: 20px;
--prismui-border-radius-5: 16px;
--prismui-border-radius-6: 20px;
--prismui-border-radius-7: 24px;
--prismui-border-radius-9: 100px;
--prismui-box-shadow-1: 0 4px 64px 0 #0000001a;
--prismui-box-shadow-2: 0 4px 12px #0000001a;
--prismui-box-shadow-3: 0 2px 12px 0 #252b3a3d;
--prismui-box-shadow-4: 2px 0 4px 0 #d5d5d540;
--prismui-box-shadow-5: -2px -2px 4px #0000000d;
--prismui-box-shadow-6: 0 2px 12px #00000019;
--prismui-box-shadow-7: 2px 2px 8px #e9e9e9;
--prismui-box-shadow-8:0 1px 8px #1919190f;
--prismui-box-shadow-9: 0 1px 8px 0 #1919190f;
background-color: var(--prismui-background-color-1);
}

Binary file not shown.