This commit is contained in:
liubiren 2026-09-04 20:15:17 +08:00
parent 5abfc83d4c
commit 714675cf5b
6 changed files with 73 additions and 19 deletions

View File

@ -396,7 +396,7 @@ def thinking(item: MessageHistoryItem) -> rx.Component:
rx.box(
rx.cond(
item.is_shown,
rx.text(
rx.markdown(
item.content,
width="100%",
margin="4px 0",
@ -418,6 +418,43 @@ def thinking(item: MessageHistoryItem) -> rx.Component:
)
def tool_call(item: MessageHistoryItem) -> rx.Component:
"""
工具调用
:return: Component
"""
return rx.vstack(
# 工具调用的标题栏
rx.hstack(
rx.icon(
"square-function",
width="14px",
height="14px",
color="var(--prismui-color-3)",
),
rx.text(
item.title,
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-3)",
class_name=rx.cond(item.is_running, "is_running", None),
),
align_items="center",
gap="4px",
margin_bottom="8px",
line_height="22px",
),
# 工具调用的内容
rx.markdown(
item.content,
width="100%",
margin="4px 0",
line_height="1.6",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-3)",
),
)
def output(item: MessageHistoryItem) -> rx.Component:
"""
输出
@ -432,7 +469,6 @@ def output(item: MessageHistoryItem) -> rx.Component:
line_height="1.75",
word_wrap="break-word",
word_break="break-all",
white_space="pre-line",
),
"strong": lambda text: rx.text(
text,
@ -454,7 +490,6 @@ def output(item: MessageHistoryItem) -> rx.Component:
font_weight="var(--prismui-font-weight-3)",
word_wrap="break-word",
word_break="break-all",
white_space="pre-line",
),
"h2": lambda text: rx.text(
text,
@ -464,7 +499,6 @@ def output(item: MessageHistoryItem) -> rx.Component:
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,
@ -473,7 +507,6 @@ def output(item: MessageHistoryItem) -> rx.Component:
font_weight="var(--prismui-font-weight-3)",
word_wrap="break-word",
word_break="break-all",
white_space="pre-line",
),
"ul": lambda children: rx.vstack(
children,
@ -500,7 +533,6 @@ def output(item: MessageHistoryItem) -> rx.Component:
line_height="1.75",
word_wrap="break-word",
word_break="break-all",
white_space="pre-line",
),
align_items="center",
justify_content="flex-start",
@ -535,6 +567,7 @@ def output(item: MessageHistoryItem) -> rx.Component:
),
},
width="100%",
margin_bottom="8px",
)
@ -550,6 +583,7 @@ def message_history_item(item: MessageHistoryItem) -> rx.Component:
item.type,
(MessageType.USER_PROMPT, user_prompt(item)),
(MessageType.THINKING, thinking(item)),
(MessageType.TOOL_CALL, tool_call(item)),
(MessageType.OUTPUT, output(item)),
),
padding="0 16px",

View File

@ -320,7 +320,7 @@ class ConversationState(rx.State):
# 运行并流式输出事件
stream_events = run_stream_events(
deps=dict(conversation.workflow.deps),
deps=conversation.workflow.deps,
user_prompt=user_prompt,
message_history=await db_state.get_message_history(
conversation_id=self.actived_conversation_id
@ -348,6 +348,7 @@ class ConversationState(rx.State):
# 若等待流式输出则将等待流式输出设置为否
if conversation.awaiting_stream:
conversation.awaiting_stream = False
print(event)
match event:
# ========== 开始事件 ==========
case PartStartEvent(
@ -452,6 +453,8 @@ class ConversationState(rx.State):
message = conversation.messages[
tool_name_map_to_message_id[tool_name]
]
message.is_running = False
yield # 通知前端更新渲染
message.title = f"{tool_name}已完成"
message.content = cast(
str, content
@ -516,7 +519,7 @@ class ConversationState(rx.State):
workflow = Workflow(type=type, deps=deps)
# 设置用户提示词
self.set_user_prompt(
f"帮我查询并预定在 {deps.date.strftime('%Y-%m-%d')}{deps.origin_airport_code}{deps.destination_airport_code} 的航班"
f"帮我查询并预定在 {workflow.deps.date.strftime('%Y-%m-%d')}{workflow.deps.origin_airport_code}{workflow.deps.destination_airport_code} 的航班"
)
conversation.workflow = workflow

View File

@ -299,8 +299,8 @@ class DatabaseState(rx.State):
) # 将 RunUsage 序列化为 JSON 字符串
if isinstance(workflow, Workflow):
record.workflow = workflow_dump_json(
Workflow(**asdict(workflow))
) # 将 Workflow 序列化为 JSON 字符串
Workflow(**workflow.model_dump(mode="json"))
) # 将 Workflow 序列化为 JSON 字符串(因 workflow 基于 pydantic 建模,故使用 model_dump 方法转为字典)
await session.commit()
async def create_message_record(

View File

@ -202,7 +202,7 @@ async def search_flights(ctx: RunContext[Deps]) -> list[Flight]:
return searched_flights
@agent.tool(name="预定航班", requires_approval=True)
@agent.tool(name="预定航班")
async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult:
"""
预定航班
@ -211,7 +211,7 @@ async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult:
raise ModelRetry("必须先使用 search_flights 查询航班")
if not ctx.tool_call_approved:
raise ApprovalRequired()
raise ApprovalRequired(metadata={"content": "您要预定哪班航班?"})
# 提取到的航班号
extracted_flight_number = (
@ -286,10 +286,6 @@ async def run_stream_events(
"""
运行并流式输出事件
"""
# 构建智能体运行事件
yield AgentRunEvent(
content=f"正在预定 {deps.date.strftime('%Y-%m-%d')}{deps.origin_airport_code}{deps.destination_airport_code} 的航班"
)
tool_names = set()
async with agent.run_stream_events(
user_prompt=user_prompt,
@ -322,9 +318,9 @@ async def run_stream_events(
case "查询航班":
# 查询到的航班
searched_flights = cast(list[Flight], content)
event.part.content = f"已查询到 **{len(searched_flights)} 班航班**\n"
event.part.content = f"已查询到 **{len(searched_flights)}** 班航班:\n\n"
for searched_flight in searched_flights:
event.part.content += f"{searched_flight.number} {searched_flight.airfare}{searched_flight.date.strftime('%Y-%m-%d')}{searched_flight.origin_airport_code}{searched_flight.destination_airport_code}\n"
event.part.content += f"{searched_flight.number} {searched_flight.airfare}${searched_flight.date.strftime('%Y-%m-%d')}{searched_flight.origin_airport_code}{searched_flight.destination_airport_code}\n\n"
yield event
case "预定航班":
@ -334,7 +330,7 @@ async def run_stream_events(
)
if isinstance(content, Flight):
event.part.content = f"已预定航班:\n{content.number} {content.airfare}{content.date.strftime('%Y-%m-%d')}{content.origin_airport_code}{content.destination_airport_code}\n"
event.part.content = f"已预定航班:\n{content.number} {content.airfare}${content.date.strftime('%Y-%m-%d')}{content.origin_airport_code}{content.destination_airport_code}\n"
yield event
case AgentRunResultEvent(

View File

@ -85,3 +85,24 @@ pre, pre code {
background: var(--prismui-color-5);
}
}
.is_running {
/* 使用主题变量:普通文字色 --prismui-color-3高光使用主题蓝色 --prismui-color-5 */
background: linear-gradient(
90deg,
var(--prismui-color-3) 0%,
var(--prismui-color-3) 35%,
var(--prismui-color-7) 50%,
var(--prismui-color-3) 65%,
var(--prismui-color-3) 100%
);
background-size: 200% 100%;
background-clip: text;
-webkit-background-clip: text;
color: transparent !important;
animation: shimmer 2.7s infinite linear;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}

Binary file not shown.