From 75a1cd998ec62775da43fc18d7aa0f260ef9a68d Mon Sep 17 00:00:00 2001 From: liubiren Date: Wed, 10 Jun 2026 20:58:31 +0800 Subject: [PATCH] =?UTF-8?q?260610=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/agent.py | 13 +- utils/agent_memory.db | Bin 36864 -> 90112 bytes 产品需求文档AI生成/application/application.py | 32 +--- .../application/components/navbar.py | 142 ------------------ .../application/components/navigation_bar.py | 137 +++++++++++++++++ .../application/components/session.py | 103 +++++-------- .../application/pages/__init__.py | 0 产品需求文档AI生成/application/pages/index.py | 23 +++ 产品需求文档AI生成/application/state.py | 133 ++++++++-------- 产品需求文档AI生成/rxconfig.py | 14 +- 10 files changed, 300 insertions(+), 297 deletions(-) delete mode 100644 产品需求文档AI生成/application/components/navbar.py create mode 100644 产品需求文档AI生成/application/components/navigation_bar.py create mode 100644 产品需求文档AI生成/application/pages/__init__.py create mode 100644 产品需求文档AI生成/application/pages/index.py diff --git a/utils/agent.py b/utils/agent.py index 4ebafdd..9270f4c 100644 --- a/utils/agent.py +++ b/utils/agent.py @@ -118,19 +118,21 @@ class Agent: def __init__( self, + session_id: str, instructions: str, output_type: OutputSpec = str, capabilities: Optional[List[AgentCapability]] = None, ): """ 初始化智能体 + :param session_id: 会话唯一标识 :param instructions: 指令 :param skills: 智能体技能列表,默认为不使用技能 :param output_type: 输出类型 :return: 智能体实例 """ - # 生成会话唯一标识 - self.session_id = uuid4().hex.lower() + # 会话唯一标识 + self.session_id = session_id # 实例智能体的记忆体 self.agent_memory = AgentMemory() @@ -150,6 +152,8 @@ class Agent: retries=1, ) + self.agent.to_web() + async def output_message_streamed( self, user_prompt: str | List[str] ) -> AsyncGenerator[str, None]: @@ -167,8 +171,9 @@ class Agent: async with self.agent.run_stream( user_prompt=user_prompt, message_history=message_history ) as result: - async for chunk in result.stream_text(): - yield chunk + async for chunk in result.stream_text(delta=True): # 只返回新增内容 + if chunk: + yield chunk self.agent_memory.create_new_messages( session_id=self.session_id, diff --git a/utils/agent_memory.db b/utils/agent_memory.db index 54008daf0f7aa15d1c3968459d4cf789f4a96c1c..6ad6cd47d4089a6eef100853b58cba02d8002b10 100644 GIT binary patch literal 90112 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 delta 5094 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 rc9Y#=88+DltoGq<>!5YG({i|YzM1U&PkiXb-S Component: - """The main app.""" - return VStack( - navbar.navbar(), - session.session(), - session.action_bar(), - background_color=Color(color="mauve", shade=1), - color=Color(color="mauve", shade=12), - height="100dvh", - align_items="stretch", - spacing="0", - ) - - -# Add state and page to the app. -app = App( - theme=Theme( - appearance="dark", - accent_color="purple", - ), -) +app = reflex.App() app.add_page(index) diff --git a/产品需求文档AI生成/application/components/navbar.py b/产品需求文档AI生成/application/components/navbar.py deleted file mode 100644 index a3f78d0..0000000 --- a/产品需求文档AI生成/application/components/navbar.py +++ /dev/null @@ -1,142 +0,0 @@ -# -*- coding: utf-8 -*- -""" -导航栏组件 -""" - -from reflex import ( - Component, - badge as Badge, - button as Button, - color as Color, - dialog as Dialog, - divider as Divider, - drawer as Drawer, - foreach as Foreach, - form as Form, - heading as Heading, - hstack as HStack, - icon as Icon, - input as Input, - tooltip as Tooltip, - vstack as VStack, -) - -from ..state import State - - -def sidebar_session(session_name: str) -> Component: - """ - 侧边栏会话组件 - :param session_name: 会话名称 - :return: Component - """ - return Drawer.close( - HStack( - Button( - session_name, - on_click=lambda: State.set_current_session(session_name), - width="80%", - variant="surface", - ), - Button( - Icon( - tag="trash", - on_click=lambda: State.delete_session(session_name), - stroke_width=1, - ), - width="20%", - variant="surface", - color_scheme="red", - ), - width="100%", - ), - key=session_name, - ) - - -def sidebar(trigger) -> Component: - """ - 侧边栏组件 - """ - return Drawer.root( - Drawer.trigger(trigger), - Drawer.overlay(), - Drawer.portal( - Drawer.content( - VStack( - Heading("Chats", color=Color("mauve", 11)), - Divider(), - Foreach( - State.get_session_names, - lambda session_name: sidebar_session(session_name), - ), - align_items="stretch", - width="100%", - ), - top="auto", - right="auto", - height="100%", - width="20em", - padding="2em", - background_color=Color("mauve", 2), - outline="none", - ) - ), - direction="left", - ) - - -def modal(trigger) -> Component: - """A modal to create a new chat.""" - return Dialog.root( - Dialog.trigger(trigger), - Dialog.content( - Form( - HStack( - Input( - placeholder="Chat name", - name="session_name", - flex="1", - min_width="20ch", - ), - Button("Create chat"), - spacing="2", - wrap="wrap", - width="100%", - ), - on_submit=State.create_session, - ), - background_color=Color("mauve", 1), - ), - open=State.create_session_modal_is_open, - on_open_change=State.toggle_create_session_modal, - ) - - -def navbar(): - return HStack( - Badge( - State.current_session_name, - Tooltip( - Icon("info", size=14), - content="The current selected chat.", - ), - size="3", - variant="soft", - margin_inline_end="auto", - ), - modal( - Icon("message-square-plus"), - ), - sidebar( - Icon( - "messages-square", - background_color=Color("mauve", 6), - ) - ), - justify_content="space-between", - align_items="center", - padding="12px", - border_bottom=f"1px solid {Color('mauve', 3)}", - background_color=Color("mauve", 2), - ) diff --git a/产品需求文档AI生成/application/components/navigation_bar.py b/产品需求文档AI生成/application/components/navigation_bar.py new file mode 100644 index 0000000..9867829 --- /dev/null +++ b/产品需求文档AI生成/application/components/navigation_bar.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +""" +导航栏组件 +""" + +import reflex + +from ..state import State + + +def create_session_modal(trigger) -> reflex.Component: + """ + 创建会话模态窗组件 + """ + return reflex.dialog.root( + reflex.dialog.trigger(trigger), + reflex.dialog.content( + reflex.form( + reflex.hstack( + reflex.input( + name="session_name", + placeholder="会话名称", + flex="auto", + min_width="20ch", + ), + reflex.button("创建会话"), + spacing="2", + wrap="wrap", + width="100%", + ), + on_submit=State.create_session, + ), + background_color=reflex.color("mauve", 1), + ), + open=State.create_session_modal_is_open, + on_open_change=State.toggle_create_session_modal, + ) + + +def session_history_item(session_name: str) -> reflex.Component: + """ + 会话历史侧边栏中一次会话组件 + :param session_name: 会话名称 + :return: Component + """ + return reflex.drawer.close( + reflex.hstack( + reflex.button( + session_name, + on_click=lambda: State.switch_session( + session_name + ), # 点击按钮将切换会话 + width="80%", + variant="surface", + ), + reflex.button( + reflex.icon( + tag="trash", + on_click=lambda: State.delete_session( + session_name + ), # 点击按钮删除会话 + stroke_width=1, + ), + width="20%", + variant="surface", + color_scheme="red", + ), + width="100%", + ), + key=session_name, # 使用会话名称作为唯一标识(会话名称不可重复) + ) + + +def session_history(trigger) -> reflex.Component: + """ + 会话历史侧边栏组件 + """ + return reflex.drawer.root( + reflex.drawer.trigger(trigger), + reflex.drawer.overlay(), + reflex.drawer.portal( + reflex.drawer.content( + reflex.vstack( + reflex.heading("会话列表", color=reflex.color("mauve", 11)), + reflex.divider(), + reflex.foreach( + State.get_session_names, # 获取所有会话名称 + lambda session_name: session_history_item( + session_name=session_name + ), # 创建会话组件 + ), + align_items="stretch", + width="100%", + ), + top="auto", + right="auto", + height="100%", + width="20em", + padding="2em", + background_color=reflex.color("mauve", 2), + outline="none", + ) + ), + direction="left", + ) + + +def navigation_bar() -> reflex.Component: + """ + 导航栏组件 + """ + return reflex.hstack( + reflex.badge( + State.current_session_name, + size="3", + variant="soft", + margin_inline_end="auto", + ), + create_session_modal( + reflex.box( + reflex.tooltip(reflex.icon("message-square-plus"), content="创建会话") + ) + ), + session_history( + reflex.box( + reflex.tooltip( + reflex.icon("messages-square"), + content="会话历史", + ) + ) + ), + justify_content="space-between", + align_items="center", + padding="12px", + border_bottom=f"1px solid {reflex.color('mauve', 3)}", + background_color=reflex.color("mauve", 2), + ) diff --git a/产品需求文档AI生成/application/components/session.py b/产品需求文档AI生成/application/components/session.py index b41ca23..de8d5d6 100644 --- a/产品需求文档AI生成/application/components/session.py +++ b/产品需求文档AI生成/application/components/session.py @@ -2,61 +2,43 @@ """ 会话组件 """ - -from reflex import ( - Component, - auto_scroll as AutoScroll, - box as Box, - button as Button, - center as Center, - color as Color, - foreach as Foreach, - form as Form, - hstack as HStack, - icon as Icon, - input as Input, - logo as Logo, - markdown as Markdown, - text as Text, - tooltip as Tooltip, - vstack as VStack, -) +import reflex from reflex.constants.colors import ColorType from ..state import State, Turn -def message(text: str, color: ColorType) -> Component: +def message_bubble(message: str, color: ColorType) -> reflex.Component: """ - 消息组件 - :param text: 文本 + 对话组件中一个消息气泡组件 + :param message: 消息 :param color: 颜色 :return: Component """ - return Markdown( - text, - background_color=Color(color=color, shade=4), - color=Color(color=color, shade=12), + return reflex.markdown( + message, + color=reflex.color(color=color, shade=12), + background_color=reflex.color(color=color, shade=4), display="inline-block", padding_inline="1em", border_radius="8px", ) -def turn(turn: Turn) -> Component: +def turn(turn: Turn) -> reflex.Component: """ - 对话组件 + 会话话组件中一次对话组件 :param turn: 对话 :return: Component """ - return Box( - Box( - message(text=turn.input_message, color="mauve"), + return reflex.box( + reflex.box( + message_bubble(message=turn.input_message, color="mauve"), text_align="right", margin_bottom="8px", ), - Box( - message(text=turn.output_message, color="accent"), + reflex.box( + message_bubble(message=turn.output_message, color="accent"), text_align="left", margin_bottom="8px", ), @@ -65,58 +47,51 @@ def turn(turn: Turn) -> Component: ) -def session() -> Component: +def session_area() -> reflex.Component: """ - 会话组件 + 会话区域组件 :return: Component """ - return AutoScroll( - Foreach(State.get_turns, turn), + return reflex.auto_scroll( + reflex.foreach(State.get_current_session_turns, turn), flex="1", padding="8px", overflow_y="auto", ) -def action_bar() -> Component: +def input_bar() -> reflex.Component: """ - 输入发送栏组件 + 输入栏组件 """ - return Center( - VStack( - Form( - HStack( - Input( - Input.slot( - Tooltip( - Icon("info", size=18), - content="Enter a question to get a response.", - ) - ), - placeholder="Type something...", - id="input_message", - flex="1", + return reflex.center( + reflex.vstack( + reflex.form( + reflex.hstack( + reflex.input( + name="input_message", + placeholder="请输入...", + flex="auto", ), - Button( - "Send", - loading=State.current_session_processing, - disabled=State.current_session_processing, + reflex.button( + "发送", type="submit", + loading=State.get_current_session_status, # 正在处理中时按钮显示为 loading + disabled=State.get_current_session_status, # 正在处理中时按钮禁用 ), max_width="50em", margin="0 auto", align_items="center", ), - reset_on_submit=True, # 提交后清空输入框 on_submit=State.adapt_input_message, + reset_on_submit=True, # 提交后清空输入框 ), - Text( - "ReflexGPT may return factually incorrect or misleading responses. Use discretion.", + reflex.text( + "抹茶兔兔工作室", text_align="center", font_size=".75em", - color=Color("mauve", 10), + color=reflex.color("mauve", 10), ), - Logo(margin_block="-1em"), width="100%", padding_x="16px", align="stretch", @@ -127,8 +102,8 @@ def action_bar() -> Component: padding_y="16px", backdrop_filter="auto", backdrop_blur="lg", - border_top=f"1px solid {Color('mauve', 3)}", - background_color=Color("mauve", 2), + border_top=f"1px solid {reflex.color('mauve', 3)}", + background_color=reflex.color("mauve", 2), align="stretch", width="100%", ) diff --git a/产品需求文档AI生成/application/pages/__init__.py b/产品需求文档AI生成/application/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/产品需求文档AI生成/application/pages/index.py b/产品需求文档AI生成/application/pages/index.py new file mode 100644 index 0000000..482d95a --- /dev/null +++ b/产品需求文档AI生成/application/pages/index.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +""" +会话页面 +""" + +import reflex + +from ..components.navigation_bar import navigation_bar +from ..components.session import session_area, input_bar + + +def index() -> reflex.Component: + """根页面""" + return reflex.vstack( + navigation_bar(), # 导航栏 + session_area(), # 会话区域 + input_bar(), # 输入栏 + color=reflex.color(color="mauve", shade=12), + background_color=reflex.color(color="mauve", shade=1), + height="100dvh", + align_items="stretch", + spacing="0", + ) diff --git a/产品需求文档AI生成/application/state.py b/产品需求文档AI生成/application/state.py index cd3c420..82570fa 100644 --- a/产品需求文档AI生成/application/state.py +++ b/产品需求文档AI生成/application/state.py @@ -3,7 +3,8 @@ 应用状态管理模块 """ -from typing import Any, AsyncGenerator, Dict, List, Optional +from typing import Any, AsyncGenerator, Dict, List +from uuid import uuid4 from pydantic import BaseModel, Field import reflex @@ -13,18 +14,22 @@ from sys import path path.append((Path(__file__).resolve().parent.parent.parent.as_posix())) from utils.agent import Agent +# 所有会话绑定的智能体 +agents: Dict[str, Any] = {} + def retrieve_agent(state) -> Agent: """ 获取当前会话绑定的智能体 :return: 当前会话绑定的智能体 """ - - if state.current_session_name not in state._agents: - state._agents[state.current_session_name] = Agent( - instructions="You are a friendly chatbot named Reflex. Respond in markdown." + current_session_name = state.current_session_name + if current_session_name not in agents: + agents[current_session_name] = Agent( + session_id=state.sessions[current_session_name].id, + instructions="You are a friendly chatbot named Reflex. Respond in markdown.", ) - return state._agents[state.current_session_name] + return agents[current_session_name] class Turn(BaseModel): @@ -35,24 +40,22 @@ class Turn(BaseModel): class Session(BaseModel): - """会话数据模型,包含会绑定智能体和会话对话列表""" + """会话数据模型,包含会话唯一标识和会话对话列表""" + id: str = Field(default_factory=lambda: uuid4().hex, description="会话唯一标识") + is_processing: bool = Field(default=False, description="会话是否正在处理中") turns: List[Turn] = Field(default_factory=list, description="会话对话列表") +# Reflex.State 统一管理应用数据与功能状态,作为前后端交互枢纽,借助响应式特性实现页面自动更新 class State(reflex.State): """应用状态""" # 当前会话名称 - current_session_name: str = "Intro" - # 当前会话正在处理 - current_session_processing: bool = False + current_session_name: str = "NewSession" # 所有会话 - sessions: Dict[str, Session] = {"Intro": Session()} - - # 所有会话绑定的智能体(私有变量) - _agents: Dict[str, Any] = {} + sessions: Dict[str, Session] = {current_session_name: Session()} # 新建会话模态窗是否打开 create_session_modal_is_open: bool = False @@ -66,14 +69,60 @@ class State(reflex.State): return list(self.sessions) @reflex.event - def set_current_session(self, session_name: str) -> None: + def switch_session(self, session_name: str) -> None: """ - 将所选会话设置为当前会话 + 切换会话 :param session_name: 会话名称 :return: None """ self.current_session_name = session_name + @reflex.var + def get_current_session_status(self) -> bool: + """ + 获取当前会话状态 + :return: 当前会话状态,其中 True 表示正在处理中,False 表示处理完成 + """ + if self.current_session_name not in self.sessions: + return False + return self.sessions[self.current_session_name].is_processing + + @reflex.var + def get_current_session_turns(self) -> List[Turn]: + """ + 获取当前会话对话列表 + :return: 当前会话对话列表 + """ + if self.current_session_name not in self.sessions: + return [] + return self.sessions[self.current_session_name].turns + + @reflex.event + def create_session(self, form_data: Dict[str, Any]) -> None: + """ + 创建会话 + :param form_data: 创建会话表单数据 + :return: None + """ + session_name = form_data["session_name"].strip() + + # 若创建会话名称为空则默认使用"NewSession"作为会话名称 + if not session_name: + session_name = "NewSession" + + original_session_name = session_name + counter = 1 + # 若会话名称重复则在会话名称后面添加序号至不重复 + while session_name in self.sessions: + session_name = f"{original_session_name}({counter})" + counter += 1 + + self.current_session_name = session_name + self.sessions[session_name] = Session() + + # 关闭新建会话模态窗 + self.create_session_modal_is_open = False + @reflex.event def delete_session(self, session_name: str) -> None: """ @@ -88,59 +137,23 @@ class State(reflex.State): # 若删除会话后所有会话为空则默认创建空白会话 if not self.sessions: - self.sessions["Intro"] = Session() + self.sessions["NewSession"] = Session() - # 若删除会话后当前会话名称不存在则默认使用第一个会话名称 + # 删除会话后,若当前会话名称不存在则默认使用第一个会话名称 if self.current_session_name not in self.sessions: self.current_session_name = next(iter(self.sessions)) - @reflex.var - def get_turns(self) -> List[Turn]: - """ - 获取当前会话所有对话 - :return: 当前会话所有对话 - """ - if self.current_session_name not in self.sessions: - return ( - [] - ) # 因 reflex 实时响应状态变更(前端立刻自动刷新),故需要考虑当前会话不存在的情况 - return self.sessions[self.current_session_name].turns - @reflex.event def toggle_create_session_modal(self, is_open: bool) -> None: """ - 切换新建会话模态窗开关状态 + 打开 / 关闭新建会话模态窗 :param is_open: 打开或关闭新建会话模态窗 :return: None """ self.create_session_modal_is_open = is_open @reflex.event - def create_session(self, form_data: Dict[str, Any]) -> None: - """ - 创建会话 - :param form_data: 创建会话表单数据 - :return: None - """ - session_name = form_data["session_name"].strip() - - # 若创建会话名称为空字则默认使用"Intro"作为会话名称 - if not session_name: - session_name = "Intro" - - original_session_name = session_name - counter = 1 - # 若会话名称重复则在会话名称后面添加序号至不重复 - while session_name in self.sessions: - session_name = f"{original_session_name}_{counter}" - counter += 1 - - self.current_session_name = session_name - self.sessions[session_name] = Session() - self.create_session_modal_is_open = False # 关闭新建会话模态窗 - - @reflex.event - async def adapt_input_message(self, form_data: dict[str, str]) -> AsyncGenerator: + async def adapt_input_message(self, form_data: dict[str, Any]) -> AsyncGenerator: """ 适配用户输入消息 :param form_data: 对话表单数据 @@ -150,10 +163,10 @@ class State(reflex.State): if not input_message: return - async for value in self._process_input_message(input_message): + async for value in self.process_input_message(input_message=input_message): yield value - async def _process_input_message(self, input_message: str) -> AsyncGenerator: + async def process_input_message(self, input_message: str) -> AsyncGenerator: """ 处理用户输入消息 :param input_message: 用户输入的消息 @@ -169,7 +182,7 @@ class State(reflex.State): ) # 当前会话正在处理 - self.current_session_processing = True + current_session.is_processing = True yield agent = retrieve_agent(self) @@ -180,4 +193,4 @@ class State(reflex.State): yield # 当前会话处理完成 - self.current_session_processing = False + current_session.is_processing = False diff --git a/产品需求文档AI生成/rxconfig.py b/产品需求文档AI生成/rxconfig.py index 64e4ac0..7595216 100644 --- a/产品需求文档AI生成/rxconfig.py +++ b/产品需求文档AI生成/rxconfig.py @@ -1,5 +1,15 @@ -import reflex as rx +import reflex +from reflex.plugins import RadixThemesPlugin +from reflex_base.plugins.sitemap import SitemapPlugin -config = rx.Config( +config = reflex.Config( app_name="application", + disable_plugins=[SitemapPlugin], + plugins=[ + RadixThemesPlugin( + theme=reflex.theme( + appearance="dark", accent_color="purple" # 暗黑模式 # 主题色 + ) + ) + ], )