This commit is contained in:
liubiren 2026-08-10 19:33:08 +08:00
parent 8c8fda3da6
commit 55aa7e30e9
7 changed files with 102 additions and 82 deletions

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from application.pages.conversation import conversation from application.pages.conversation import conversation_page
from application.pages.knowledge_base import knowledge_base from application.pages.knowledge_base import knowledge_base_page
__all__ = ["conversation", "knowledge_base"] __all__ = ["conversation_page", "knowledge_base_page"]

View File

@ -9,32 +9,32 @@ from application.states.models import (
Conversation, Conversation,
Dialog, Dialog,
Thought, Thought,
ConversationHistoryItem,
) )
from application.states import ConversationState, AuthState from application.states import ConversationState, AuthState
def conversation_history_item( def conversation_history_item(
item: Tuple[str, Conversation], item: ConversationHistoryItem,
) -> rx.Component: ) -> rx.Component:
""" """
会话历史项 会话历史项
:param item: 会话历史项 :param item: 会话历史项
:return: Component :return: Component
""" """
conversation = item[1]
# 高亮:若为当前会话或显示更多则高亮 # 高亮
highlight: bool = ( is_highlight = item.id in (
conversation.id ConversationState.is_conversation_history_item_popover_shown,
== ConversationState.is_conversation_history_item_more_button_shown ConversationState.actived_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",
@ -53,7 +53,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,8 +64,8 @@ def conversation_history_item(
height="20px", height="20px",
margin_bottom="8px", margin_bottom="8px",
), ),
# 点击事件:切换会话 # 点击事件:设置激活会话
on_click=lambda: ConversationState.switch_conversation(conversation.id), on_click=lambda: ConversationState.set_actived_conversation(item.id),
width="100%", width="100%",
), ),
# 更多按钮:点击更多按钮显示气泡卡片,可删除会话 # 更多按钮:点击更多按钮显示气泡卡片,可删除会话
@ -98,7 +98,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,8 +130,9 @@ def conversation_history_item(
box_shadow="var(--prismui-box-shadow-6)", box_shadow="var(--prismui-box-shadow-6)",
overflow="visible", overflow="visible",
), ),
on_open_change=lambda is_shown: ConversationState.set_shown_more_conversation_id( # 悬停气泡显示 / 隐藏变化事件:设置会话历史项悬停气泡显示 / 隐藏
conversation.id, is_shown on_open_change=lambda is_shown: ConversationState.set_conversation_history_item_popover_shown(
item.id, is_shown
), ),
open_delay=0, open_delay=0,
), ),
@ -227,9 +228,9 @@ def conversation_history(
) )
def greeting_showing() -> rx.Component: def guidance() -> rx.Component:
""" """
欢迎展示 引导
:return: Component :return: Component
""" """
return rx.vstack( return rx.vstack(
@ -360,9 +361,9 @@ def thought_showing(item: Tuple[int, Thought]):
) )
def dialog_showing(item: Tuple[str, Dialog]) -> rx.Component: def message_history_item(item: Tuple[str, Dialog]) -> rx.Component:
""" """
对话展示 消息历史项
:param item: 对话实例 :param item: 对话实例
:return: Component :return: Component
""" """
@ -612,24 +613,23 @@ def dialog_showing(item: Tuple[str, Dialog]) -> rx.Component:
) )
def dialog_items_showing() -> rx.Component: def message_history() -> rx.Component:
""" """
对话项列表展示 消息历史
:return: Component :return: Component
""" """
# 消息历史
# 当前会话的对话列表 message_history = ConversationState.message_history
dialogs = ConversationState.dialogs return rx.box(
# 若对话列表为空则显示欢迎,否则显示对话列表 # 若消息历史为空则显示引导,否则渲染消息历史项
return rx.cond( rx.cond(
dialogs.length() == 0, message_history.length() == 0,
# 欢迎展示 guidance(),
greeting_showing(),
rx.vstack( rx.vstack(
rx.auto_scroll( rx.auto_scroll(
rx.foreach( rx.foreach(
dialogs, message_history,
dialog_showing, message_history_item,
), ),
width="100%", width="100%",
padding="0 12px", padding="0 12px",
@ -649,6 +649,7 @@ def dialog_items_showing() -> rx.Component:
overflow_x="hidden", overflow_x="hidden",
overflow_y="auto", overflow_y="auto",
), ),
),
) )
@ -723,7 +724,7 @@ def user_prompt_sending() -> rx.Component:
conversation_creating(), conversation_creating(),
rx.vstack( rx.vstack(
rx.text_area( rx.text_area(
# 值绑定用户提示词 # 值绑定用户提示词
value=ConversationState.user_prompt, value=ConversationState.user_prompt,
# 输入事件:设置用户提示词 # 输入事件:设置用户提示词
on_change=ConversationState.set_user_prompt, on_change=ConversationState.set_user_prompt,
@ -867,7 +868,7 @@ def conversation_history_collapse_button(
def conversation_page() -> rx.Component: def conversation_page() -> rx.Component:
""" """
会话页面布局参考 MetaChat从左到右分别为会话历史和工作区其中工作区从上到下分别为对话历史和用户提示词输入框 会话页面布局参考 MetaChat从左到右分别为会话历史和工作区其中工作区从上到下分别为消息历史和用户提示词输入框
""" """
# 会话历史展示状态 # 会话历史展示状态
is_shown = ConversationState.is_conversation_history_shown is_shown = ConversationState.is_conversation_history_shown
@ -880,7 +881,7 @@ def conversation_page() -> rx.Component:
rx.box( rx.box(
# 对话历史 # 对话历史
rx.vstack( rx.vstack(
dialog_history(), message_history(),
width="100%", width="100%",
height="100%", height="100%",
gap="8px", gap="8px",

View File

@ -4,7 +4,7 @@
""" """
import reflex as rx import reflex as rx
from application.pages import conversation, knowledge_base from application.pages import conversation_page, knowledge_base_page
from application.states import AuthState from application.states import AuthState
@ -434,13 +434,13 @@ def index() -> rx.Component:
rx.hstack( rx.hstack(
# 侧边栏 # 侧边栏
sidebar(), sidebar(),
# 根据侧边栏状态中激活的导航按钮相应页面(本项目采用卡片布局) # 根据侧边栏状态中激活的导航按钮相应页面
rx.match( rx.match(
AuthState.activated_nav_button, AuthState.activated_nav_button,
# 知识库页面 # 知识库页面
("knowledge_base", knowledge_base()), ("knowledge_base", knowledge_base_page()),
# 会话页面 # 会话页面
conversation(), conversation_page(),
), ),
width="100%", width="100%",
height="100vh", height="100vh",

View File

@ -5,7 +5,7 @@
import reflex as rx import reflex as rx
def knowledge_base() -> rx.Component: def knowledge_base_page() -> rx.Component:
""" """
知识库页面 知识库页面
""" """

View File

@ -66,8 +66,8 @@ class ConversationState(rx.State):
# 显示会话历史True 表示显示False 表示隐藏 # 显示会话历史True 表示显示False 表示隐藏
is_conversation_history_shown: bool = False is_conversation_history_shown: bool = False
# 会话历史项显示悬停气泡True 表示显示False 表示隐藏 # 会话历史项显示悬停气泡
is_conversation_history_item_popover_shown: bool = False is_conversation_history_item_popover_shown: str = ""
# 当前数据库状态(私有变量) # 当前数据库状态(私有变量)
# 私有变量reflex 约定以 _ 开头的变量为私有变量,后端不序列化,前端不可使用 # 私有变量reflex 约定以 _ 开头的变量为私有变量,后端不序列化,前端不可使用
@ -121,6 +121,7 @@ class ConversationState(rx.State):
""" """
return [ return [
ConversationHistoryItem( ConversationHistoryItem(
id=conversation.id,
description=conversation.description, description=conversation.description,
created_at=format_conversation_created_at(conversation.created_at), created_at=format_conversation_created_at(conversation.created_at),
) )
@ -128,12 +129,16 @@ class ConversationState(rx.State):
] ]
@rx.event @rx.event
def set_conversation_history_item_popover_shown(self, is_shown: bool) -> None: def set_conversation_history_item_popover_shown(
self, conversation_id: str, is_shown: bool
) -> None:
""" """
设置会话历史项悬停气泡显示 / 隐藏 设置会话历史项悬停气泡显示 / 隐藏
:return: None :return: None
""" """
self.is_conversation_history_item_popover_shown = is_shown self.is_conversation_history_item_popover_shown = (
conversation_id if is_shown else ""
)
@rx.event @rx.event
async def delete_conversation(self, conversation_id: str) -> None: async def delete_conversation(self, conversation_id: str) -> None:
@ -166,6 +171,18 @@ class ConversationState(rx.State):
""" """
self.actived_conversation_id = conversation_id self.actived_conversation_id = conversation_id
@rx.var
def message_history(self) -> list[Message]:
"""
获取当前会话的消息历史
:return: 当前会话的消息历史
"""
# 当前会话
conversation = self.conversations.get(self.actived_conversation_id)
if not conversation:
return []
return list(conversation.messages.values())
@rx.event @rx.event
async def create_conversation(self) -> None: async def create_conversation(self) -> None:
""" """
@ -181,6 +198,18 @@ class ConversationState(rx.State):
# 将最后一个会话的唯一标识作为激活会话唯一标识 # 将最后一个会话的唯一标识作为激活会话唯一标识
self.actived_conversation_id = next(reversed(self.conversations.keys())) self.actived_conversation_id = next(reversed(self.conversations.keys()))
@rx.var
def user_prompt(self) -> str:
"""
用户提示词
:return: 用户提示词
"""
# 当前会话
conversation = self.conversations.get(self.actived_conversation_id)
if not conversation:
return ""
return conversation.user_prompt
@rx.event @rx.event
def set_user_prompt(self, user_prompt: str) -> None: def set_user_prompt(self, user_prompt: str) -> None:
""" """
@ -276,7 +305,7 @@ class ConversationState(rx.State):
from application.workshop.unstructured_dialogue import ( from application.workshop.unstructured_dialogue import (
run_stream_events, run_stream_events,
) )
# 运行并流式输出事件
stream_events = run_stream_events( stream_events = run_stream_events(
user_prompt=user_prompt, user_prompt=user_prompt,
message_history=message_history, message_history=message_history,
@ -415,11 +444,6 @@ class ConversationState(rx.State):
case "tool_call": case "tool_call":
dialog.thoughts[index].content = f"已调用 {content}" dialog.thoughts[index].content = f"已调用 {content}"
case TaskNodeResultEvent(task=task, content=content):
# 更新任务
conversation.task = task
dialog.result_output += content
# ========== 智能体运行结果事件 ========== # ========== 智能体运行结果事件 ==========
case AgentRunResultEvent(result=result): case AgentRunResultEvent(result=result):
# 创建对话记录 # 创建对话记录

View File

@ -23,7 +23,6 @@ class MessageType(StrEnum):
RESULT_OUTPUT = "result_output" RESULT_OUTPUT = "result_output"
class Message(BaseModel): class Message(BaseModel):
""" """
消息类 消息类
@ -73,12 +72,6 @@ class WorkType(StrEnum):
BOOK_FLIGHT = "预定航班" BOOK_FLIGHT = "预定航班"
class TaskStatus(StrEnum):
"""
任务状态枚举
"""
NONE = "none"
class Deps(BaseModel): class Deps(BaseModel):
@ -126,6 +119,7 @@ class ConversationHistoryItem(BaseModel):
会话历史项类 会话历史项类
""" """
id: str = Field(..., description="会话唯一标识")
description: str = Field(..., description="会话描述") description: str = Field(..., description="会话描述")
created_at: str = Field(..., description="会话创建日期时间") created_at: str = Field(..., description="会话创建日期时间")

View File

@ -17,7 +17,7 @@ from application.states.models import (
usage_to_dict, usage_to_dict,
usage_to_object, usage_to_object,
) )
from application.tasks.models import DEEPSEEK_V4_FLASH_MODEL, MODEL_SETTINGS from application.workshop.models import DEEPSEEK_V4_FLASH_MODEL, MODEL_SETTINGS
class Deps(BaseModel): class Deps(BaseModel):
@ -67,7 +67,7 @@ flight_details_extraction_agent = Agent(
) )
@flight_search_agent.tool(name="提取所有航班详情") @flight_search_agent.tool
async def extract_flight_details(ctx: RunContext[Deps]) -> list[FlightDetail]: async def extract_flight_details(ctx: RunContext[Deps]) -> list[FlightDetail]:
""" """
工具提取所有航班详情 工具提取所有航班详情
@ -168,15 +168,16 @@ flight_info = """
def init_task() -> Task: def init_task() -> Task:
return Task( return WorkFlow(
type=TaskType.BOOK_FLIGHT, type=WorkType.BOOK_FLIGHT,
node="flight_search", tools={"extract_flight_details": {"": ""}},
deps=Deps( deps=Deps(
flight_info=flight_info, flight_info=flight_info,
date=datetime.date(2025, 1, 10), date=datetime.date(2025, 1, 10),
origin="SFO", origin="SFO",
destination="ANC", destination="ANC",
), ),
usage_limits={},
) )