diff --git a/agent/alembic/versions/6cd382f48060_.py b/agent/alembic/versions/51f049b860bb_.py similarity index 96% rename from agent/alembic/versions/6cd382f48060_.py rename to agent/alembic/versions/51f049b860bb_.py index 126ebd7..0c62370 100644 --- a/agent/alembic/versions/6cd382f48060_.py +++ b/agent/alembic/versions/51f049b860bb_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 6cd382f48060 +Revision ID: 51f049b860bb Revises: -Create Date: 2026-07-13 17:24:40.090382 +Create Date: 2026-07-13 22:23:49.155469 """ from typing import Sequence, Union @@ -12,7 +12,7 @@ import sqlalchemy as sa import sqlmodel # revision identifiers, used by Alembic. -revision: str = '6cd382f48060' +revision: str = '51f049b860bb' down_revision: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None diff --git a/agent/application/application.py b/agent/application/application.py index 7d9e083..8f8794f 100644 --- a/agent/application/application.py +++ b/agent/application/application.py @@ -20,7 +20,9 @@ app = rx.App( "*::before, *::after": { "box_sizing": "border-box", }, - "--prismui-background-color": "#ffffff", + "--prismui-background-color-1": "#ffffff", + "--prismui-background-color-2": "rgb(235, 235, 235)", + "--prismui-background-color-3": "#f6f6f8", "--mc-float-block-shadow": "rgba(213, 213, 213, 0.25)", "--devui-global-bg": "#f6f6f8", "--mc-global-bg": "linear-gradient(to bottom, #D0C9FF 0%, #E6D6F0 8%, #F1DBEA 12%, #C8DCFB 40%, #ABC6F6 60%, #87AEFE 90%)", @@ -32,10 +34,18 @@ app = rx.App( "--prismui-border-radius": "8px", "--devui-border-radius-full": "100px", "--font-family": "HuaweiFont,Helvetica,Arial,PingFangSC-Regular,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Microsoft JhengHei", + "--prismui-font-size": "14px", + "--prismui-font-size-1": "14px", + "--prismui-font-size-2": "12px", "--prismui-font-size-sm": "12px", + "--prismui-font-weight-1": "700", "--devui-font-size": "14px", "--devui-font-size-lg": "14px", "--prismui-color-text": "#252b3a", + "--prismui-color-text-light": "#babbc0", + "--prismui-color-1": "#252b3a", + "--prismui-color-2": "#575d6c", + "--prismui-color-3": "#babbc0", "--devui-text-weak": "#575d6c", "--devui-light-text": "#ffffff", "--placeholder": "#babbc0", diff --git a/agent/application/components/typography.py b/agent/application/components/typography.py new file mode 100644 index 0000000..0a4cf1d --- /dev/null +++ b/agent/application/components/typography.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +""" +排版相关组件 +""" +import reflex as rx diff --git a/agent/application/pages/conversation.py b/agent/application/pages/conversation.py index 96a2698..95fb7f2 100644 --- a/agent/application/pages/conversation.py +++ b/agent/application/pages/conversation.py @@ -23,7 +23,7 @@ def render_conversation_history_item( """ # 会话激活状态 - is_conversation_actived = conversation_id == ConversationState.conversation_id + is_actived = conversation_id == ConversationState.conversation_id return rx.box( rx.hstack( @@ -105,10 +105,10 @@ def render_conversation_history_item( cursor="pointer", style={ "opacity": rx.cond( - is_conversation_actived, "1", "0" + is_actived, "1", "0" ), # 若当前会话已激活则不透明,否则透明 "pointer_events": rx.cond( - is_conversation_actived, "auto", "none" + is_actived, "auto", "none" ), # 若当前会话已激活则可点击,否则不可点击 "transition": "opacity 0.18s ease", }, @@ -127,13 +127,11 @@ def render_conversation_history_item( cursor="pointer", style={ "background": rx.cond( - is_conversation_actived, + is_actived, "linear-gradient(to right, #f3efff, #f3efff33, #e2f1fd33, #e2f1fd)", "var(--devui-base-bg)", ), - "box_shadow": rx.cond( - is_conversation_actived, "2px 2px 8px #e9e9e9", "none" - ), + "box_shadow": rx.cond(is_actived, "2px 2px 8px #e9e9e9", "none"), "&:hover": { "background": "linear-gradient(to right, #f3efff, #f3efff33, #e2f1fd33, #e2f1fd)", "box_shadow": "2px 2px 8px #e9e9e9", @@ -313,28 +311,33 @@ def render_greeting() -> rx.Component: ) -def render_reasoning_thought_node(thought_node_id: int, thought_node: ThoughtNode): +def render_thought_node(thought_node_id: int, thought_node: ThoughtNode): """ 渲染思考节点 :param thought_node_id: 思考节点唯一标识 :param thought_node: 思考节点实例 :return: Component """ - return rx.hstack( - rx.vstack( - rx.box( - width="8px", - height="8px", - background_color=rx.color("blue", 7), + return rx.vstack( + rx.match( + thought_node.kind, + ( + "thinking", + rx.markdown( + thought_node.content, + component_map={ + "p": lambda text: rx.text( + text, + color="var(--prismui-color-text-light)", + margin="0 0 8px", + font_size="var(--prismui-font-size-sm)", + ) + }, + ), ), - rx.box(width="2px", flex=1, background_color=rx.color("blue", 3)), - align_items="center", - spacing="0", - height="100%", + rx.fragment(), ), - rx.markdown(thought_node.content, color=rx.color("gray", 11), padding_y="4px"), align_items="flex-start", - spacing="2", width="100%", key=thought_node_id, ) @@ -343,51 +346,51 @@ def render_reasoning_thought_node(thought_node_id: int, thought_node: ThoughtNod def render_dialog_history_item(dialog_id: str, dialog: Dialog) -> rx.Component: """ 渲染对话历史条目 - :param run: 对话实例 + :param dialog_id: 对话唯一标识 + :param dialog: 对话实例 :return: Component """ # 推理状态 is_thinking = dialog.is_thinking + # 思考折叠面板展开状态 + is_collapse_expanded = dialog.is_collapse_expanded # 思考节点 thought_nodes = dialog.thought_nodes - # 推理折叠面板展开状态 - is_reasoning_panel_open = dialog.is_collapse_expanded - return rx.vstack( - # 渲染问题和再次发送按钮 + # 渲染问题和操作栏 rx.hstack( rx.spacer(), rx.vstack( # 渲染问题 - rx.markdown( + rx.text( dialog.question, max_width="600px", padding="12px 16px", - background_color="var(--devui-global-bg)", + background_color="var(--prismui-background-color-3)", border_radius="12px", word_wrap="break-word", word_break="break-all", + white_space="pre-line", color="var(--prismui-color-text)", - style={ - "& p.rt-Text": { - "padding": "0", - "margin": "0 !important", - }, - }, ), - # 再次发送按钮 + # 操作栏 rx.hstack( rx.box( rx.icon( "rotate-ccw", width="14px", height="14px", - color="var(--devui-text-weak)", + color="var(--prismui-color-2)", ), padding="4px", border_radius="4px", cursor="pointer", + style={ + "_hover": { + "background_color": "var(--prismui-background-color-2)", + } + }, ), width="100%", margin_top="8px", @@ -399,32 +402,72 @@ def render_dialog_history_item(dialog_id: str, dialog: Dialog) -> rx.Component: gap="4px", font_size="var(--devui-font-size)", ), - # 渲染智能体思考链面板和回答 + # 渲染思考折叠面板 rx.vstack( - - rx.auto_scroll( - rx.foreach( - thought_nodes, - lambda i, _: render_reasoning_thought_node(i[0], i[1]), + # 标题栏 + rx.box( + rx.hstack( + rx.text( + rx.cond(is_thinking, "思考中", "思考完成"), + color="var(--prismui-color-3)", + font_size="var(--prismui-font-size-2)", + ), + rx.icon( + "chevron-right", + width="14px", + height="14px", + color="var(--prismui-color-3)", + style={ + "transition": "transform 0.2s ease-in-out", + "transform": rx.cond( + is_collapse_expanded, "rotate(90deg)", "rotate(0deg)" + ), + }, + ), + align_items="center", + gap="4px", + margin_bottom="8px", + line_height="22px", ), - width="100%", - padding="0 12px", + cursor="pointer", + # 点击事件,展开/折叠思考折叠面板 + on_click=lambda: ConversationState.toggle_collapse(dialog_id), ), - # 渲染回答 - rx.markdown( - dialog.answer, - display="inline-block", - max_width="85%", - padding_x="1.25em", - padding_y="0.5em", - margin_left="auto", - margin_bottom="8px", - background_color=rx.color("gray", 2), - border_radius="12px", - color="var(--prismui-color-text)", + rx.cond( + is_collapse_expanded, + rx.auto_scroll( + rx.foreach( + thought_nodes, + lambda i, _: render_thought_node(i[0], i[1]), + ), + width="100%", + ), + rx.fragment(), ), text_align="left", width="100%", + margin_top="8px", + ), + # 渲染回答 + rx.markdown( + dialog.answer, + component_map={ + "p": lambda text: rx.text( + text, + 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)", + ), + }, + width="100%", margin_bottom="8px", ), width="100%", @@ -470,7 +513,7 @@ def render_dialog_history() -> rx.Component: def render_create_conversation_button() -> rx.Component: """ - 渲染新建对话按钮 + 渲染新建会话按钮 :return: Component """ return rx.hstack( @@ -501,7 +544,7 @@ def render_create_conversation_button() -> rx.Component: } }, ), - content="新建对话", + content="新建会话", background_color="#ffffff", box_shadow="0 2px 12px 0 rgba(37, 43, 58, .24)", color="#252b3a", @@ -524,11 +567,11 @@ def render_create_conversation_button() -> rx.Component: rx.hstack( rx.input( name="description", - placeholder="请输入对话描述", + placeholder="请输入会话描述", flex="auto", min_width="20ch", ), - rx.button("创建"), + rx.button("新建"), spacing="2", wrap="wrap", width="100%", @@ -711,7 +754,7 @@ def render_conversation_history_collapse_button( z_index=99, width="16px", height="40px", - background="var(--prismui-background-color)", + background="var(--prismui-background-color-1)", box_shadow="2px 0 4px 0 var(--mc-float-block-shadow)", border_radius=rx.cond(is_collapse_expanded, "6px", "0 6px 6px 0"), transition="all 0.3s ease-in-out", diff --git a/agent/application/states/conversation.py b/agent/application/states/conversation.py index f38467f..62913c9 100644 --- a/agent/application/states/conversation.py +++ b/agent/application/states/conversation.py @@ -395,9 +395,9 @@ class ConversationState(rx.State): yield @rx.event - def toggle_reasoning_panel(self, dialog_id: str) -> None: + def toggle_collapse(self, dialog_id: str) -> None: """ - 展开/折叠指定运行唯一标识的推理面板 + 展开/折叠思考折叠面板 """ # 指定运行 dialog = self.conversations[self.conversation_id].dialogs[dialog_id] diff --git a/agent/database.db b/agent/database.db new file mode 100644 index 0000000..3b965b3 Binary files /dev/null and b/agent/database.db differ