This commit is contained in:
parent
5abfc83d4c
commit
714675cf5b
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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.
Loading…
Reference in New Issue