This commit is contained in:
parent
a8dc684c20
commit
48cbabe836
|
|
@ -25,7 +25,7 @@ def conversation_history_item(
|
||||||
# 高亮
|
# 高亮
|
||||||
is_highlight = (
|
is_highlight = (
|
||||||
item.id == ConversationState.popover_shown_conversation_history_item_id
|
item.id == ConversationState.popover_shown_conversation_history_item_id
|
||||||
) | (item.id == ConversationState.actived_conversation_id)
|
) | (item.id == ConversationState.conversation_id)
|
||||||
|
|
||||||
return rx.list.item(
|
return rx.list.item(
|
||||||
rx.vstack(
|
rx.vstack(
|
||||||
|
|
@ -63,7 +63,7 @@ def conversation_history_item(
|
||||||
margin_bottom="8px",
|
margin_bottom="8px",
|
||||||
),
|
),
|
||||||
# 点击事件:设置激活会话
|
# 点击事件:设置激活会话
|
||||||
on_click=lambda: ConversationState.set_actived_conversation(item.id),
|
on_click=lambda: ConversationState.set_conversation(item.id),
|
||||||
width="100%",
|
width="100%",
|
||||||
),
|
),
|
||||||
# 会话历史项的更多按钮
|
# 会话历史项的更多按钮
|
||||||
|
|
@ -461,13 +461,12 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
|
||||||
item.has_approval,
|
item.has_approval,
|
||||||
rx.vstack(
|
rx.vstack(
|
||||||
rx.text_area(
|
rx.text_area(
|
||||||
# 值绑定审批用户提示词
|
# 值绑定审批内容
|
||||||
value=item.approval_user_prompt,
|
value=item.approval_content,
|
||||||
# 值变化事件:设置审批用户提示词
|
# 值变化事件:设置审批内容
|
||||||
on_change=lambda user_prompt: ConversationState.set_approval_user_prompt(
|
on_change=lambda content: ConversationState.set_approval_content(
|
||||||
item.id, user_prompt
|
item.id, content
|
||||||
),
|
),
|
||||||
disabled=item.is_approval_user_prompt_input_disabled,
|
|
||||||
placeholder="请输入",
|
placeholder="请输入",
|
||||||
vertical_align="middle",
|
vertical_align="middle",
|
||||||
width="100%",
|
width="100%",
|
||||||
|
|
@ -490,30 +489,16 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
|
||||||
),
|
),
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
rx.button(
|
rx.button(
|
||||||
rx.cond(
|
|
||||||
item.is_approval_user_prompt_input_disabled,
|
|
||||||
"已提交",
|
|
||||||
"提交",
|
"提交",
|
||||||
),
|
|
||||||
# 点击事件:审批
|
# 点击事件:审批
|
||||||
on_click=lambda: ConversationState.approve(item.id),
|
on_click=lambda: ConversationState.run(True, item.id),
|
||||||
disabled=item.is_approval_user_prompt_input_disabled,
|
|
||||||
padding="0 12px",
|
padding="0 12px",
|
||||||
# 是否禁用绑定审批用户提示词不可输出状态
|
background_color="var(--prismui-background-color-7)",
|
||||||
background_color=rx.cond(
|
|
||||||
item.is_approval_user_prompt_input_disabled,
|
|
||||||
"var(--prismui-background-color-8)",
|
|
||||||
"var(--prismui-background-color-7)",
|
|
||||||
),
|
|
||||||
border_radius="var(--prismui-border-radius-3)",
|
border_radius="var(--prismui-border-radius-3)",
|
||||||
line_height="18px",
|
line_height="18px",
|
||||||
font_size="var(--prismui-font-size-1)",
|
font_size="var(--prismui-font-size-1)",
|
||||||
color="var(--prismui-color-9)",
|
color="var(--prismui-color-9)",
|
||||||
cursor=rx.cond(
|
cursor="pointer",
|
||||||
item.is_approval_user_prompt_input_disabled,
|
|
||||||
"not-allowed",
|
|
||||||
"pointer",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
justify_content="flex-end",
|
justify_content="flex-end",
|
||||||
width="100%",
|
width="100%",
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ from application.states.models import (
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowType,
|
WorkflowType,
|
||||||
)
|
)
|
||||||
from application.workshop.events import AgentPromptEvent
|
from application.workshop.events import AgentRuntimeEvent
|
||||||
|
|
||||||
|
|
||||||
def format_conversation_created_at(created_at: datetime) -> str:
|
def format_conversation_created_at(created_at: datetime) -> str:
|
||||||
|
|
@ -61,9 +61,9 @@ class ConversationState(rx.State):
|
||||||
# 当前用户的会话字典(私有变量)
|
# 当前用户的会话字典(私有变量)
|
||||||
|
|
||||||
# 当前用户的会话字典
|
# 当前用户的会话字典
|
||||||
conversations: dict[str, Conversation] = {} # 键为会话唯一标识,值为会话实例
|
conversations: dict[str, Conversation] = {} # 键为会话唯一标识,值为会话
|
||||||
# 激活的会话唯一标识
|
# 当前会话唯一标识
|
||||||
actived_conversation_id: str = ""
|
conversation_id: str = ""
|
||||||
|
|
||||||
# 显示会话历史,True 表示显示,False 表示隐藏
|
# 显示会话历史,True 表示显示,False 表示隐藏
|
||||||
is_conversation_history_shown: bool = False
|
is_conversation_history_shown: bool = False
|
||||||
|
|
@ -98,14 +98,25 @@ class ConversationState(rx.State):
|
||||||
db_state = await self.get_db_state()
|
db_state = await self.get_db_state()
|
||||||
# 查询当前用户的会话字典
|
# 查询当前用户的会话字典
|
||||||
self.conversations = await db_state.read_conversations(self.user_id)
|
self.conversations = await db_state.read_conversations(self.user_id)
|
||||||
# 若当前用户的会话字典为空则先创建会话记录再添加会话实例
|
# 若当前用户的会话字典为空则先创建会话记录再添加会话
|
||||||
if not self.conversations:
|
if not self.conversations:
|
||||||
self.conversations.update(
|
self.conversations.update(
|
||||||
await db_state.create_conversation_record(self.user_id)
|
await db_state.create_conversation_record(self.user_id)
|
||||||
)
|
)
|
||||||
# 将最后一个会话的唯一标识作为激活会话唯一标识
|
# 将最后一个会话的唯一标识作为当前会话唯一标识
|
||||||
self.actived_conversation_id = next(reversed(self.conversations.keys()))
|
self.conversation_id = next(reversed(self.conversations.keys()))
|
||||||
print(self.conversations[self.actived_conversation_id])
|
|
||||||
|
# 当前会话
|
||||||
|
conversation = self.conversations[self.conversation_id]
|
||||||
|
# 最后一个消息
|
||||||
|
message = next(reversed(conversation.messages.values()))
|
||||||
|
# 若最后一个消息包含审批且审批结果为 None 则将会话正在运行设置为是、消息正在运行设置为是
|
||||||
|
if (
|
||||||
|
isinstance(approval := message.approval, Approval)
|
||||||
|
and approval.result is None
|
||||||
|
):
|
||||||
|
conversation.is_running = True
|
||||||
|
message.is_running = True
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def toggle_conversation_history_shown(self) -> None:
|
def toggle_conversation_history_shown(self) -> None:
|
||||||
|
|
@ -159,18 +170,18 @@ class ConversationState(rx.State):
|
||||||
await db_state.create_conversation_record(self.user_id)
|
await db_state.create_conversation_record(self.user_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 删除后,若激活会话唯一标识不存在则将最后一个会话的唯一标识作为激活会话唯一标识
|
# 删除后,若当前会话唯一标识不存在则将最后一个会话的唯一标识作为当前会话唯一标识
|
||||||
if self.actived_conversation_id not in self.conversations:
|
if self.conversation_id not in self.conversations:
|
||||||
self.actived_conversation_id = next(reversed(self.conversations.keys()))
|
self.conversation_id = next(reversed(self.conversations.keys()))
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def set_actived_conversation(self, conversation_id: str) -> None:
|
def set_conversation(self, conversation_id: str) -> None:
|
||||||
"""
|
"""
|
||||||
设置激活会话
|
设置当前会话
|
||||||
:param conversation_id: 会话唯一标识
|
:param conversation_id: 会话唯一标识
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.actived_conversation_id = conversation_id
|
self.conversation_id = conversation_id
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def message_history(self) -> list[MessageHistoryItem]:
|
def message_history(self) -> list[MessageHistoryItem]:
|
||||||
|
|
@ -179,7 +190,7 @@ class ConversationState(rx.State):
|
||||||
:return: 当前会话的消息历史
|
:return: 当前会话的消息历史
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return []
|
return []
|
||||||
message_history: list[MessageHistoryItem] = []
|
message_history: list[MessageHistoryItem] = []
|
||||||
|
|
@ -187,13 +198,9 @@ class ConversationState(rx.State):
|
||||||
message_history.append(
|
message_history.append(
|
||||||
MessageHistoryItem(
|
MessageHistoryItem(
|
||||||
**message.model_dump(),
|
**message.model_dump(),
|
||||||
has_approval=isinstance(approval := message.approval, Approval),
|
has_approval=isinstance(approval := message.approval, Approval)
|
||||||
approval_user_prompt=getattr(approval, "user_prompt", ""),
|
and approval.result is None,
|
||||||
is_approval_user_prompt_input_disabled=(
|
approval_content=getattr(approval, "content", ""),
|
||||||
False
|
|
||||||
if (isinstance(approval, Approval) and approval.result is None)
|
|
||||||
else True
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return message_history
|
return message_history
|
||||||
|
|
@ -206,12 +213,12 @@ class ConversationState(rx.State):
|
||||||
"""
|
"""
|
||||||
# 获取当前数据库状态
|
# 获取当前数据库状态
|
||||||
db_state = await self.get_db_state()
|
db_state = await self.get_db_state()
|
||||||
# 创建会话记录再添加会话实例
|
# 创建会话记录再添加会话
|
||||||
self.conversations.update(
|
self.conversations.update(
|
||||||
await db_state.create_conversation_record(self.user_id)
|
await db_state.create_conversation_record(self.user_id)
|
||||||
)
|
)
|
||||||
# 将最后一个会话的唯一标识作为激活会话唯一标识
|
# 将最后一个会话的唯一标识作为当前会话唯一标识
|
||||||
self.actived_conversation_id = next(reversed(self.conversations.keys()))
|
self.conversation_id = next(reversed(self.conversations.keys()))
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def user_prompt(self) -> str:
|
def user_prompt(self) -> str:
|
||||||
|
|
@ -220,7 +227,7 @@ class ConversationState(rx.State):
|
||||||
:return: 用户提示词
|
:return: 用户提示词
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return ""
|
return ""
|
||||||
return conversation.user_prompt
|
return conversation.user_prompt
|
||||||
|
|
@ -233,9 +240,13 @@ class ConversationState(rx.State):
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not user_prompt.strip():
|
||||||
|
return
|
||||||
|
|
||||||
conversation.user_prompt = user_prompt.strip()
|
conversation.user_prompt = user_prompt.strip()
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
|
|
@ -245,7 +256,7 @@ class ConversationState(rx.State):
|
||||||
:return: bool
|
:return: bool
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return True
|
return True
|
||||||
return conversation.is_running or not conversation.user_prompt
|
return conversation.is_running or not conversation.user_prompt
|
||||||
|
|
@ -253,27 +264,53 @@ class ConversationState(rx.State):
|
||||||
@rx.event
|
@rx.event
|
||||||
async def run(
|
async def run(
|
||||||
self,
|
self,
|
||||||
approval: Approval | None = None,
|
is_resume_run: bool = False,
|
||||||
deferred_tool_results: DeferredToolResults | None = None,
|
message_id: str | None = None,
|
||||||
) -> AsyncGenerator[None]:
|
) -> AsyncGenerator[None]:
|
||||||
"""
|
"""
|
||||||
运行
|
运行
|
||||||
:param approval: 审批实例
|
:param is_resume_run: 恢复运行
|
||||||
:param deferred_tool_results: 延迟工具调用结果
|
:param message_id: 消息唯一标识
|
||||||
:return: AsyncGenerator
|
:return: AsyncGenerator
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return
|
return
|
||||||
|
|
||||||
# 获取数据库状态
|
# 获取当前数据库状态
|
||||||
db_state = await self.get_db_state()
|
db_state = await self.get_db_state()
|
||||||
|
|
||||||
if isinstance(approval, Approval):
|
user_prompt = None # 用户提示词
|
||||||
if not (user_prompt := approval.user_prompt):
|
deferred_tool_results = None # 延迟工具结果
|
||||||
|
# 若为恢复运行
|
||||||
|
if is_resume_run:
|
||||||
|
if not message_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 指定消息唯一标识的消息
|
||||||
|
message = conversation.messages.get(message_id)
|
||||||
|
if (
|
||||||
|
not message
|
||||||
|
or not isinstance(approval := message.approval, Approval)
|
||||||
|
or not approval.content
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 将等待流式输出设置为是
|
||||||
|
conversation.awaiting_stream = True
|
||||||
|
# 更新消息内容
|
||||||
|
message.content = f"您输入的是:\n\n{approval.content}\n\n"
|
||||||
|
# 更新审批结果
|
||||||
|
approval.result = True
|
||||||
|
yield # 通知前端更新渲染
|
||||||
|
|
||||||
|
# 构建延迟工具结果
|
||||||
|
deferred_tool_results = DeferredToolResults(
|
||||||
|
approvals={approval.tool_call_id: ToolApproved()}, # 审批通过
|
||||||
|
metadata={approval.tool_call_id: {"content": approval.content}},
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 若用户提示词为空则直接返回
|
# 若用户提示词为空则直接返回
|
||||||
if not (user_prompt := conversation.user_prompt):
|
if not (user_prompt := conversation.user_prompt):
|
||||||
|
|
@ -285,7 +322,7 @@ class ConversationState(rx.State):
|
||||||
conversation.is_running = True
|
conversation.is_running = True
|
||||||
# 将等待流式输出设置为是
|
# 将等待流式输出设置为是
|
||||||
conversation.awaiting_stream = True
|
conversation.awaiting_stream = True
|
||||||
# 先创建消息记录再添加消息实例
|
# 先创建消息记录再添加消息
|
||||||
conversation.messages.update(
|
conversation.messages.update(
|
||||||
await db_state.create_message_record(
|
await db_state.create_message_record(
|
||||||
conversation_id=conversation.id,
|
conversation_id=conversation.id,
|
||||||
|
|
@ -297,13 +334,15 @@ class ConversationState(rx.State):
|
||||||
)
|
)
|
||||||
yield # 通知前端更新渲染
|
yield # 通知前端更新渲染
|
||||||
|
|
||||||
# 初始化片段索引映射为消息实例唯一标识字典
|
# 初始化片段索引映射为消息唯一标识字典
|
||||||
index_map_to_message_id: dict[int, str] = {}
|
index_map_to_message_id: dict[int, str] = {}
|
||||||
|
|
||||||
# 查询消息历史列表
|
# 查询消息历史列表
|
||||||
message_history = await db_state.read_message_history(
|
message_history = await db_state.read_message_history(
|
||||||
conversation_id=self.actived_conversation_id
|
conversation_id=self.conversation_id
|
||||||
)
|
)
|
||||||
|
# 使用量
|
||||||
|
usage = conversation.usage
|
||||||
|
|
||||||
# 初始化工作流
|
# 初始化工作流
|
||||||
match conversation.workflow.type:
|
match conversation.workflow.type:
|
||||||
|
|
@ -316,7 +355,7 @@ class ConversationState(rx.State):
|
||||||
stream_events = run_stream_events(
|
stream_events = run_stream_events(
|
||||||
user_prompt=user_prompt,
|
user_prompt=user_prompt,
|
||||||
message_history=message_history,
|
message_history=message_history,
|
||||||
usage=conversation.usage,
|
usage=usage,
|
||||||
)
|
)
|
||||||
case WorkflowType.BOOK_FLIGHT:
|
case WorkflowType.BOOK_FLIGHT:
|
||||||
from application.workshop.book_flight import (
|
from application.workshop.book_flight import (
|
||||||
|
|
@ -328,7 +367,7 @@ class ConversationState(rx.State):
|
||||||
deps=conversation.workflow.deps,
|
deps=conversation.workflow.deps,
|
||||||
user_prompt=user_prompt,
|
user_prompt=user_prompt,
|
||||||
message_history=message_history,
|
message_history=message_history,
|
||||||
usage=conversation.usage,
|
usage=usage,
|
||||||
deferred_tool_results=deferred_tool_results,
|
deferred_tool_results=deferred_tool_results,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -348,7 +387,7 @@ class ConversationState(rx.State):
|
||||||
match part:
|
match part:
|
||||||
# 思考分片
|
# 思考分片
|
||||||
case ThinkingPart(content=content):
|
case ThinkingPart(content=content):
|
||||||
# 构建消息实例
|
# 构建消息
|
||||||
message = Message(
|
message = Message(
|
||||||
type=MessageType.THINKING,
|
type=MessageType.THINKING,
|
||||||
title="正在思考",
|
title="正在思考",
|
||||||
|
|
@ -357,7 +396,7 @@ class ConversationState(rx.State):
|
||||||
)
|
)
|
||||||
# 添加至消息字典
|
# 添加至消息字典
|
||||||
conversation.messages[message.id] = message
|
conversation.messages[message.id] = message
|
||||||
# 将消息实例唯一标识与片段索引映射
|
# 将消息唯一标识与片段索引映射
|
||||||
index_map_to_message_id[index] = message.id
|
index_map_to_message_id[index] = message.id
|
||||||
yield # 通知前端更新渲染
|
yield # 通知前端更新渲染
|
||||||
|
|
||||||
|
|
@ -369,7 +408,7 @@ class ConversationState(rx.State):
|
||||||
tool_call_id
|
tool_call_id
|
||||||
not in conversation.workflow.tool_call_id_map_to_message_id
|
not in conversation.workflow.tool_call_id_map_to_message_id
|
||||||
):
|
):
|
||||||
# 构建消息实例
|
# 构建消息
|
||||||
message = Message(
|
message = Message(
|
||||||
type=MessageType.TOOL_CALL,
|
type=MessageType.TOOL_CALL,
|
||||||
title=f"正在{tool_name}",
|
title=f"正在{tool_name}",
|
||||||
|
|
@ -377,7 +416,7 @@ class ConversationState(rx.State):
|
||||||
)
|
)
|
||||||
# 添加至消息字典
|
# 添加至消息字典
|
||||||
conversation.messages[message.id] = message
|
conversation.messages[message.id] = message
|
||||||
# 将消息实例唯一标识与工具调用唯一标识映射
|
# 将消息唯一标识与工具调用唯一标识映射
|
||||||
conversation.workflow.tool_call_id_map_to_message_id[
|
conversation.workflow.tool_call_id_map_to_message_id[
|
||||||
tool_call_id
|
tool_call_id
|
||||||
] = message.id
|
] = message.id
|
||||||
|
|
@ -385,11 +424,11 @@ class ConversationState(rx.State):
|
||||||
|
|
||||||
# 文本分片
|
# 文本分片
|
||||||
case TextPart(content=content):
|
case TextPart(content=content):
|
||||||
# 构建消息实例
|
# 构建消息
|
||||||
message = Message(type=MessageType.TEXT, content=content)
|
message = Message(type=MessageType.TEXT, content=content)
|
||||||
# 添加至消息字典
|
# 添加至消息字典
|
||||||
conversation.messages[message.id] = message
|
conversation.messages[message.id] = message
|
||||||
# 将消息实例唯一标识与片段索引映射
|
# 将消息唯一标识与片段索引映射
|
||||||
index_map_to_message_id[index] = message.id
|
index_map_to_message_id[index] = message.id
|
||||||
yield # 通知前端更新渲染
|
yield # 通知前端更新渲染
|
||||||
|
|
||||||
|
|
@ -425,7 +464,7 @@ class ConversationState(rx.State):
|
||||||
match part:
|
match part:
|
||||||
# 思考分片
|
# 思考分片
|
||||||
case ThinkingPart():
|
case ThinkingPart():
|
||||||
# 获取消息实例
|
# 获取消息
|
||||||
message = conversation.messages[
|
message = conversation.messages[
|
||||||
index_map_to_message_id[index]
|
index_map_to_message_id[index]
|
||||||
]
|
]
|
||||||
|
|
@ -436,7 +475,7 @@ class ConversationState(rx.State):
|
||||||
|
|
||||||
# 文本分片
|
# 文本分片
|
||||||
case TextPart():
|
case TextPart():
|
||||||
# 获取消息实例
|
# 获取消息
|
||||||
message = conversation.messages[
|
message = conversation.messages[
|
||||||
index_map_to_message_id[index]
|
index_map_to_message_id[index]
|
||||||
]
|
]
|
||||||
|
|
@ -451,7 +490,7 @@ class ConversationState(rx.State):
|
||||||
tool_name=tool_name,
|
tool_name=tool_name,
|
||||||
content=content,
|
content=content,
|
||||||
):
|
):
|
||||||
# 获取消息实例
|
# 获取消息
|
||||||
message = conversation.messages[
|
message = conversation.messages[
|
||||||
conversation.workflow.tool_call_id_map_to_message_id[
|
conversation.workflow.tool_call_id_map_to_message_id[
|
||||||
tool_call_id
|
tool_call_id
|
||||||
|
|
@ -464,20 +503,30 @@ class ConversationState(rx.State):
|
||||||
yield # 通知前端更新渲染
|
yield # 通知前端更新渲染
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
|
|
||||||
# ========== 智能体提示事件 ==========
|
# ========== 智能体运行时事件 ==========
|
||||||
case AgentPromptEvent(content=content):
|
case AgentRuntimeEvent(tool_call_id=tool_call_id, content=content):
|
||||||
# 构建消息实例
|
# 若工具调用唯一标识为空则创建消息并添加至消息字典,否则若消息内容非空则根据工具调用唯一标识获取消息并更新消息内容
|
||||||
|
if not tool_call_id:
|
||||||
message = Message(type=MessageType.TEXT, content=content)
|
message = Message(type=MessageType.TEXT, content=content)
|
||||||
# 添加至消息字典
|
|
||||||
conversation.messages[message.id] = message
|
conversation.messages[message.id] = message
|
||||||
yield # 通知前端更新渲染
|
yield # 通知前端更新渲染
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if content:
|
||||||
|
message = conversation.messages[
|
||||||
|
conversation.workflow.tool_call_id_map_to_message_id[
|
||||||
|
tool_call_id
|
||||||
|
]
|
||||||
|
]
|
||||||
|
message.content = content
|
||||||
|
yield # 通知前端更新渲染
|
||||||
|
|
||||||
# ========== 智能体运行结果事件 ==========
|
# ========== 智能体运行结果事件 ==========
|
||||||
case AgentRunResultEvent(result=result):
|
case AgentRunResultEvent(result=result):
|
||||||
if isinstance(output := result.output, DeferredToolRequests):
|
if isinstance(output := result.output, DeferredToolRequests):
|
||||||
for appr in output.approvals:
|
for appr in output.approvals:
|
||||||
# 获取消息实例
|
# 获取消息
|
||||||
message = conversation.messages[
|
message = conversation.messages[
|
||||||
conversation.workflow.tool_call_id_map_to_message_id[
|
conversation.workflow.tool_call_id_map_to_message_id[
|
||||||
tool_call_id := appr.tool_call_id
|
tool_call_id := appr.tool_call_id
|
||||||
|
|
@ -524,7 +573,7 @@ class ConversationState(rx.State):
|
||||||
:return: 当前会话正在运行(True 表示正在运行,False 表示运行完成)
|
:return: 当前会话正在运行(True 表示正在运行,False 表示运行完成)
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return False
|
return False
|
||||||
return conversation.is_running
|
return conversation.is_running
|
||||||
|
|
@ -536,7 +585,7 @@ class ConversationState(rx.State):
|
||||||
:return: 当前会话正在等待流式输出(True 表示等待流式输出,False 表示已开始流式输出或已完成)
|
:return: 当前会话正在等待流式输出(True 表示等待流式输出,False 表示已开始流式输出或已完成)
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return False
|
return False
|
||||||
return conversation.awaiting_stream
|
return conversation.awaiting_stream
|
||||||
|
|
@ -547,59 +596,32 @@ class ConversationState(rx.State):
|
||||||
展示 / 隐藏消息历史项
|
展示 / 隐藏消息历史项
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return
|
return
|
||||||
conversation.messages[item_id].is_shown ^= True
|
conversation.messages[item_id].is_shown ^= True
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def set_approval_user_prompt(self, item_id: str, user_prompt: str) -> None:
|
def set_approval_content(self, item_id: str, content: str) -> None:
|
||||||
"""
|
"""
|
||||||
设置审批用户提示词
|
设置审批内容
|
||||||
:param item_id: 消息历史项唯一标识
|
:param item_id: 消息历史项唯一标识
|
||||||
:param user_prompt: 用户提示词
|
:param content: 内容
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return
|
return
|
||||||
# 获取消息实例
|
|
||||||
|
# 获取消息
|
||||||
message = conversation.messages.get(item_id)
|
message = conversation.messages.get(item_id)
|
||||||
if not message or not isinstance(message.approval, Approval):
|
if not message or not isinstance(approval := message.approval, Approval):
|
||||||
return
|
|
||||||
message.approval.user_prompt = user_prompt
|
|
||||||
|
|
||||||
@rx.event
|
|
||||||
async def approve(self, item_id: str) -> AsyncGenerator[None, None]:
|
|
||||||
"""
|
|
||||||
审批消息历史项
|
|
||||||
:param item_id: 消息历史项唯一标识
|
|
||||||
"""
|
|
||||||
# 当前会话
|
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
|
||||||
if not conversation:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if not (message := conversation.messages.get(item_id)) or not isinstance(
|
if not content.strip():
|
||||||
approval := message.approval, Approval
|
|
||||||
):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# 若会话未正在运行则将正在运行设置为是
|
approval.content = content
|
||||||
if not conversation.is_running:
|
|
||||||
conversation.is_running = True
|
|
||||||
# 将等待流式输出设置为是
|
|
||||||
conversation.awaiting_stream = True
|
|
||||||
# 更新审批结果为审批通过
|
|
||||||
approval.result = True
|
|
||||||
yield # 通知前端更新渲染
|
|
||||||
|
|
||||||
# 运行
|
|
||||||
async for _ in self.run(
|
|
||||||
approval,
|
|
||||||
DeferredToolResults(approvals={approval.tool_call_id: ToolApproved()}),
|
|
||||||
):
|
|
||||||
...
|
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
async def set_workflow(self, type: WorkflowType) -> None:
|
async def set_workflow(self, type: WorkflowType) -> None:
|
||||||
|
|
@ -607,7 +629,7 @@ class ConversationState(rx.State):
|
||||||
设置工作流
|
设置工作流
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ from application.states.models import (
|
||||||
usage_validate_json,
|
usage_validate_json,
|
||||||
workflow_dump_json,
|
workflow_dump_json,
|
||||||
workflow_validate_json,
|
workflow_validate_json,
|
||||||
|
Approval,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -368,6 +369,25 @@ class DatabaseState(rx.State):
|
||||||
)
|
)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
async def update_message_record(
|
||||||
|
self,
|
||||||
|
message_id: str,
|
||||||
|
approval: Approval | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
更新消息记录
|
||||||
|
:param message_id: 消息唯一标识
|
||||||
|
:param approval: 审批实例
|
||||||
|
"""
|
||||||
|
async with rx.asession() as session:
|
||||||
|
record = await session.get(MessageRecord, message_id)
|
||||||
|
if not record:
|
||||||
|
return
|
||||||
|
if isinstance(approval, Approval):
|
||||||
|
record.approval = approval_dump_json(approval)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
async def read_message_history(self, conversation_id: str) -> list[ModelMessage]:
|
async def read_message_history(self, conversation_id: str) -> list[ModelMessage]:
|
||||||
"""
|
"""
|
||||||
查询消息历史列表
|
查询消息历史列表
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,6 @@ from pydantic_ai import RunUsage
|
||||||
from pydantic_ai._uuid import uuid7
|
from pydantic_ai._uuid import uuid7
|
||||||
|
|
||||||
|
|
||||||
class NoResult(BaseModel):
|
|
||||||
"""
|
|
||||||
无结果类
|
|
||||||
"""
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class MessageType(StrEnum):
|
class MessageType(StrEnum):
|
||||||
"""
|
"""
|
||||||
消息类型枚举
|
消息类型枚举
|
||||||
|
|
@ -36,7 +28,7 @@ class Approval(BaseModel):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tool_call_id: str = Field(..., description="工具调用唯一标识")
|
tool_call_id: str = Field(..., description="工具调用唯一标识")
|
||||||
user_prompt: str = Field(default="", description="用户提示词")
|
content: str = Field(default="", description="审批内容")
|
||||||
result: bool | None = Field(
|
result: bool | None = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="审批结果,True 表示审批通过,False 表示审批拒绝,None 表示待审批",
|
description="审批结果,True 表示审批通过,False 表示审批拒绝,None 表示待审批",
|
||||||
|
|
@ -119,10 +111,7 @@ class MessageHistoryItem(Message):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
has_approval: bool = Field(default=False, description="包含审批实例")
|
has_approval: bool = Field(default=False, description="包含审批实例")
|
||||||
approval_user_prompt: str = Field(default="", description="审批用户提示词")
|
approval_content: str = Field(default="", description="审批内容")
|
||||||
is_approval_user_prompt_input_disabled: bool = Field(
|
|
||||||
default=False, description="审批用户提示词不可输出状态"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# 工作流适配器
|
# 工作流适配器
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ from pydantic_ai.messages import (
|
||||||
)
|
)
|
||||||
from pydantic_ai.run import AgentRunResultEvent
|
from pydantic_ai.run import AgentRunResultEvent
|
||||||
|
|
||||||
from application.workshop.events import AgentPromptEvent
|
from application.workshop.events import AgentRuntimeEvent
|
||||||
from application.workshop.models import (
|
from application.workshop.models import (
|
||||||
DEEPSEEK_V4_FLASH_MODEL,
|
DEEPSEEK_V4_FLASH_MODEL,
|
||||||
DEEPSEEK_V4_FLASH_MODEL_SETTINGS,
|
DEEPSEEK_V4_FLASH_MODEL_SETTINGS,
|
||||||
|
|
@ -172,7 +172,7 @@ async def search_flights(ctx: RunContext[Deps]) -> list[Flight]:
|
||||||
**设计工具需先设计业务流程**
|
**设计工具需先设计业务流程**
|
||||||
本示例按照 查询航班 -> 推荐航班 -> 预定航班
|
本示例按照 查询航班 -> 推荐航班 -> 预定航班
|
||||||
"""
|
"""
|
||||||
if ctx.deps.searched_flights:
|
if ctx.deps.searched_flights is not None:
|
||||||
return ctx.deps.searched_flights
|
return ctx.deps.searched_flights
|
||||||
|
|
||||||
# 提取到的所有航班信息
|
# 提取到的所有航班信息
|
||||||
|
|
@ -216,13 +216,14 @@ async def book_flight(ctx: RunContext[Deps]) -> Flight:
|
||||||
if (searched_flights := ctx.deps.searched_flights) is None:
|
if (searched_flights := ctx.deps.searched_flights) is None:
|
||||||
raise ModelRetry("必须先使用 search_flights 查询航班")
|
raise ModelRetry("必须先使用 search_flights 查询航班")
|
||||||
|
|
||||||
|
# 若工具调用未审批则抛出需要审批异常,否则解析审批内容并作为用户输入航班号
|
||||||
if not ctx.tool_call_approved:
|
if not ctx.tool_call_approved:
|
||||||
raise ApprovalRequired(metadata={"content": "请输入航班号:\n\n"})
|
raise ApprovalRequired(metadata={"content": "请输入航班号:\n\n"})
|
||||||
|
|
||||||
# 提取到的航班号
|
# 提取到的航班号
|
||||||
extracted_flight_number = (
|
extracted_flight_number = (
|
||||||
await extraction_flight_number_agent.run(
|
await extraction_flight_number_agent.run(
|
||||||
f"用户提示词:\n{cast(str, ctx.prompt).upper()}\n查询到的航班:\n{(flight_numbers:= [flight.number for flight in searched_flights])}",
|
f"用户提示词:\n{cast(str, ctx.tool_call_metadata["content"]).upper()}\n查询到的航班:\n{(flight_numbers:= [flight.number for flight in searched_flights])}",
|
||||||
deps=ctx.deps,
|
deps=ctx.deps,
|
||||||
usage=ctx.usage,
|
usage=ctx.usage,
|
||||||
usage_limits=UsageLimits(request_limit=10),
|
usage_limits=UsageLimits(request_limit=10),
|
||||||
|
|
@ -286,7 +287,7 @@ deps = Deps(
|
||||||
|
|
||||||
async def run_stream_events(
|
async def run_stream_events(
|
||||||
deps: Any,
|
deps: Any,
|
||||||
user_prompt: str,
|
user_prompt: str | None,
|
||||||
message_history: list[ModelMessage],
|
message_history: list[ModelMessage],
|
||||||
usage: RunUsage,
|
usage: RunUsage,
|
||||||
usage_limits: UsageLimits | None = None,
|
usage_limits: UsageLimits | None = None,
|
||||||
|
|
@ -323,6 +324,7 @@ async def run_stream_events(
|
||||||
case FunctionToolResultEvent(part):
|
case FunctionToolResultEvent(part):
|
||||||
match part:
|
match part:
|
||||||
case ToolReturnPart(
|
case ToolReturnPart(
|
||||||
|
tool_call_id=tool_call_id,
|
||||||
tool_name=tool_name,
|
tool_name=tool_name,
|
||||||
content=content,
|
content=content,
|
||||||
):
|
):
|
||||||
|
|
@ -330,28 +332,31 @@ async def run_stream_events(
|
||||||
case "查询航班":
|
case "查询航班":
|
||||||
# 查询到的航班
|
# 查询到的航班
|
||||||
searched_flights = cast(list[Flight], content)
|
searched_flights = cast(list[Flight], content)
|
||||||
event.part.content = f"已查询到 **{len(searched_flights)}** 班航班:\n\n"
|
# 构造智能体运行时事件
|
||||||
|
agent_runtime_event = AgentRuntimeEvent(
|
||||||
|
tool_call_id=tool_call_id,
|
||||||
|
content=f"已查询到 **{len(searched_flights)}** 班航班:\n\n",
|
||||||
|
)
|
||||||
for searched_flight in searched_flights:
|
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\n"
|
agent_runtime_event.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
|
|
||||||
|
yield agent_runtime_event
|
||||||
|
|
||||||
case "预定航班":
|
case "预定航班":
|
||||||
if isinstance(deps.booked_flight, Flight):
|
# 构造智能体运行时事件
|
||||||
event.part.content = (
|
agent_runtime_event = AgentRuntimeEvent(
|
||||||
"请输入航班号:\n\n" # 成功预定内容维持提示
|
tool_call_id=tool_call_id,
|
||||||
)
|
)
|
||||||
else:
|
yield agent_runtime_event
|
||||||
event.part.content = (
|
|
||||||
"未提取到航班号,请检查后重试\n\n"
|
yield event # 智能体运行时依赖 event,原则上不可修改
|
||||||
)
|
|
||||||
yield event
|
|
||||||
|
|
||||||
case AgentRunResultEvent(result=result):
|
case AgentRunResultEvent(result=result):
|
||||||
match result:
|
match result:
|
||||||
case AgentRunResult(output=output):
|
case AgentRunResult(output=output):
|
||||||
match output:
|
match output:
|
||||||
case Flight():
|
case Flight():
|
||||||
yield AgentPromptEvent(
|
yield AgentRuntimeEvent(
|
||||||
content=f"---\n\n✅ 预定机票成功:\n\n{output.number} | 💰 {output.airfare} | {output.date.strftime('%Y-%m-%d')} | {output.origin_airport_code} ✈️ {output.destination_airport_code}\n\n本次会话结束。"
|
content=f"---\n\n✅ 预定机票成功:\n\n{output.number} | 💰 {output.airfare} | {output.date.strftime('%Y-%m-%d')} | {output.origin_airport_code} ✈️ {output.destination_airport_code}\n\n本次会话结束。"
|
||||||
)
|
)
|
||||||
yield event
|
yield event
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ from pydantic_ai.messages import CustomEvent
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class AgentPromptEvent(CustomEvent):
|
class AgentRuntimeEvent(CustomEvent):
|
||||||
"""
|
"""
|
||||||
智能体提示事件
|
智能体运行时事件
|
||||||
"""
|
"""
|
||||||
|
|
||||||
content: str
|
content: str = ""
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ agent = Agent(
|
||||||
|
|
||||||
|
|
||||||
async def run_stream_events(
|
async def run_stream_events(
|
||||||
user_prompt: str,
|
user_prompt: str | None,
|
||||||
message_history: list[ModelMessage],
|
message_history: list[ModelMessage],
|
||||||
usage: RunUsage,
|
usage: RunUsage,
|
||||||
) -> AsyncGenerator[AgentStreamEvent | AgentRunResultEvent, None]:
|
) -> AsyncGenerator[AgentStreamEvent | AgentRunResultEvent, None]:
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue