This commit is contained in:
parent
93bcc1f1b4
commit
3f8917c803
|
|
@ -114,7 +114,7 @@ def use_guidance() -> rx.Component:
|
|||
def user_prompt(item: MessageHistoryItem) -> rx.Component:
|
||||
"""
|
||||
用户提示词
|
||||
:return: Component
|
||||
:return: rx.Component
|
||||
"""
|
||||
return rx.hstack(
|
||||
rx.spacer(),
|
||||
|
|
@ -491,14 +491,80 @@ def message_history() -> rx.Component:
|
|||
消息历史
|
||||
:return: Component
|
||||
"""
|
||||
return rx.box(
|
||||
return rx.vstack(
|
||||
# 若消息历史为空则显示使用指引,否则渲染消息历史项
|
||||
rx.cond(
|
||||
TaskState.message_history.length() == 0,
|
||||
use_guidance(),
|
||||
rx.vstack(
|
||||
rx.hstack(
|
||||
rx.hstack(
|
||||
# 渲染任务描述
|
||||
rx.text(
|
||||
TaskState.task_description,
|
||||
line_height="32px",
|
||||
font_size="var(--prismui-font-size-3)",
|
||||
font_weight="var(--prismui-font-weight-3)",
|
||||
),
|
||||
rx.icon(
|
||||
"pencil-line",
|
||||
width="14px",
|
||||
height="14px",
|
||||
object_fit="contain",
|
||||
color="var(--prismui-color-2)",
|
||||
),
|
||||
align_items="center",
|
||||
gap="4px",
|
||||
height="56px",
|
||||
),
|
||||
rx.hstack(
|
||||
rx.text(
|
||||
TaskState.task_last_run_at,
|
||||
line_height="20px",
|
||||
font_size="var(--prismui-font-size-1)",
|
||||
color="var(--prismui-color-3)",
|
||||
),
|
||||
rx.box(
|
||||
rx.icon(
|
||||
"panel-right",
|
||||
width="16px",
|
||||
height="16px",
|
||||
object_fit="contain",
|
||||
color="var(--prismui-color-2)",
|
||||
),
|
||||
display="flex",
|
||||
align_items="center",
|
||||
justify_content="center",
|
||||
width="32px",
|
||||
height="32px",
|
||||
border_radius="var(--prismui-border-radius-3)",
|
||||
),
|
||||
rx.box(
|
||||
rx.icon(
|
||||
"ellipsis",
|
||||
width="16px",
|
||||
height="16px",
|
||||
object_fit="contain",
|
||||
color="var(--prismui-color-2)",
|
||||
),
|
||||
display="flex",
|
||||
align_items="center",
|
||||
justify_content="center",
|
||||
width="32px",
|
||||
height="32px",
|
||||
border_radius="var(--prismui-border-radius-3)",
|
||||
),
|
||||
align_items="center",
|
||||
gap="4px",
|
||||
),
|
||||
align_items="center",
|
||||
justify_content="space-between",
|
||||
width="100%",
|
||||
padding="0 12px",
|
||||
),
|
||||
rx.auto_scroll(
|
||||
rx.vstack(
|
||||
# 渲染消息历史项
|
||||
rx.foreach(TaskState.message_history, message_history_item),
|
||||
# 若等待流式输出则显示加载动效
|
||||
rx.cond(
|
||||
|
|
@ -533,188 +599,103 @@ def message_history() -> rx.Component:
|
|||
width="100%",
|
||||
max_width="1200px",
|
||||
margin_x="auto",
|
||||
padding_top="20px",
|
||||
justify_content="flex-start",
|
||||
overflow_x="hidden",
|
||||
overflow_y="auto",
|
||||
),
|
||||
),
|
||||
display="flex",
|
||||
flex_direction="column",
|
||||
width="100%",
|
||||
height="100%",
|
||||
flex="1",
|
||||
min_height="0",
|
||||
)
|
||||
|
||||
|
||||
def conversation_creating() -> rx.Component:
|
||||
def user_prompt_sending_box() -> rx.Component:
|
||||
"""
|
||||
创建会话
|
||||
:return: Component
|
||||
"""
|
||||
return rx.hstack(
|
||||
rx.el.style(
|
||||
"""
|
||||
.rt-TooltipArrow polygon {
|
||||
fill: var(--prismui-background-color-1) !important;
|
||||
}
|
||||
.rt-TooltipText {
|
||||
font-family: var(--prismui-font-family) !important;
|
||||
color: var(--prismui-color-1) !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
"""
|
||||
),
|
||||
rx.spacer(),
|
||||
rx.box(
|
||||
rx.tooltip(
|
||||
rx.icon(
|
||||
"plus",
|
||||
on_click=TaskState.create_task,
|
||||
width="14px",
|
||||
height="14px",
|
||||
style={
|
||||
"_hover": {
|
||||
"color": "var(--prismui-color-5)",
|
||||
}
|
||||
},
|
||||
),
|
||||
content="创建任务",
|
||||
side="top",
|
||||
side_offset=9,
|
||||
background_color="var(--prismui-background-color-1)",
|
||||
box_shadow="var(--prismui-box-shadow-3)",
|
||||
),
|
||||
display="flex",
|
||||
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",
|
||||
),
|
||||
display="flex",
|
||||
justify_content="flex-end",
|
||||
align_items="center",
|
||||
gap="4px",
|
||||
padding="0 12px",
|
||||
margin_bottom="8px",
|
||||
width="100%",
|
||||
max_width="1200px",
|
||||
height="39px",
|
||||
background="transparent",
|
||||
)
|
||||
|
||||
|
||||
def user_prompt_box() -> rx.Component:
|
||||
"""
|
||||
用户提示词框
|
||||
用户提示词发送框
|
||||
:return: rx.Component
|
||||
"""
|
||||
return rx.vstack(
|
||||
# 创建对话
|
||||
conversation_creating(),
|
||||
rx.vstack(
|
||||
rx.text_area(
|
||||
# 值绑定:用户提示词
|
||||
value=TaskState.user_prompt,
|
||||
# 输入事件:设置用户提示词
|
||||
on_change=TaskState.set_user_prompt,
|
||||
placeholder="请输入您的问题,按Enter换行",
|
||||
vertical_align="middle",
|
||||
width="100%",
|
||||
min_height="64px",
|
||||
max_height="224px",
|
||||
padding="4px 0",
|
||||
background_color="var(--prismui-background-color-1)",
|
||||
border="none !important",
|
||||
outline="none !important",
|
||||
box_shadow="none !important",
|
||||
style={
|
||||
".rt-TextAreaInput": {
|
||||
"border": "none !important",
|
||||
"outline": "none !important",
|
||||
"box-shadow": "none !important",
|
||||
"field-sizing": "content !important",
|
||||
"min-height": "64px",
|
||||
"max-height": "224px",
|
||||
"overflow-y": "auto",
|
||||
},
|
||||
},
|
||||
),
|
||||
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=TaskState.is_running,
|
||||
# 不可点击状态:绑定用户提示词发送按钮不可点击状态
|
||||
disabled=TaskState.is_user_prompt_sending_button_disabled,
|
||||
# 点击事件:运行
|
||||
on_click=lambda: TaskState.run(),
|
||||
display="inline-flex",
|
||||
position="relative",
|
||||
justify_content="center",
|
||||
align_items="center",
|
||||
padding="0 12px",
|
||||
background_color=rx.cond(
|
||||
TaskState.is_user_prompt_sending_button_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",
|
||||
cursor=rx.cond(
|
||||
TaskState.is_user_prompt_sending_button_disabled,
|
||||
"not-allowed",
|
||||
"pointer",
|
||||
),
|
||||
),
|
||||
justify_content="flex-end",
|
||||
align_items="center",
|
||||
height="32px",
|
||||
width="100%",
|
||||
),
|
||||
rx.text_area(
|
||||
# 值绑定:用户提示词
|
||||
value=TaskState.user_prompt,
|
||||
# 输入事件:设置用户提示词
|
||||
on_change=TaskState.set_user_prompt,
|
||||
placeholder="请输入您的问题,按Enter换行",
|
||||
vertical_align="middle",
|
||||
width="100%",
|
||||
padding="12px 16px",
|
||||
min_height="64px",
|
||||
max_height="224px",
|
||||
padding="4px 0",
|
||||
background_color="var(--prismui-background-color-1)",
|
||||
border_radius="var(--prismui-border-radius-6)",
|
||||
box_shadow="var(--prismui-box-shadow-9)",
|
||||
border="none !important",
|
||||
outline="none !important",
|
||||
box_shadow="none !important",
|
||||
style={
|
||||
".rt-TextAreaInput": {
|
||||
"border": "none !important",
|
||||
"outline": "none !important",
|
||||
"box-shadow": "none !important",
|
||||
"field-sizing": "content !important",
|
||||
"min-height": "64px",
|
||||
"max-height": "224px",
|
||||
"overflow-y": "auto",
|
||||
},
|
||||
},
|
||||
),
|
||||
# 免责声明
|
||||
rx.text(
|
||||
"内容由大模型生成,无法确保准确性和完整性,仅供参考",
|
||||
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=TaskState.is_running,
|
||||
# 不可点击状态:绑定用户提示词发送按钮不可点击状态
|
||||
disabled=TaskState.is_user_prompt_sending_button_disabled,
|
||||
# 点击事件:运行
|
||||
on_click=lambda: TaskState.run(),
|
||||
display="inline-flex",
|
||||
position="relative",
|
||||
justify_content="center",
|
||||
align_items="center",
|
||||
padding="0 12px",
|
||||
background_color=rx.cond(
|
||||
TaskState.is_user_prompt_sending_button_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",
|
||||
cursor=rx.cond(
|
||||
TaskState.is_user_prompt_sending_button_disabled,
|
||||
"not-allowed",
|
||||
"pointer",
|
||||
),
|
||||
),
|
||||
justify_content="flex-end",
|
||||
align_items="center",
|
||||
height="32px",
|
||||
width="100%",
|
||||
text_align="center",
|
||||
margin_top="8px",
|
||||
line_height="18px",
|
||||
font_size="var(--prismui-font-size-1)",
|
||||
color="var(--prismui-color-10)",
|
||||
),
|
||||
z_index="99",
|
||||
display="flex",
|
||||
flex_direction="column",
|
||||
width="100%",
|
||||
max_width="1200px",
|
||||
padding="0 12px 12px",
|
||||
margin_x="auto",
|
||||
border="none",
|
||||
padding="12px 16px",
|
||||
background_color="var(--prismui-background-color-1)",
|
||||
border_radius="var(--prismui-border-radius-6)",
|
||||
box_shadow="var(--prismui-box-shadow-9)",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -735,7 +716,28 @@ def task_page() -> rx.Component:
|
|||
width="100%",
|
||||
overflow_y="auto",
|
||||
),
|
||||
user_prompt_box(),
|
||||
rx.vstack(
|
||||
# 渲染用户提示词发送框
|
||||
user_prompt_sending_box(),
|
||||
# 渲染免责声明
|
||||
rx.text(
|
||||
"内容由大模型生成,无法确保准确性和完整性,仅供参考",
|
||||
width="100%",
|
||||
text_align="center",
|
||||
margin_top="8px",
|
||||
line_height="18px",
|
||||
font_size="var(--prismui-font-size-1)",
|
||||
color="var(--prismui-color-10)",
|
||||
),
|
||||
z_index="99",
|
||||
display="flex",
|
||||
flex_direction="column",
|
||||
width="100%",
|
||||
max_width="1200px",
|
||||
padding="0 12px 12px",
|
||||
margin_x="auto",
|
||||
border="none",
|
||||
),
|
||||
width="100%",
|
||||
height="100%",
|
||||
background="var(--prismui-background-2)",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import reflex as rx
|
||||
|
||||
from application.pages import task_page, knowledge_base_page, login_page
|
||||
from application.states import AuthState, TaskState
|
||||
from application.states import AuthState, TaskState, SidebarState
|
||||
from application.states.models import TaskHistoryItem
|
||||
|
||||
|
||||
|
|
@ -29,8 +29,8 @@ def nav_button(
|
|||
:return : Component
|
||||
"""
|
||||
|
||||
# 导航按钮选中状态
|
||||
is_selected = nav_button == AuthState.selected_nav_button
|
||||
# 选中状态
|
||||
is_selected = nav_button == SidebarState.nav_button
|
||||
|
||||
return rx.hstack(
|
||||
# 导航按钮的图标
|
||||
|
|
@ -48,7 +48,6 @@ def nav_button(
|
|||
font_weight=rx.cond(
|
||||
is_selected, "var(--prismui-font-weight-3)", None
|
||||
), # 通过加粗字体区分是否选中
|
||||
color="var(--prismui-color-1)",
|
||||
),
|
||||
align_items="center",
|
||||
gap="8px",
|
||||
|
|
@ -59,8 +58,8 @@ def nav_button(
|
|||
), # 通过背景颜色区分是否选中
|
||||
border_radius="var(--prismui-border-radius-3)",
|
||||
cursor="pointer",
|
||||
# 点击事件:设置选中导航按钮
|
||||
on_click=AuthState.set_selected_nav_button(nav_button),
|
||||
# 点击事件:将指定导航按钮设置为选中的导航按钮
|
||||
on_click=SidebarState.set_nav_button(nav_button),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -73,59 +72,55 @@ def task_history_item(
|
|||
:return: Component
|
||||
"""
|
||||
|
||||
# 悬停状态
|
||||
is_hovered = item.id == TaskState.hovered_task_id
|
||||
# 选中状态
|
||||
is_selected = item.id == TaskState.task_id
|
||||
|
||||
# 若悬停状态为是则渲染任务描述和更多图标,否则:若运行状态为是则渲染正在运行图标,否则不渲染;同时渲染任务描述和上一次运行日期时间
|
||||
return rx.list.item(
|
||||
return rx.hstack(
|
||||
rx.cond(
|
||||
is_hovered,
|
||||
rx.hstack(
|
||||
rx.text(
|
||||
item.description,
|
||||
line_height="20px",
|
||||
color="var(--prismui-color-2)",
|
||||
text_overflow="ellipsis",
|
||||
white_space="nowrap",
|
||||
),
|
||||
rx.icon("ellipsis"),
|
||||
width="100%",
|
||||
padding="5px 12px",
|
||||
),
|
||||
rx.hstack(
|
||||
rx.cond(
|
||||
item.is_running,
|
||||
rx.image("/is_running.svg"),
|
||||
rx.image("/is_running.svg"),
|
||||
),
|
||||
# 渲染任务描述
|
||||
rx.text(
|
||||
item.description,
|
||||
flex=1,
|
||||
min_width="0", # 自适应宽度
|
||||
line_height="20px",
|
||||
color="var(--prismui-color-2)",
|
||||
white_space="nowrap", # 禁止换行
|
||||
overflow="hidden",
|
||||
mask_image="linear-gradient(to right, white calc(100% - 24px), transparent 100%)", # 添加蒙版
|
||||
),
|
||||
# 渲染上一次运行日期时间
|
||||
rx.text(
|
||||
item.last_run_at,
|
||||
line_height="20px",
|
||||
font_size="calc(var(--prismui-font-size-1) - 1px)",
|
||||
color="var(--prismui-color-2)",
|
||||
flex_shrink=0, # 禁止缩放
|
||||
),
|
||||
gap="8px",
|
||||
width="100%",
|
||||
padding="5px 12px",
|
||||
item.is_running,
|
||||
rx.image(
|
||||
"/spinner.svg",
|
||||
width="16px",
|
||||
height="16px",
|
||||
object_fit="contain",
|
||||
animation="spin 1.6s linear infinite",
|
||||
),
|
||||
),
|
||||
# 渲染任务描述
|
||||
rx.text(
|
||||
item.description,
|
||||
font_weight=rx.cond(is_selected, "var(--prismui-font-weight-3)", None),
|
||||
flex=1,
|
||||
min_width="0", # 自适应宽度
|
||||
line_height="20px",
|
||||
white_space="nowrap", # 禁止换行
|
||||
overflow="hidden",
|
||||
mask_image="linear-gradient(to right, white calc(100% - 24px), transparent 100%)", # 添加蒙版
|
||||
),
|
||||
# 渲染上一次运行日期时间
|
||||
rx.text(
|
||||
item.last_run_at,
|
||||
line_height="20px",
|
||||
font_size="var(--prismui-font-size-1)",
|
||||
color="var(--prismui-color-3)",
|
||||
flex_shrink=0, # 禁止缩放
|
||||
),
|
||||
align_items="center",
|
||||
gap="8px",
|
||||
width="100%",
|
||||
padding="5px 12px",
|
||||
background_color=rx.cond(
|
||||
is_selected, "var(--prismui-background-color-2)", "transparent"
|
||||
), # 通过背景颜色区分是否选中
|
||||
border_radius="var(--prismui-border-radius-3)",
|
||||
cursor="pointer",
|
||||
# 点击事件:将指定任务唯一标识设置为当前任务唯一标识
|
||||
on_click=lambda: TaskState.set_task_id(item.id),
|
||||
on_click=SidebarState.set_task_id(item.id),
|
||||
style={
|
||||
"_hover": {
|
||||
"background": "var(--prismui-background-color-2)",
|
||||
}, # 鼠标悬停背景颜色
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -135,23 +130,20 @@ def task_history() -> rx.Component:
|
|||
:return: rx.Component
|
||||
"""
|
||||
return rx.auto_scroll(
|
||||
rx.list(
|
||||
rx.foreach(
|
||||
TaskState.task_history,
|
||||
task_history_item,
|
||||
),
|
||||
display="flex",
|
||||
flex_direction="column",
|
||||
gap="2px",
|
||||
width="100%",
|
||||
style={
|
||||
"&::-webkit-scrollbar": {
|
||||
"display": "none",
|
||||
},
|
||||
},
|
||||
overflow="auto",
|
||||
rx.foreach(
|
||||
TaskState.task_history,
|
||||
task_history_item,
|
||||
),
|
||||
display="flex",
|
||||
flex_direction="column",
|
||||
gap="2px",
|
||||
width="100%",
|
||||
overflow="auto",
|
||||
style={
|
||||
"&::-webkit-scrollbar": {
|
||||
"display": "none",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -170,7 +162,6 @@ def settings_button():
|
|||
stroke_width=1.5,
|
||||
width="20px",
|
||||
height="20px",
|
||||
color="var(--prismui-color-1)",
|
||||
),
|
||||
display="flex",
|
||||
flex_flow="column",
|
||||
|
|
@ -333,7 +324,7 @@ def workbench() -> rx.Component:
|
|||
sidebar(),
|
||||
# 根据选中的导航按钮渲染对应的页面
|
||||
rx.match(
|
||||
AuthState.selected_nav_button,
|
||||
SidebarState.nav_button,
|
||||
# 知识库页面
|
||||
("knowledge_base", knowledge_base_page()),
|
||||
# 任务页面
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from application.states.auth import AuthState
|
||||
from application.states.task import TaskState
|
||||
from application.states.database import DatabaseState
|
||||
from application.states.sidebar import SidebarState
|
||||
from application.states.task import TaskState
|
||||
|
||||
|
||||
__all__ = [
|
||||
"AuthState",
|
||||
"TaskState",
|
||||
"DatabaseState",
|
||||
"SidebarState",
|
||||
"TaskState",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@ class AuthState(rx.State):
|
|||
# 登录中状态
|
||||
is_logging_in: bool = False
|
||||
|
||||
# 选中的导航按钮,默认为创建任务
|
||||
selected_nav_button: str = "create_task"
|
||||
# 设置悬停卡片打开状态
|
||||
is_settings_hover_card_open: bool = False
|
||||
|
||||
|
|
@ -278,20 +276,6 @@ class AuthState(rx.State):
|
|||
"""
|
||||
self.is_settings_hover_card_open = is_settings_hover_card_open
|
||||
|
||||
@rx.event
|
||||
async def set_selected_nav_button(self, nav_button: str):
|
||||
"""
|
||||
设置选中导航按钮
|
||||
"""
|
||||
self.selected_nav_button = nav_button
|
||||
|
||||
# 若选中创建任务导航按钮则将空设置为当前任务唯一标识
|
||||
if nav_button == "create_task":
|
||||
# 获取任务状态
|
||||
task_state = await self.get_state(TaskState)
|
||||
# 将空设置为当前任务唯一标识
|
||||
task_state.set_task_id("")
|
||||
|
||||
@rx.event
|
||||
async def logout(self) -> None:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ from sqlmodel import Field, SQLModel, select, update
|
|||
|
||||
from application.states.models import (
|
||||
Approval,
|
||||
Task,
|
||||
Message,
|
||||
MessageType,
|
||||
Task,
|
||||
Workflow,
|
||||
approval_dump_json,
|
||||
approval_validate_json,
|
||||
|
|
@ -208,10 +208,13 @@ class DatabaseState(rx.State):
|
|||
await session.refresh(record)
|
||||
return record.id
|
||||
|
||||
async def create_task_record(self, user_id: str) -> dict[str, Task]:
|
||||
async def create_task_record(
|
||||
self, user_id: str, user_prompt: str = ""
|
||||
) -> dict[str, Task]:
|
||||
"""
|
||||
创建任务记录
|
||||
:param user_id: 用户唯一标识
|
||||
:param user_prompt: 用户提示词,默认为空
|
||||
:return: 任务实例
|
||||
"""
|
||||
async with rx.asession() as session:
|
||||
|
|
@ -223,6 +226,7 @@ class DatabaseState(rx.State):
|
|||
record.id: Task(
|
||||
id=record.id,
|
||||
description=record.description,
|
||||
user_prompt=user_prompt,
|
||||
usage=usage_validate_json(
|
||||
record.usage
|
||||
), # 将 record.usage 由 JSON 字符串反序列化为 RunUsage
|
||||
|
|
@ -377,7 +381,6 @@ class DatabaseState(rx.State):
|
|||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
async def update_message_record(
|
||||
self,
|
||||
message_id: str,
|
||||
|
|
|
|||
|
|
@ -85,10 +85,10 @@ class Task(BaseModel):
|
|||
id: str = Field(..., description="任务唯一标识")
|
||||
description: str = Field(..., description="任务描述")
|
||||
last_run_at: datetime = Field(..., description="上一次运行日期时间")
|
||||
user_prompt: str = Field(default="", description="用户提示词")
|
||||
usage: RunUsage = Field(..., description="任务使用量")
|
||||
messages: dict[str, Message] = Field(default_factory=dict, description="消息字典")
|
||||
workflow: Workflow = Field(default_factory=Workflow, description="工作,默认为对话")
|
||||
user_prompt: str = Field(default="", description="用户提示词")
|
||||
is_running: bool = Field(
|
||||
default=False, description="正在运行,True 表示正在运行, False 表示运行结束"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
侧边栏状态
|
||||
"""
|
||||
import reflex as rx
|
||||
|
||||
from application.states.task import TaskState
|
||||
|
||||
|
||||
class SidebarState(rx.State):
|
||||
"""
|
||||
侧边栏状态
|
||||
"""
|
||||
|
||||
# 选中的导航按钮,默认为创建任务
|
||||
nav_button: str = "create_task"
|
||||
|
||||
@rx.event
|
||||
async def set_nav_button(self, nav_button: str):
|
||||
"""
|
||||
将指定导航按钮设置为选中的导航按钮
|
||||
"""
|
||||
self.nav_button = nav_button
|
||||
|
||||
# 若选中创建任务导航按钮则将空设置为当前任务唯一标识
|
||||
if nav_button == "create_task":
|
||||
# 获取任务状态
|
||||
task_state = await self.get_state(TaskState)
|
||||
# 将空设置为当前任务唯一标识
|
||||
task_state.set_task_id("")
|
||||
|
||||
@rx.event
|
||||
async def set_task_id(self, task_id: str):
|
||||
"""
|
||||
将指定任务唯一标识设置为当前选中任务
|
||||
"""
|
||||
self.nav_button = ""
|
||||
|
||||
# 获取任务状态
|
||||
task_state = await self.get_state(TaskState)
|
||||
# 将指定任务唯一标识设置为当前任务唯一标识
|
||||
task_state.set_task_id(task_id)
|
||||
|
|
@ -48,8 +48,8 @@ class TaskState(rx.State):
|
|||
# 当前任务唯一标识
|
||||
task_id: str = ""
|
||||
|
||||
# 鼠标悬停的任务唯一标识
|
||||
hovered_task_id: str = ""
|
||||
# 临时用户提示词
|
||||
temporary_user_prompt: str = ""
|
||||
|
||||
# 当前数据库状态(私有变量)
|
||||
# 私有变量:reflex 约定以 _ 开头的变量为私有变量,后端不序列化,前端不可使用
|
||||
|
|
@ -71,7 +71,7 @@ class TaskState(rx.State):
|
|||
:param user_id: 用户唯一标识
|
||||
:return: None
|
||||
"""
|
||||
self.user_id = user_id.strip()
|
||||
self.user_id = user_id
|
||||
if not self.user_id:
|
||||
return
|
||||
|
||||
|
|
@ -79,24 +79,19 @@ class TaskState(rx.State):
|
|||
db_state = await self.get_db_state()
|
||||
# 查询当前用户的任务字典
|
||||
self.tasks = await db_state.read_tasks(self.user_id)
|
||||
# 若当前用户的任务字典为空则先创建任务记录再添加任务
|
||||
if not self.tasks:
|
||||
self.tasks.update(await db_state.create_task_record(self.user_id))
|
||||
# 将最后一个任务的唯一标识作为当前任务唯一标识
|
||||
self.task_id = next(reversed(self.tasks.keys()))
|
||||
|
||||
# 当前任务
|
||||
task = self.tasks[self.task_id]
|
||||
# 最后一个消息
|
||||
message = next(iter(reversed(task.messages.values())), None)
|
||||
# 若最后一个消息包含审批且审批结果为 None 则任务正在运行设置为是、消息正在运行设置为是
|
||||
if (
|
||||
message is not None
|
||||
and isinstance(approval := message.approval, Approval)
|
||||
and approval.result is None
|
||||
):
|
||||
task.is_running = True
|
||||
message.is_running = True
|
||||
# 遍历所有任务若最后一条消息未审批则将该任务和该消息正在运行设置为是
|
||||
for task in self.tasks.values():
|
||||
# 最后一个消息
|
||||
message = next(iter(reversed(task.messages.values())), None)
|
||||
# 若最后一个消息包含审批且审批结果为 None 则任务正在运行设置为是、消息正在运行设置为是
|
||||
if (
|
||||
message
|
||||
and isinstance(approval := message.approval, Approval)
|
||||
and approval.result is None
|
||||
):
|
||||
task.is_running = True
|
||||
message.is_running = True
|
||||
|
||||
@rx.var
|
||||
def task_history(self) -> list[TaskHistoryItem]:
|
||||
|
|
@ -105,16 +100,16 @@ class TaskState(rx.State):
|
|||
:return: 任务历史项列表
|
||||
"""
|
||||
|
||||
def formatter(datetime: datetime) -> str:
|
||||
def formatter(dt: datetime) -> str:
|
||||
"""
|
||||
格式器
|
||||
:param datetime: 日期时间
|
||||
:param dt: 日期时间
|
||||
:return: 格式化后日期时间字符串
|
||||
"""
|
||||
# 时间间隔
|
||||
delta = (datetime.now().date() - datetime.date()).total_seconds()
|
||||
delta = (datetime.now() - dt).total_seconds()
|
||||
if delta < 60:
|
||||
return f"刚刚"
|
||||
return "刚刚"
|
||||
elif delta < 60 * 60:
|
||||
return f"{round(delta / 60)}分钟前"
|
||||
elif delta < 60 * 60 * 24:
|
||||
|
|
@ -132,13 +127,29 @@ class TaskState(rx.State):
|
|||
for task in reversed(self.tasks.values())
|
||||
]
|
||||
|
||||
@rx.event
|
||||
def set_task_history_item_popover_shown(self, item_id: str, is_shown: bool) -> None:
|
||||
@rx.var
|
||||
def task_description(self) -> str:
|
||||
"""
|
||||
设置会话历史项悬停气泡显示 / 隐藏
|
||||
:return: None
|
||||
获取当前任务的描述
|
||||
:return: 当前任务的描述
|
||||
"""
|
||||
self.popover_shown_conversation_history_item_id = item_id if is_shown else ""
|
||||
# 当前任务
|
||||
task = self.tasks.get(self.task_id)
|
||||
if not task:
|
||||
return ""
|
||||
return task.description
|
||||
|
||||
@rx.var
|
||||
def task_last_run_at(self) -> str:
|
||||
"""
|
||||
获取当前任务的上一次运行日期时间
|
||||
:return: 当前任务的上一次运行日期时间
|
||||
"""
|
||||
# 当前任务
|
||||
task = self.tasks.get(self.task_id)
|
||||
if not task:
|
||||
return ""
|
||||
return f"更新于 {task.last_run_at.strftime("%Y-%m-%d %H:%M:%S")}"
|
||||
|
||||
@rx.event
|
||||
async def delete_task(self, task_id: str) -> None:
|
||||
|
|
@ -192,19 +203,6 @@ class TaskState(rx.State):
|
|||
)
|
||||
return message_history
|
||||
|
||||
@rx.event
|
||||
async def create_task(self) -> None:
|
||||
"""
|
||||
创建任务
|
||||
:return: None
|
||||
"""
|
||||
# 获取当前数据库状态
|
||||
db_state = await self.get_db_state()
|
||||
# 创建任务记录再添加任务
|
||||
self.tasks.update(await db_state.create_task_record(self.user_id))
|
||||
# 将最后一个任务的唯一标识作为当前任务唯一标识
|
||||
self.task_id = next(reversed(self.tasks.keys()))
|
||||
|
||||
@rx.var
|
||||
def user_prompt(self) -> str:
|
||||
"""
|
||||
|
|
@ -213,9 +211,11 @@ class TaskState(rx.State):
|
|||
"""
|
||||
# 当前任务
|
||||
task = self.tasks.get(self.task_id)
|
||||
if not task:
|
||||
return ""
|
||||
return task.user_prompt
|
||||
# 若当前任务存在则返回当前任务的用户提示词,否则返回临时用户提示词
|
||||
if task:
|
||||
return task.user_prompt
|
||||
else:
|
||||
return self.temporary_user_prompt
|
||||
|
||||
@rx.event
|
||||
def set_user_prompt(self, user_prompt: str) -> None:
|
||||
|
|
@ -226,13 +226,11 @@ class TaskState(rx.State):
|
|||
"""
|
||||
# 当前任务
|
||||
task = self.tasks.get(self.task_id)
|
||||
if not task:
|
||||
return
|
||||
|
||||
if not user_prompt.strip():
|
||||
return
|
||||
|
||||
task.user_prompt = user_prompt
|
||||
# 若当前任务存在则将用户提示词设置为当前任务的用户提示词,否则将用户提示词设置为临时用户提示词
|
||||
if task:
|
||||
task.user_prompt = user_prompt
|
||||
else:
|
||||
self.temporary_user_prompt = user_prompt
|
||||
|
||||
@rx.var
|
||||
def is_user_prompt_sending_button_disabled(self) -> bool:
|
||||
|
|
@ -242,9 +240,11 @@ class TaskState(rx.State):
|
|||
"""
|
||||
# 当前任务
|
||||
task = self.tasks.get(self.task_id)
|
||||
if not task:
|
||||
return True
|
||||
return task.is_running or not task.user_prompt
|
||||
# 若当前任务存在则判断该任务是否正在运行或用户提示词非空
|
||||
if task:
|
||||
return task.is_running or not task.user_prompt
|
||||
else:
|
||||
return not self.temporary_user_prompt
|
||||
|
||||
@rx.event
|
||||
async def run(
|
||||
|
|
@ -258,14 +258,30 @@ class TaskState(rx.State):
|
|||
:param message_id: 消息唯一标识
|
||||
:return: AsyncGenerator
|
||||
"""
|
||||
# 当前任务
|
||||
task = self.tasks.get(self.task_id)
|
||||
if not task:
|
||||
return
|
||||
from application.states import SidebarState
|
||||
|
||||
# 获取当前数据库状态
|
||||
db_state = await self.get_db_state()
|
||||
|
||||
# 若当前任务唯一标识为空则创建任务
|
||||
if not self.task_id:
|
||||
# 获取当前侧边栏状态
|
||||
sidebar_state = await self.get_state(SidebarState)
|
||||
# 将空设置为导航按钮
|
||||
sidebar_state.nav_button = ""
|
||||
|
||||
# 创建任务记录再添加任务
|
||||
self.tasks.update(
|
||||
await db_state.create_task_record(
|
||||
self.user_id, self.temporary_user_prompt
|
||||
)
|
||||
)
|
||||
# 将最后一个任务的唯一标识作为当前任务唯一标识
|
||||
self.task_id = next(reversed(self.tasks.keys()))
|
||||
|
||||
# 当前任务
|
||||
task = self.tasks[self.task_id]
|
||||
|
||||
user_prompt = None # 用户提示词
|
||||
deferred_tool_results = None # 延迟工具结果
|
||||
# 若为恢复运行
|
||||
|
|
@ -298,11 +314,11 @@ class TaskState(rx.State):
|
|||
|
||||
else:
|
||||
# 若用户提示词为空则直接返回
|
||||
if not (user_prompt := task.user_prompt):
|
||||
if not (user_prompt := task.user_prompt.strip()): # 去除用户提示词首尾空格
|
||||
return
|
||||
|
||||
# 清空用户提示词
|
||||
task.user_prompt = ""
|
||||
self.temporary_user_prompt = task.user_prompt = ""
|
||||
# 将正在运行设置为是
|
||||
task.is_running = True
|
||||
# 将等待流式输出设置为是
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="paint_linear_0" x1="20.4101181" x2="13.4623451" y1="22.4444809" y2="9.22279358" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="rgb(195.886,115.909,255)" offset="0" stop-opacity="1" />
|
||||
<stop stop-color="rgb(120.545,222.545,255)" offset="0.91785866" stop-opacity="1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint_linear_1" x1="21.1874924" x2="19.1952991" y1="7.64146042" y2="4.28173828" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="rgb(195.886,115.909,255)" offset="0" stop-opacity="1" />
|
||||
<stop stop-color="rgb(120.545,222.545,255)" offset="0.91785866" stop-opacity="1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint_linear_2" x1="16.3122597" x2="5.95285082" y1="20.5696716" y2="3.09911919" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="rgb(195.886,115.909,255)" offset="0" stop-opacity="1" />
|
||||
<stop stop-color="rgb(120.545,222.545,255)" offset="0.91785866" stop-opacity="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect id="对话" width="24" height="24" x="0" y="0" />
|
||||
<g id="组合 2745">
|
||||
<path id="矢量 652" d="M11.1465 19.6708C12.2203 20.4478 13.5424 20.9059 14.9688 20.9059C15.8547 20.9059 16.699 20.7296 17.469 20.4101C18.2668 20.0796 20.9063 20.3121 20.9063 20.3121C20.9063 20.3121 20.6743 17.6733 21.006 16.8749C21.3237 16.1048 21.5 15.26 21.5 14.3746C21.5 12.2814 20.5166 10.4178 18.9835 9.22279C19.0763 9.73827 19.125 10.27 19.125 10.8121C19.125 15.4177 15.6298 19.207 11.1465 19.6708Z" fill="url(#paint_linear_0)" fill-rule="evenodd" />
|
||||
<path id="路径 33备份" d="M20.0156 4.28174L19.6034 5.34381L18.5313 5.77253L19.6034 6.18613L20.0156 7.25049L20.4007 6.18613L21.5 5.77253L20.4007 5.34381L20.0156 4.28174Z" fill="url(#paint_linear_1)" fill-rule="evenodd" />
|
||||
<path id="减去顶层" d="M17.9373 10.8179C17.9373 6.55492 14.4815 3.09912 10.2186 3.09912C5.95562 3.09912 2.49981 6.55492 2.49981 10.8179C2.49981 11.8645 2.70811 12.8624 3.0855 13.7725C3.47671 14.7159 3.20152 17.8349 3.20152 17.8349C3.20152 17.8349 6.32055 17.5597 7.26394 17.9509C8.17404 18.3283 9.17198 18.5366 10.2186 18.5366C14.4815 18.5366 17.9373 15.0808 17.9373 10.8179ZM8.80933 8.78985C9.43966 8.78985 9.95063 9.30087 9.95063 9.93115C9.95063 10.5614 9.43966 11.0725 8.80933 11.0725C8.179 11.0725 7.66803 10.5614 7.66803 9.93115C7.66803 9.30087 8.179 8.78985 8.80933 8.78985ZM13.5267 8.78985C14.1571 8.78985 14.668 9.30087 14.668 9.93115C14.668 10.5614 14.1571 11.0725 13.5267 11.0725C12.8964 11.0725 12.3854 10.5614 12.3854 9.93115C12.3854 9.30087 12.8964 8.78985 13.5267 8.78985Z" fill="url(#paint_linear_2)" fill-rule="evenodd" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v4"></path><path d="m16.2 7.8 2.9-2.9"></path><path d="M18 12h4"></path><path d="m16.2 16.2 2.9 2.9"></path><path d="M12 18v4"></path><path d="m4.9 19.1 2.9-2.9"></path><path d="M2 12h4"></path><path d="m4.9 4.9 2.9 2.9"></path></svg>
|
||||
|
After Width: | Height: | Size: 429 B |
|
|
@ -26,7 +26,7 @@
|
|||
--prismui-color-1: #252b3a;
|
||||
--prismui-color-2: #575d6c;
|
||||
--prismui-color-3: #babbc0;
|
||||
--prismui-color-4: #060a263d;
|
||||
--prismui-color-4: rgba(37, 43, 58, 0.5);
|
||||
--prismui-color-5: #5e7ce0;
|
||||
--prismui-color-6: #f66f6a;
|
||||
--prismui-color-7: #f2f2f3;
|
||||
|
|
@ -107,7 +107,13 @@ pre, pre code {
|
|||
color: transparent !important;
|
||||
animation: shimmer 2.7s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in New Issue