This commit is contained in:
parent
998a0fc572
commit
f9b91d45c1
|
|
@ -3,7 +3,7 @@
|
||||||
领域模型
|
领域模型
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict
|
from typing import Dict, List
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from pydantic_ai._uuid import uuid7
|
from pydantic_ai._uuid import uuid7
|
||||||
|
|
@ -57,3 +57,20 @@ class ConversationHistoryItem(BaseModel):
|
||||||
id: str = Field(..., description="会话唯一标识")
|
id: str = Field(..., description="会话唯一标识")
|
||||||
description: str = Field(..., description="会话描述")
|
description: str = Field(..., description="会话描述")
|
||||||
created_at: str = Field(..., description="会话创建时间")
|
created_at: str = Field(..., description="会话创建时间")
|
||||||
|
|
||||||
|
|
||||||
|
class DialogItem(BaseModel):
|
||||||
|
"""
|
||||||
|
对话项领域模型
|
||||||
|
"""
|
||||||
|
|
||||||
|
id: str = Field(..., description="对话唯一标识")
|
||||||
|
user_prompt: str = Field(..., description="用户提示词")
|
||||||
|
thoughts: List[Thought] = Field(default_factory=list, description="思考列表")
|
||||||
|
result_output: str = Field(default="", description="结果输出")
|
||||||
|
is_thinking: bool = Field(
|
||||||
|
default=False, description="正在思考,True 表示正在思考,False 表示未正在思考"
|
||||||
|
)
|
||||||
|
is_expanded: bool = Field(
|
||||||
|
default=False, description="思考折叠面板展开状态,True 表示展开,False 表示折叠"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,33 +7,33 @@ from typing import Dict
|
||||||
|
|
||||||
from application.domain_models import (
|
from application.domain_models import (
|
||||||
Conversation,
|
Conversation,
|
||||||
Dialog,
|
DialogItem,
|
||||||
Thought,
|
Thought,
|
||||||
ConversationHistoryItem,
|
ConversationHistoryItem,
|
||||||
)
|
)
|
||||||
from application.states import ConversationState, AuthState
|
from application.states import ConversationState, AuthState
|
||||||
|
|
||||||
|
|
||||||
def conversation_history_item(
|
def conversation_history_item_showing(
|
||||||
conversation: ConversationHistoryItem,
|
item: ConversationHistoryItem,
|
||||||
) -> rx.Component:
|
) -> rx.Component:
|
||||||
"""
|
"""
|
||||||
会话历史项
|
会话历史项展示
|
||||||
:param conversation: 会话历史项
|
:param conversation: 会话历史项
|
||||||
:return: Component
|
:return: Component
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 高亮:若为当前会话或显示更多则高亮
|
# 高亮:若为当前会话或显示更多则高亮
|
||||||
highlight: bool = (
|
highlight: bool = (item.id == ConversationState.shown_more_conversation_id) | (
|
||||||
conversation.id == ConversationState.shown_more_conversation_id
|
item.id == ConversationState.conversation_id
|
||||||
) | (conversation.id == ConversationState.conversation_id)
|
)
|
||||||
|
|
||||||
return rx.list.item(
|
return rx.list.item(
|
||||||
rx.vstack(
|
rx.vstack(
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
# 对话描述
|
# 对话描述
|
||||||
rx.text(
|
rx.text(
|
||||||
conversation.description,
|
item.description,
|
||||||
flex=1,
|
flex=1,
|
||||||
height="22px",
|
height="22px",
|
||||||
line_height="22px",
|
line_height="22px",
|
||||||
|
|
@ -52,7 +52,7 @@ def conversation_history_item(
|
||||||
rx.spacer(),
|
rx.spacer(),
|
||||||
# 创建时间
|
# 创建时间
|
||||||
rx.text(
|
rx.text(
|
||||||
conversation.created_at,
|
item.created_at,
|
||||||
line_height="20px",
|
line_height="20px",
|
||||||
font_size="var(--prismui-font-size-1)",
|
font_size="var(--prismui-font-size-1)",
|
||||||
color="var(--prismui-color-8)",
|
color="var(--prismui-color-8)",
|
||||||
|
|
@ -64,7 +64,7 @@ def conversation_history_item(
|
||||||
margin_bottom="8px",
|
margin_bottom="8px",
|
||||||
),
|
),
|
||||||
# 点击事件:切换会话
|
# 点击事件:切换会话
|
||||||
on_click=lambda: ConversationState.switch_conversation(conversation.id),
|
on_click=lambda: ConversationState.switch_conversation(item.id),
|
||||||
width="100%",
|
width="100%",
|
||||||
),
|
),
|
||||||
# 更多按钮:点击更多按钮显示气泡卡片,可删除会话
|
# 更多按钮:点击更多按钮显示气泡卡片,可删除会话
|
||||||
|
|
@ -97,7 +97,7 @@ def conversation_history_item(
|
||||||
"删除",
|
"删除",
|
||||||
# 点击事件:删除对话
|
# 点击事件:删除对话
|
||||||
on_click=lambda: ConversationState.delete_conversation(
|
on_click=lambda: ConversationState.delete_conversation(
|
||||||
conversation.id
|
item.id
|
||||||
),
|
),
|
||||||
display="flex",
|
display="flex",
|
||||||
align_items="center",
|
align_items="center",
|
||||||
|
|
@ -130,7 +130,7 @@ def conversation_history_item(
|
||||||
overflow="visible",
|
overflow="visible",
|
||||||
),
|
),
|
||||||
on_open_change=lambda is_shown: ConversationState.set_shown_more_conversation_id(
|
on_open_change=lambda is_shown: ConversationState.set_shown_more_conversation_id(
|
||||||
conversation.id, is_shown
|
item.id, is_shown
|
||||||
),
|
),
|
||||||
open_delay=0,
|
open_delay=0,
|
||||||
),
|
),
|
||||||
|
|
@ -138,7 +138,7 @@ def conversation_history_item(
|
||||||
top="27px",
|
top="27px",
|
||||||
right="16px",
|
right="16px",
|
||||||
transform="translateY(-50%)",
|
transform="translateY(-50%)",
|
||||||
z_index=99,
|
z_index="99",
|
||||||
min_width="14px",
|
min_width="14px",
|
||||||
cursor="pointer",
|
cursor="pointer",
|
||||||
opacity=rx.cond(highlight, "1", "0"),
|
opacity=rx.cond(highlight, "1", "0"),
|
||||||
|
|
@ -172,11 +172,11 @@ def conversation_history_item(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def conversation_history(
|
def conversation_history_items_showing(
|
||||||
is_conversation_history_shown: bool,
|
is_conversation_history_shown: bool,
|
||||||
) -> rx.Component:
|
) -> rx.Component:
|
||||||
"""
|
"""
|
||||||
会话历史
|
会话历史列表展示
|
||||||
:param is_conversation_history_shown: 会话历史展示状态
|
:param is_conversation_history_shown: 会话历史展示状态
|
||||||
:return: Component
|
:return: Component
|
||||||
"""
|
"""
|
||||||
|
|
@ -193,11 +193,10 @@ def conversation_history(
|
||||||
align_items="center",
|
align_items="center",
|
||||||
justify_content="space-between",
|
justify_content="space-between",
|
||||||
),
|
),
|
||||||
# 会话历史列表
|
|
||||||
rx.auto_scroll(
|
rx.auto_scroll(
|
||||||
rx.foreach(
|
rx.foreach(
|
||||||
ConversationState.conversation_history_items,
|
ConversationState.conversation_history_items,
|
||||||
conversation_history_item,
|
conversation_history_item_showing,
|
||||||
),
|
),
|
||||||
flex="1",
|
flex="1",
|
||||||
align_items="stretch",
|
align_items="stretch",
|
||||||
|
|
@ -227,9 +226,9 @@ def conversation_history(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def greeting() -> rx.Component:
|
def greeting_showing() -> rx.Component:
|
||||||
"""
|
"""
|
||||||
欢迎
|
欢迎展示
|
||||||
:return: Component
|
:return: Component
|
||||||
"""
|
"""
|
||||||
return rx.vstack(
|
return rx.vstack(
|
||||||
|
|
@ -332,20 +331,19 @@ def greeting() -> rx.Component:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def thought(thought):
|
def thought_showing(item: Thought):
|
||||||
"""
|
"""
|
||||||
思考卡片
|
思考展示
|
||||||
|
:param item: 思考实例
|
||||||
:param thought: 思考实例
|
|
||||||
:return: Component
|
:return: Component
|
||||||
"""
|
"""
|
||||||
return rx.vstack(
|
return rx.vstack(
|
||||||
rx.match(
|
rx.match(
|
||||||
thought[1].type,
|
item.type,
|
||||||
(
|
(
|
||||||
"thinking",
|
"thinking",
|
||||||
rx.text(
|
rx.text(
|
||||||
thought[1].content,
|
item.content,
|
||||||
line_height="22px",
|
line_height="22px",
|
||||||
font_size="var(--prismui-font-size-2)",
|
font_size="var(--prismui-font-size-2)",
|
||||||
color="var(--prismui-color-3)",
|
color="var(--prismui-color-3)",
|
||||||
|
|
@ -358,16 +356,16 @@ def thought(thought):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def dialog(dialog: Dialog) -> rx.Component:
|
def dialog_item_showing(item: DialogItem) -> rx.Component:
|
||||||
"""
|
"""
|
||||||
对话
|
对话项展示
|
||||||
:param dialog: 对话实例
|
:param item: 对话项实例
|
||||||
:return: Component
|
:return: Component
|
||||||
"""
|
"""
|
||||||
# 推理状态
|
# 推理状态
|
||||||
is_thinking = dialog.is_thinking
|
is_thinking = item.is_thinking
|
||||||
# 思考折叠面板展开状态
|
# 思考折叠面板展开状态
|
||||||
is_expanded = dialog.is_expanded
|
is_expanded = item.is_expanded
|
||||||
|
|
||||||
return rx.vstack(
|
return rx.vstack(
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
|
|
@ -375,13 +373,12 @@ def dialog(dialog: Dialog) -> rx.Component:
|
||||||
rx.vstack(
|
rx.vstack(
|
||||||
# 用户提示词
|
# 用户提示词
|
||||||
rx.text(
|
rx.text(
|
||||||
dialog.user_prompt,
|
item.user_prompt,
|
||||||
max_width="600px",
|
max_width="600px",
|
||||||
padding="12px 16px",
|
padding="12px 16px",
|
||||||
background_color="var(--prismui-background-color-3)",
|
background_color="var(--prismui-background-color-3)",
|
||||||
border_radius="var(--prismui-border-radius-3)",
|
border_radius="var(--prismui-border-radius-3)",
|
||||||
line_height="1.5",
|
line_height="1.5",
|
||||||
color="var(--prismui-color-text)",
|
|
||||||
word_wrap="break-word",
|
word_wrap="break-word",
|
||||||
word_break="break-all",
|
word_break="break-all",
|
||||||
white_space="pre-line",
|
white_space="pre-line",
|
||||||
|
|
@ -442,14 +439,14 @@ def dialog(dialog: Dialog) -> rx.Component:
|
||||||
),
|
),
|
||||||
cursor="pointer",
|
cursor="pointer",
|
||||||
# 点击事件,展开/折叠思考折叠面板
|
# 点击事件,展开/折叠思考折叠面板
|
||||||
on_click=lambda: ConversationState.toggle_collapse(dialog.id),
|
on_click=lambda: ConversationState.toggle_collapse(item.id),
|
||||||
),
|
),
|
||||||
rx.box(
|
rx.box(
|
||||||
rx.box(
|
rx.box(
|
||||||
rx.auto_scroll(
|
rx.auto_scroll(
|
||||||
rx.foreach(
|
rx.foreach(
|
||||||
dialog.thoughts,
|
item.thoughts,
|
||||||
thought,
|
thought_showing,
|
||||||
),
|
),
|
||||||
width="100%",
|
width="100%",
|
||||||
margin_top="8px",
|
margin_top="8px",
|
||||||
|
|
@ -470,20 +467,41 @@ def dialog(dialog: Dialog) -> rx.Component:
|
||||||
),
|
),
|
||||||
# 结果输出
|
# 结果输出
|
||||||
rx.markdown(
|
rx.markdown(
|
||||||
dialog.result_output,
|
item.result_output,
|
||||||
component_map={
|
component_map={
|
||||||
"p": lambda text: rx.text(
|
"p": lambda text: rx.text(
|
||||||
text,
|
text,
|
||||||
margin_bottom="8px",
|
margin="6px 0",
|
||||||
line_height="1.5",
|
line_height="1.5",
|
||||||
word_wrap="break-word",
|
word_wrap="break-word",
|
||||||
word_break="break-all",
|
word_break="break-all",
|
||||||
white_space="pre-line",
|
white_space="pre-line",
|
||||||
),
|
),
|
||||||
|
"h1": lambda text: rx.text(
|
||||||
|
text,
|
||||||
|
margin="12px 0 6px",
|
||||||
|
line_height="1.5",
|
||||||
|
font_size="20px",
|
||||||
|
font_weight="var(--prismui-font-weight-3)",
|
||||||
|
word_wrap="break-word",
|
||||||
|
word_break="break-all",
|
||||||
|
white_space="pre-line",
|
||||||
|
),
|
||||||
"h2": lambda text: rx.text(
|
"h2": lambda text: rx.text(
|
||||||
text,
|
text,
|
||||||
margin_bottom="8px",
|
margin="10px 0 6px",
|
||||||
line_height="1.5",
|
line_height="1.5",
|
||||||
|
font_size="18px",
|
||||||
|
font_weight="var(--prismui-font-weight-3)",
|
||||||
|
word_wrap="break-word",
|
||||||
|
word_break="break-all",
|
||||||
|
white_space="pre-line",
|
||||||
|
),
|
||||||
|
"h3": lambda text: rx.text(
|
||||||
|
text,
|
||||||
|
margin="8px 0 6px",
|
||||||
|
line_height="1.5",
|
||||||
|
font_size="16px",
|
||||||
font_weight="var(--prismui-font-weight-3)",
|
font_weight="var(--prismui-font-weight-3)",
|
||||||
word_wrap="break-word",
|
word_wrap="break-word",
|
||||||
word_break="break-all",
|
word_break="break-all",
|
||||||
|
|
@ -495,54 +513,71 @@ def dialog(dialog: Dialog) -> rx.Component:
|
||||||
font_weight="var(--prismui-font-weight-3)",
|
font_weight="var(--prismui-font-weight-3)",
|
||||||
),
|
),
|
||||||
"ul": lambda children: rx.vstack(
|
"ul": lambda children: rx.vstack(
|
||||||
children, margin_bottom="8px", gap="4px"
|
children,
|
||||||
|
align_items="flex-start",
|
||||||
|
margin="6px 0",
|
||||||
|
style={"padding": "0 !important"},
|
||||||
),
|
),
|
||||||
"li": lambda text: rx.text(
|
"ol": lambda children: rx.vstack(
|
||||||
text,
|
children,
|
||||||
display="list-item",
|
align_items="flex-start",
|
||||||
list_style_type="disc",
|
margin="6px 0",
|
||||||
list_style_position="outside",
|
style={"padding": "0 !important"},
|
||||||
margin="0 0 4px 16px",
|
),
|
||||||
line_height="1.5",
|
"li": lambda text: rx.hstack(
|
||||||
font_size="var(--prismui-font-size-1)",
|
rx.icon(
|
||||||
color="var(--prismui-color-1)",
|
"dot",
|
||||||
word_wrap="break-word",
|
flex_shrink="0",
|
||||||
word_break="break-all",
|
width="21px",
|
||||||
white_space="pre-line",
|
height="21px",
|
||||||
style={},
|
color="var(--prismui-color-5)",
|
||||||
|
),
|
||||||
|
rx.text(
|
||||||
|
text,
|
||||||
|
line_height="1.5",
|
||||||
|
word_wrap="break-word",
|
||||||
|
word_break="break-all",
|
||||||
|
white_space="pre-line",
|
||||||
|
),
|
||||||
|
align_items="flex-start",
|
||||||
|
margin_bottom="6px",
|
||||||
|
),
|
||||||
|
"hr": lambda _: rx.divider(
|
||||||
|
height="1px",
|
||||||
|
margin="12px 0",
|
||||||
|
background_color="var(--prismui-background-color-6)",
|
||||||
),
|
),
|
||||||
"hr": lambda _: rx.divider(margin="8px 0"),
|
|
||||||
},
|
},
|
||||||
width="100%",
|
width="100%",
|
||||||
margin_top="8px",
|
|
||||||
),
|
),
|
||||||
padding="0 16px",
|
padding="0 16px",
|
||||||
width="100%",
|
width="100%",
|
||||||
key=dialog.id,
|
key=item.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def dialogs_showing() -> rx.Component:
|
def dialog_items_showing() -> rx.Component:
|
||||||
"""
|
"""
|
||||||
对话列表展示
|
对话项列表展示
|
||||||
:return: Component
|
:return: Component
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 当前会话的对话列表
|
# 当前会话的对话列表
|
||||||
dialogs = ConversationState.dialogs
|
dialog_items = ConversationState.dialog_items
|
||||||
# 若对话列表为空则显示欢迎,否则显示对话列表
|
# 若对话列表为空则显示欢迎,否则显示对话列表
|
||||||
return rx.cond(
|
return rx.cond(
|
||||||
dialogs.length() == 0,
|
dialog_items.length() == 0,
|
||||||
# 欢迎
|
# 欢迎展示
|
||||||
greeting(),
|
greeting_showing(),
|
||||||
rx.vstack(
|
rx.vstack(
|
||||||
rx.auto_scroll(
|
rx.auto_scroll(
|
||||||
rx.foreach(
|
rx.foreach(
|
||||||
dialogs,
|
dialog_items,
|
||||||
dialog,
|
dialog_item_showing,
|
||||||
),
|
),
|
||||||
width="100%",
|
width="100%",
|
||||||
padding="0 12px",
|
padding="0 12px",
|
||||||
|
padding_bottom="180px",
|
||||||
),
|
),
|
||||||
flex="1",
|
flex="1",
|
||||||
width="100%",
|
width="100%",
|
||||||
|
|
@ -556,7 +591,7 @@ def dialogs_showing() -> rx.Component:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_conversation() -> rx.Component:
|
def conversation_creating() -> rx.Component:
|
||||||
"""
|
"""
|
||||||
创建会话
|
创建会话
|
||||||
:return: Component
|
:return: Component
|
||||||
|
|
@ -629,13 +664,15 @@ def create_conversation() -> rx.Component:
|
||||||
on_open_change=ConversationState.toggle_conversation_creating,
|
on_open_change=ConversationState.toggle_conversation_creating,
|
||||||
),
|
),
|
||||||
display="flex",
|
display="flex",
|
||||||
width="100%",
|
|
||||||
max_width="1200px",
|
|
||||||
height="39px",
|
|
||||||
padding="0 12px",
|
|
||||||
justify_content="flex-end",
|
justify_content="flex-end",
|
||||||
align_items="center",
|
align_items="center",
|
||||||
gap="4px",
|
gap="4px",
|
||||||
|
padding="0 12px",
|
||||||
|
margin_bottom="8px",
|
||||||
|
width="100%",
|
||||||
|
max_width="1200px",
|
||||||
|
height="39px",
|
||||||
|
background="transparent",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -645,6 +682,8 @@ def user_prompt_sending() -> rx.Component:
|
||||||
:return: Component
|
:return: Component
|
||||||
"""
|
"""
|
||||||
return rx.vstack(
|
return rx.vstack(
|
||||||
|
# 创建对话
|
||||||
|
conversation_creating(),
|
||||||
rx.vstack(
|
rx.vstack(
|
||||||
rx.text_area(
|
rx.text_area(
|
||||||
# 值绑定用户提示词
|
# 值绑定用户提示词
|
||||||
|
|
@ -654,7 +693,8 @@ def user_prompt_sending() -> rx.Component:
|
||||||
placeholder="请输入您的问题,按Enter换行",
|
placeholder="请输入您的问题,按Enter换行",
|
||||||
vertical_align="middle",
|
vertical_align="middle",
|
||||||
width="100%",
|
width="100%",
|
||||||
height="64px",
|
min_height="64px",
|
||||||
|
max_height="224px",
|
||||||
padding="4px 0",
|
padding="4px 0",
|
||||||
background_color="var(--prismui-background-color-1)",
|
background_color="var(--prismui-background-color-1)",
|
||||||
border="none !important",
|
border="none !important",
|
||||||
|
|
@ -665,6 +705,10 @@ def user_prompt_sending() -> rx.Component:
|
||||||
"border": "none !important",
|
"border": "none !important",
|
||||||
"outline": "none !important",
|
"outline": "none !important",
|
||||||
"box-shadow": "none !important",
|
"box-shadow": "none !important",
|
||||||
|
"field-sizing": "content !important",
|
||||||
|
"min-height": "64px",
|
||||||
|
"max-height": "224px",
|
||||||
|
"overflow-y": "auto",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -731,6 +775,10 @@ def user_prompt_sending() -> rx.Component:
|
||||||
font_size="var(--prismui-font-size-1)",
|
font_size="var(--prismui-font-size-1)",
|
||||||
color="var(--prismui-color-10)",
|
color="var(--prismui-color-10)",
|
||||||
),
|
),
|
||||||
|
position="absolute",
|
||||||
|
bottom="0",
|
||||||
|
left="0",
|
||||||
|
z_index="99",
|
||||||
display="flex",
|
display="flex",
|
||||||
flex_direction="column",
|
flex_direction="column",
|
||||||
width="100%",
|
width="100%",
|
||||||
|
|
@ -756,12 +804,14 @@ def conversation_history_collapse_button(
|
||||||
height="16px",
|
height="16px",
|
||||||
color="var(--prismui-color-3)",
|
color="var(--prismui-color-3)",
|
||||||
),
|
),
|
||||||
|
# 点击事件:切换会话历史折叠面板展开状态
|
||||||
|
on_click=ConversationState.toggle_conversation_history_shown,
|
||||||
position="absolute",
|
position="absolute",
|
||||||
top="calc(50% - 20px)",
|
top="calc(50% - 20px)",
|
||||||
left=rx.cond(
|
left=rx.cond(
|
||||||
is_conversation_history_shown, "calc(clamp(240px, 25%, 380px) - 8px)", "0"
|
is_conversation_history_shown, "calc(clamp(240px, 25%, 380px) - 8px)", "0"
|
||||||
),
|
),
|
||||||
z_index=99,
|
z_index="99",
|
||||||
width="16px",
|
width="16px",
|
||||||
height="40px",
|
height="40px",
|
||||||
background_color="var(--prismui-background-color-1)",
|
background_color="var(--prismui-background-color-1)",
|
||||||
|
|
@ -773,8 +823,6 @@ def conversation_history_collapse_button(
|
||||||
box_shadow="var(--prismui-box-shadow-4)",
|
box_shadow="var(--prismui-box-shadow-4)",
|
||||||
transition="all 0.3s ease-in-out",
|
transition="all 0.3s ease-in-out",
|
||||||
cursor="pointer",
|
cursor="pointer",
|
||||||
# 点击事件:切换会话历史折叠面板展开状态
|
|
||||||
on_click=ConversationState.toggle_conversation_history_shown,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -787,29 +835,26 @@ def conversation() -> rx.Component:
|
||||||
|
|
||||||
return rx.box(
|
return rx.box(
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
# 会话历史
|
# 会话历史列表展示
|
||||||
conversation_history(is_conversation_history_shown),
|
conversation_history_items_showing(is_conversation_history_shown),
|
||||||
rx.box(
|
rx.box(
|
||||||
rx.vstack(
|
rx.vstack(
|
||||||
# 对话列表展示
|
# 对话列表展示
|
||||||
dialogs_showing(),
|
dialog_items_showing(),
|
||||||
# 创建对话
|
|
||||||
create_conversation(),
|
|
||||||
# 用户提示词发送
|
|
||||||
user_prompt_sending(),
|
|
||||||
display="flex",
|
|
||||||
flex_flow="column",
|
|
||||||
width="100%",
|
width="100%",
|
||||||
height="100%",
|
height="100%",
|
||||||
gap="8px",
|
gap="8px",
|
||||||
align_items="center",
|
align_items="center",
|
||||||
),
|
),
|
||||||
|
# 用户提示词发送
|
||||||
|
user_prompt_sending(),
|
||||||
position="relative",
|
position="relative",
|
||||||
display="flex",
|
display="flex",
|
||||||
flex="1",
|
flex="1",
|
||||||
width="0",
|
width="0",
|
||||||
height="100%",
|
height="100%",
|
||||||
background="linear-gradient(180deg, #fffffff2, #f8fafff2 99%)",
|
padding_bottom="120px",
|
||||||
|
background="var(--prismui-background-2)",
|
||||||
),
|
),
|
||||||
position="relative",
|
position="relative",
|
||||||
display="flex",
|
display="flex",
|
||||||
|
|
@ -821,11 +866,11 @@ def conversation() -> rx.Component:
|
||||||
),
|
),
|
||||||
# 会话历史折叠面板按钮
|
# 会话历史折叠面板按钮
|
||||||
conversation_history_collapse_button(is_conversation_history_shown),
|
conversation_history_collapse_button(is_conversation_history_shown),
|
||||||
|
# 挂载事件:恢复会话状态
|
||||||
|
on_mount=AuthState.resume_conversation_state,
|
||||||
position="relative",
|
position="relative",
|
||||||
width="100%",
|
width="100%",
|
||||||
height="100%",
|
height="100%",
|
||||||
border_radius="var(--prismui-border-radius-4)",
|
border_radius="var(--prismui-border-radius-4)",
|
||||||
overflow="hidden",
|
overflow="hidden",
|
||||||
# 挂载事件:恢复当前用户会话状态
|
|
||||||
on_mount=AuthState.resume_conversation_state,
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -190,9 +190,9 @@ class AuthState(rx.State):
|
||||||
@rx.event
|
@rx.event
|
||||||
async def resume_conversation_state(self):
|
async def resume_conversation_state(self):
|
||||||
"""
|
"""
|
||||||
恢复当前用户会话状态
|
恢复会话状态
|
||||||
"""
|
"""
|
||||||
# 恢复当前用户会话状态
|
# 恢复会话状态
|
||||||
conversation_state = await self.get_state(ConversationState)
|
conversation_state = await self.get_state(ConversationState)
|
||||||
await conversation_state.resume(user_id=self.user_id)
|
await conversation_state.resume(user_id=self.user_id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import reflex as rx
|
||||||
from application.states.database import DatabaseState
|
from application.states.database import DatabaseState
|
||||||
from application.domain_models import (
|
from application.domain_models import (
|
||||||
Conversation,
|
Conversation,
|
||||||
Dialog,
|
DialogItem,
|
||||||
Thought,
|
Thought,
|
||||||
ConversationHistoryItem,
|
ConversationHistoryItem,
|
||||||
)
|
)
|
||||||
|
|
@ -252,16 +252,29 @@ class ConversationState(rx.State):
|
||||||
return conversation.is_running
|
return conversation.is_running
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def dialogs(self) -> List[Dialog]:
|
def dialog_items(self) -> List[DialogItem]:
|
||||||
"""
|
"""
|
||||||
当前会话的对话列表
|
当前会话的对话项列表
|
||||||
:return: 当前会话的对话列表
|
:return: 当前会话的对话项列表
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return []
|
return []
|
||||||
return list(conversation.dialogs.values())
|
|
||||||
|
items: List[DialogItem] = []
|
||||||
|
for dialog in conversation.dialogs.values():
|
||||||
|
items.append(
|
||||||
|
DialogItem(
|
||||||
|
id=dialog.id,
|
||||||
|
user_prompt=dialog.user_prompt,
|
||||||
|
thoughts=list(dialog.thoughts.values()),
|
||||||
|
result_output=dialog.result_output,
|
||||||
|
is_thinking=dialog.is_thinking,
|
||||||
|
is_expanded=dialog.is_expanded,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return items
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
async def handle_user_prompt(self) -> AsyncGenerator[None]:
|
async def handle_user_prompt(self) -> AsyncGenerator[None]:
|
||||||
|
|
@ -278,27 +291,26 @@ class ConversationState(rx.State):
|
||||||
|
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations[self.conversation_id]
|
conversation = self.conversations[self.conversation_id]
|
||||||
# 将当前会话的运行状态设置为正在运行
|
# 将运行状态设置为正在运行
|
||||||
conversation.is_running = True
|
conversation.is_running = True
|
||||||
yield # 通知前端渲染
|
yield
|
||||||
|
|
||||||
# 获取数据库状态
|
|
||||||
database_state = await self.get_state(DatabaseState)
|
database_state = await self.get_state(DatabaseState)
|
||||||
|
|
||||||
# 获取消息历史
|
|
||||||
message_history = await database_state.get_message_history(
|
|
||||||
conversation_id=self.conversation_id
|
|
||||||
)
|
|
||||||
|
|
||||||
# 创建对话记录再添加对话实例
|
# 创建对话记录再添加对话实例
|
||||||
conversation.dialogs.update(
|
conversation.dialogs.update(
|
||||||
await database_state.create_dialog_record(
|
await database_state.create_dialog_record(
|
||||||
conversation_id=self.conversation_id, user_prompt=user_prompt
|
conversation_id=self.conversation_id, user_prompt=user_prompt
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# 将最后一个会话作为当前会话
|
# 将最后一个对话作为当前对话
|
||||||
dialog = next(reversed(conversation.dialogs.values()))
|
dialog = next(reversed(conversation.dialogs.values()))
|
||||||
|
|
||||||
|
# 获取消息历史
|
||||||
|
message_history = await database_state.get_message_history(
|
||||||
|
conversation_id=self.conversation_id
|
||||||
|
)
|
||||||
|
|
||||||
# 初始化工具调用唯一标识和片段索引映射字典
|
# 初始化工具调用唯一标识和片段索引映射字典
|
||||||
tool_call_ids: Dict[str, int] = {}
|
tool_call_ids: Dict[str, int] = {}
|
||||||
async with agent.run_stream_events(
|
async with agent.run_stream_events(
|
||||||
|
|
@ -324,6 +336,7 @@ class ConversationState(rx.State):
|
||||||
dialog.thoughts[index] = Thought(
|
dialog.thoughts[index] = Thought(
|
||||||
type="thinking", content=content
|
type="thinking", content=content
|
||||||
)
|
)
|
||||||
|
yield
|
||||||
|
|
||||||
# 工具检索分片开始事件
|
# 工具检索分片开始事件
|
||||||
case ToolSearchCallPart(tool_call_id=tool_call_id):
|
case ToolSearchCallPart(tool_call_id=tool_call_id):
|
||||||
|
|
@ -334,6 +347,7 @@ class ConversationState(rx.State):
|
||||||
type="tool_search",
|
type="tool_search",
|
||||||
content="正在生成检索关键词",
|
content="正在生成检索关键词",
|
||||||
)
|
)
|
||||||
|
yield
|
||||||
|
|
||||||
# 能力加载分片开始事件
|
# 能力加载分片开始事件
|
||||||
case LoadCapabilityCallPart(tool_call_id=tool_call_id):
|
case LoadCapabilityCallPart(tool_call_id=tool_call_id):
|
||||||
|
|
@ -343,6 +357,7 @@ class ConversationState(rx.State):
|
||||||
type="capability_load",
|
type="capability_load",
|
||||||
content="正在生成加载参数",
|
content="正在生成加载参数",
|
||||||
)
|
)
|
||||||
|
yield
|
||||||
|
|
||||||
# 工具调用分片开始事件
|
# 工具调用分片开始事件
|
||||||
case ToolCallPart(tool_call_id=tool_call_id):
|
case ToolCallPart(tool_call_id=tool_call_id):
|
||||||
|
|
@ -352,10 +367,12 @@ class ConversationState(rx.State):
|
||||||
type="tool_call",
|
type="tool_call",
|
||||||
content="正在生成调用参数",
|
content="正在生成调用参数",
|
||||||
)
|
)
|
||||||
|
yield
|
||||||
|
|
||||||
# 文本分片开始事件
|
# 文本分片开始事件
|
||||||
case TextPart(content=content):
|
case TextPart(content=content):
|
||||||
dialog.result_output = content
|
dialog.result_output = content
|
||||||
|
yield
|
||||||
|
|
||||||
# ========== 增量事件 ==========
|
# ========== 增量事件 ==========
|
||||||
case PartDeltaEvent(index=index, delta=delta):
|
case PartDeltaEvent(index=index, delta=delta):
|
||||||
|
|
@ -365,12 +382,14 @@ class ConversationState(rx.State):
|
||||||
content_delta=content_delta,
|
content_delta=content_delta,
|
||||||
):
|
):
|
||||||
dialog.thoughts[index].content += content_delta or ""
|
dialog.thoughts[index].content += content_delta or ""
|
||||||
|
yield
|
||||||
|
|
||||||
# 文本分片增量事件
|
# 文本分片增量事件
|
||||||
case TextPartDelta(
|
case TextPartDelta(
|
||||||
content_delta=content_delta,
|
content_delta=content_delta,
|
||||||
):
|
):
|
||||||
dialog.result_output += content_delta
|
dialog.result_output += content_delta
|
||||||
|
yield
|
||||||
|
|
||||||
# ========== 结束事件 ==========
|
# ========== 结束事件 ==========
|
||||||
case PartEndEvent(
|
case PartEndEvent(
|
||||||
|
|
@ -385,6 +404,7 @@ class ConversationState(rx.State):
|
||||||
if next_part_kind == "text":
|
if next_part_kind == "text":
|
||||||
dialog.is_thinking = False
|
dialog.is_thinking = False
|
||||||
dialog.is_expanded = False
|
dialog.is_expanded = False
|
||||||
|
yield
|
||||||
|
|
||||||
# ========== 函数工具调用事件 ==========
|
# ========== 函数工具调用事件 ==========
|
||||||
case FunctionToolCallEvent(tool_call_id=tool_call_id, part=part):
|
case FunctionToolCallEvent(tool_call_id=tool_call_id, part=part):
|
||||||
|
|
@ -394,18 +414,21 @@ class ConversationState(rx.State):
|
||||||
# 工具检索
|
# 工具检索
|
||||||
case "tool_search":
|
case "tool_search":
|
||||||
dialog.thoughts[index].content = "正在检索"
|
dialog.thoughts[index].content = "正在检索"
|
||||||
|
yield
|
||||||
|
|
||||||
# 能力加载
|
# 能力加载
|
||||||
case "capability_load":
|
case "capability_load":
|
||||||
dialog.thoughts[index].content = (
|
dialog.thoughts[index].content = (
|
||||||
f"正在加载能力 {part.tool_name}"
|
f"正在加载能力 {part.tool_name}"
|
||||||
)
|
)
|
||||||
|
yield
|
||||||
|
|
||||||
# 工具调用
|
# 工具调用
|
||||||
case "tool_call":
|
case "tool_call":
|
||||||
dialog.thoughts[index].content = (
|
dialog.thoughts[index].content = (
|
||||||
f"正在调用工具 {part.tool_name}"
|
f"正在调用工具 {part.tool_name}"
|
||||||
)
|
)
|
||||||
|
yield
|
||||||
|
|
||||||
# ========== 函数工具结果事件 ==========
|
# ========== 函数工具结果事件 ==========
|
||||||
case FunctionToolResultEvent(
|
case FunctionToolResultEvent(
|
||||||
|
|
@ -419,14 +442,17 @@ class ConversationState(rx.State):
|
||||||
dialog.thoughts[index].content = (
|
dialog.thoughts[index].content = (
|
||||||
content if isinstance(content, str) else ""
|
content if isinstance(content, str) else ""
|
||||||
) # 暂仅考虑文本内容
|
) # 暂仅考虑文本内容
|
||||||
|
yield
|
||||||
|
|
||||||
# 能力加载
|
# 能力加载
|
||||||
case "capability_load":
|
case "capability_load":
|
||||||
dialog.thoughts[index].content = "已加载"
|
dialog.thoughts[index].content = "已加载"
|
||||||
|
yield
|
||||||
|
|
||||||
# 工具调用
|
# 工具调用
|
||||||
case "tool_call":
|
case "tool_call":
|
||||||
dialog.thoughts[index].content = f"已调用"
|
dialog.thoughts[index].content = f"已调用"
|
||||||
|
yield
|
||||||
|
|
||||||
# ========== 智能体运行结果事件 ==========
|
# ========== 智能体运行结果事件 ==========
|
||||||
case AgentRunResultEvent(result=result):
|
case AgentRunResultEvent(result=result):
|
||||||
|
|
@ -444,8 +470,7 @@ class ConversationState(rx.State):
|
||||||
)
|
)
|
||||||
# 将当前会话的运行状态设置为运行完成
|
# 将当前会话的运行状态设置为运行完成
|
||||||
conversation.is_running = False
|
conversation.is_running = False
|
||||||
|
yield
|
||||||
yield
|
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def toggle_collapse(self, dialog_id: str) -> None:
|
def toggle_collapse(self, dialog_id: str) -> None:
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue