This commit is contained in:
parent
606ad01352
commit
31828a7be3
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"python.languageServer": "None",
|
"python.languageServer": "None",
|
||||||
"python.analysis.extraPaths": ["${workspaceFolder}"]
|
"python.analysis.extraPaths": ["${workspaceFolder}"],
|
||||||
|
"python.REPL.enableREPLSmartSend": false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ def conversation_history_item(
|
||||||
|
|
||||||
# 高亮
|
# 高亮
|
||||||
is_highlight = (
|
is_highlight = (
|
||||||
item.id == ConversationState.is_conversation_history_item_popover_shown
|
item.id == ConversationState.popover_shown_conversation_history_item_id
|
||||||
) | (item.id == ConversationState.actived_conversation_id)
|
) | (item.id == ConversationState.actived_conversation_id)
|
||||||
|
|
||||||
return rx.list.item(
|
return rx.list.item(
|
||||||
|
|
@ -425,7 +425,7 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
|
||||||
:return: Component
|
:return: Component
|
||||||
"""
|
"""
|
||||||
return rx.vstack(
|
return rx.vstack(
|
||||||
# 工具调用的标题栏
|
# 标题栏
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
rx.icon(
|
rx.icon(
|
||||||
"square-function",
|
"square-function",
|
||||||
|
|
@ -444,7 +444,7 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
|
||||||
margin_bottom="8px",
|
margin_bottom="8px",
|
||||||
line_height="22px",
|
line_height="22px",
|
||||||
),
|
),
|
||||||
# 工具调用的内容
|
# 内容
|
||||||
rx.markdown(
|
rx.markdown(
|
||||||
item.content,
|
item.content,
|
||||||
width="100%",
|
width="100%",
|
||||||
|
|
@ -453,6 +453,54 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
|
||||||
font_size="var(--prismui-font-size-1)",
|
font_size="var(--prismui-font-size-1)",
|
||||||
color="var(--prismui-color-3)",
|
color="var(--prismui-color-3)",
|
||||||
),
|
),
|
||||||
|
# 审批
|
||||||
|
rx.cond(
|
||||||
|
item.has_approval,
|
||||||
|
rx.hstack(
|
||||||
|
rx.input(
|
||||||
|
placeholder="请输入",
|
||||||
|
# 值绑定审批用户提示词
|
||||||
|
value=item.approval_user_prompt,
|
||||||
|
# 值变化事件:设置审批用户提示词
|
||||||
|
on_change=lambda user_prompt: ConversationState.set_approval_user_prompt(
|
||||||
|
item.id, user_prompt
|
||||||
|
),
|
||||||
|
flex=1,
|
||||||
|
height="100%",
|
||||||
|
padding="4px",
|
||||||
|
border_radius="var(--prismui-border-radius-4)",
|
||||||
|
line_height="16px",
|
||||||
|
border="none !important",
|
||||||
|
),
|
||||||
|
rx.button(
|
||||||
|
"发送",
|
||||||
|
# 点击事件:审批
|
||||||
|
on_click=lambda: ConversationState.approve(item.id),
|
||||||
|
# 是否禁用绑定验证码发送禁用状态
|
||||||
|
disabled=AuthState.is_captcha_code_sending_button_disabled,
|
||||||
|
margin="0 8px",
|
||||||
|
background_color="var(--prismui-background-color-1)",
|
||||||
|
line_height="16px",
|
||||||
|
# 若验证码发送禁用状态为是则颜色设置为 color-4,否则设置为 color-5(主题色)
|
||||||
|
color=rx.cond(
|
||||||
|
AuthState.is_captcha_code_sending_button_disabled,
|
||||||
|
"var(--prismui-color-4)",
|
||||||
|
"var(--prismui-color-5)",
|
||||||
|
),
|
||||||
|
cursor=rx.cond(
|
||||||
|
AuthState.is_captcha_code_sending_button_disabled,
|
||||||
|
"not-allowed",
|
||||||
|
"pointer",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width="100%",
|
||||||
|
height="45px",
|
||||||
|
align_items="center",
|
||||||
|
border="var(--prismui-border-1)",
|
||||||
|
border_radius="var(--prismui-border-radius-4)",
|
||||||
|
),
|
||||||
|
rx.fragment(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -772,7 +820,7 @@ def user_prompt_sending() -> rx.Component:
|
||||||
# 不可点击状态:绑定用户提示词发送按钮不可点击状态
|
# 不可点击状态:绑定用户提示词发送按钮不可点击状态
|
||||||
disabled=ConversationState.is_user_prompt_sending_button_disabled,
|
disabled=ConversationState.is_user_prompt_sending_button_disabled,
|
||||||
# 点击事件:运行
|
# 点击事件:运行
|
||||||
on_click=ConversationState.run,
|
on_click=lambda: ConversationState.run(),
|
||||||
display="inline-flex",
|
display="inline-flex",
|
||||||
position="relative",
|
position="relative",
|
||||||
justify_content="center",
|
justify_content="center",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,12 @@
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import AsyncGenerator, cast
|
from typing import AsyncGenerator, cast
|
||||||
from pydantic_ai import DeferredToolRequests
|
from pydantic_ai import (
|
||||||
|
DeferredToolRequests,
|
||||||
|
DeferredToolResults,
|
||||||
|
ToolApproved,
|
||||||
|
ToolDenied,
|
||||||
|
)
|
||||||
|
|
||||||
from pydantic_ai.messages import (
|
from pydantic_ai.messages import (
|
||||||
FunctionToolCallEvent,
|
FunctionToolCallEvent,
|
||||||
|
|
@ -67,13 +72,13 @@ class ConversationState(rx.State):
|
||||||
|
|
||||||
# 当前用户的会话字典
|
# 当前用户的会话字典
|
||||||
conversations: dict[str, Conversation] = {} # 键为会话唯一标识,值为会话实例
|
conversations: dict[str, Conversation] = {} # 键为会话唯一标识,值为会话实例
|
||||||
# 激活会话唯一标识
|
# 激活的会话唯一标识
|
||||||
actived_conversation_id: str = ""
|
actived_conversation_id: str = ""
|
||||||
|
|
||||||
# 显示会话历史,True 表示显示,False 表示隐藏
|
# 显示会话历史,True 表示显示,False 表示隐藏
|
||||||
is_conversation_history_shown: bool = False
|
is_conversation_history_shown: bool = False
|
||||||
# 会话历史项显示悬停气泡
|
# 悬停气泡显示的会话历史项唯一标识(一个会话只允许存在一个悬停气泡会话历史项)
|
||||||
is_conversation_history_item_popover_shown: str = ""
|
popover_shown_conversation_history_item_id: str = ""
|
||||||
|
|
||||||
# 当前数据库状态(私有变量)
|
# 当前数据库状态(私有变量)
|
||||||
# 私有变量:reflex 约定以 _ 开头的变量为私有变量,后端不序列化,前端不可使用
|
# 私有变量:reflex 约定以 _ 开头的变量为私有变量,后端不序列化,前端不可使用
|
||||||
|
|
@ -101,8 +106,8 @@ class ConversationState(rx.State):
|
||||||
|
|
||||||
# 获取当前数据库状态
|
# 获取当前数据库状态
|
||||||
db_state = await self.get_db_state()
|
db_state = await self.get_db_state()
|
||||||
# 获取当前用户的会话字典
|
# 查询当前用户的会话字典
|
||||||
self.conversations = await db_state.get_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(
|
||||||
|
|
@ -136,15 +141,13 @@ class ConversationState(rx.State):
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def set_conversation_history_item_popover_shown(
|
def set_conversation_history_item_popover_shown(
|
||||||
self, conversation_id: str, is_shown: bool
|
self, item_id: str, is_shown: bool
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
设置会话历史项悬停气泡显示 / 隐藏
|
设置会话历史项悬停气泡显示 / 隐藏
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.is_conversation_history_item_popover_shown = (
|
self.popover_shown_conversation_history_item_id = item_id if is_shown else ""
|
||||||
conversation_id if is_shown else ""
|
|
||||||
)
|
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
async def delete_conversation(self, conversation_id: str) -> None:
|
async def delete_conversation(self, conversation_id: str) -> None:
|
||||||
|
|
@ -188,10 +191,22 @@ class ConversationState(rx.State):
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
conversation = self.conversations.get(self.actived_conversation_id)
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return []
|
return []
|
||||||
return [
|
message_history: list[MessageHistoryItem] = []
|
||||||
MessageHistoryItem(**message.model_dump())
|
has_approval = False
|
||||||
for message in list(conversation.messages.values())
|
approval_user_prompt = ""
|
||||||
]
|
for message in conversation.messages.values():
|
||||||
|
approval = message.approval
|
||||||
|
if isinstance(approval, Approval):
|
||||||
|
has_approval = True
|
||||||
|
approval_user_prompt = approval.user_prompt
|
||||||
|
message_history.append(
|
||||||
|
MessageHistoryItem(
|
||||||
|
**message.model_dump(),
|
||||||
|
has_approval=has_approval,
|
||||||
|
approval_user_prompt=approval_user_prompt,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return message_history
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
async def create_conversation(self) -> None:
|
async def create_conversation(self) -> None:
|
||||||
|
|
@ -245,34 +260,16 @@ class ConversationState(rx.State):
|
||||||
return True
|
return True
|
||||||
return conversation.is_running or not conversation.user_prompt
|
return conversation.is_running or not conversation.user_prompt
|
||||||
|
|
||||||
@rx.var
|
|
||||||
def is_running(self) -> bool:
|
|
||||||
"""
|
|
||||||
当前会话正在运行
|
|
||||||
:return: 当前会话正在运行(True 表示正在运行,False 表示运行完成)
|
|
||||||
"""
|
|
||||||
# 当前会话
|
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
|
||||||
if not conversation:
|
|
||||||
return False
|
|
||||||
return conversation.is_running
|
|
||||||
|
|
||||||
@rx.var
|
|
||||||
def awaiting_stream(self) -> bool:
|
|
||||||
"""
|
|
||||||
当前会话正在等待流式输出
|
|
||||||
:return: 当前会话正在等待流式输出(True 表示等待流式输出,False 表示已开始流式输出或已完成)
|
|
||||||
"""
|
|
||||||
# 当前会话
|
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
|
||||||
if not conversation:
|
|
||||||
return False
|
|
||||||
return conversation.awaiting_stream
|
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
async def run(self) -> AsyncGenerator[None]:
|
async def run(
|
||||||
|
self,
|
||||||
|
approval: Approval | None = None,
|
||||||
|
deferred_tool_results: DeferredToolResults | None = None,
|
||||||
|
) -> AsyncGenerator[None]:
|
||||||
"""
|
"""
|
||||||
运行
|
运行
|
||||||
|
:param approval: 审批实例
|
||||||
|
:param deferred_tool_results: 延迟工具调用结果
|
||||||
:return: AsyncGenerator
|
:return: AsyncGenerator
|
||||||
"""
|
"""
|
||||||
# 当前会话
|
# 当前会话
|
||||||
|
|
@ -280,36 +277,43 @@ class ConversationState(rx.State):
|
||||||
if not conversation:
|
if not conversation:
|
||||||
return
|
return
|
||||||
|
|
||||||
# 若用户提示词为空则直接返回
|
|
||||||
if not conversation.user_prompt:
|
|
||||||
return
|
|
||||||
|
|
||||||
# 将正在运行设置为是
|
|
||||||
conversation.is_running = True
|
|
||||||
# 将等待流式输出设置为是
|
|
||||||
conversation.awaiting_stream = True
|
|
||||||
yield # 通知前端更新渲染
|
|
||||||
|
|
||||||
# 获取数据库状态
|
# 获取数据库状态
|
||||||
db_state = await self.get_db_state()
|
db_state = await self.get_db_state()
|
||||||
# 更新会话记录
|
|
||||||
await db_state.update_conversation_record(
|
# 无审批实例
|
||||||
conversation.id,
|
if not approval:
|
||||||
workflow=conversation.workflow,
|
# 若用户提示词为空则直接返回
|
||||||
)
|
if not conversation.user_prompt:
|
||||||
# 先创建消息记录再添加消息实例
|
return
|
||||||
conversation.messages.update(
|
|
||||||
await db_state.create_message_record(
|
# 将正在运行设置为是
|
||||||
conversation_id=conversation.id,
|
conversation.is_running = True
|
||||||
message=Message(
|
# 将等待流式输出设置为是
|
||||||
type=MessageType.USER_PROMPT,
|
conversation.awaiting_stream = True
|
||||||
content=(user_prompt := conversation.user_prompt),
|
yield # 通知前端更新渲染
|
||||||
),
|
|
||||||
|
# 先创建消息记录再添加消息实例
|
||||||
|
conversation.messages.update(
|
||||||
|
await db_state.create_message_record(
|
||||||
|
conversation_id=conversation.id,
|
||||||
|
message=Message(
|
||||||
|
type=MessageType.USER_PROMPT,
|
||||||
|
content=(user_prompt := conversation.user_prompt),
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
# 清空用户提示词
|
||||||
# 清空用户提示词
|
conversation.user_prompt = ""
|
||||||
conversation.user_prompt = ""
|
yield # 通知前端更新渲染
|
||||||
yield # 通知前端更新渲染
|
|
||||||
|
# 有审批实例
|
||||||
|
else:
|
||||||
|
if not (user_prompt := approval.user_prompt):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 将等待流式输出设置为是
|
||||||
|
conversation.awaiting_stream = True
|
||||||
|
yield # 通知前端更新渲染
|
||||||
|
|
||||||
# 初始化片段索引映射为消息实例唯一标识字典
|
# 初始化片段索引映射为消息实例唯一标识字典
|
||||||
index_map_to_message_id: dict[int, str] = {}
|
index_map_to_message_id: dict[int, str] = {}
|
||||||
|
|
@ -328,10 +332,11 @@ class ConversationState(rx.State):
|
||||||
stream_events = run_stream_events(
|
stream_events = run_stream_events(
|
||||||
deps=conversation.workflow.deps,
|
deps=conversation.workflow.deps,
|
||||||
user_prompt=user_prompt,
|
user_prompt=user_prompt,
|
||||||
message_history=await db_state.get_message_history(
|
message_history=await db_state.read_message_history(
|
||||||
conversation_id=self.actived_conversation_id
|
conversation_id=self.actived_conversation_id
|
||||||
),
|
),
|
||||||
usage=conversation.usage,
|
usage=conversation.usage,
|
||||||
|
deferred_tool_results=deferred_tool_results,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
from application.workshop.talk import (
|
from application.workshop.talk import (
|
||||||
|
|
@ -341,7 +346,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=await db_state.get_message_history(
|
message_history=await db_state.read_message_history(
|
||||||
conversation_id=self.actived_conversation_id
|
conversation_id=self.actived_conversation_id
|
||||||
),
|
),
|
||||||
usage=conversation.usage,
|
usage=conversation.usage,
|
||||||
|
|
@ -469,15 +474,17 @@ class ConversationState(rx.State):
|
||||||
|
|
||||||
# ========== 智能体运行结果事件 ==========
|
# ========== 智能体运行结果事件 ==========
|
||||||
case AgentRunResultEvent(result=result):
|
case AgentRunResultEvent(result=result):
|
||||||
if isinstance(result.output, DeferredToolRequests):
|
if isinstance(output := result.output, DeferredToolRequests):
|
||||||
for approval in result.output.approvals:
|
for approval_ in output.approvals:
|
||||||
# 获取消息实例
|
# 获取消息实例
|
||||||
message = conversation.messages[
|
message = conversation.messages[
|
||||||
tool_name_map_to_message_id[approval.tool_name]
|
tool_name_map_to_message_id[approval_.tool_name]
|
||||||
]
|
]
|
||||||
|
message.content = output.metadata[
|
||||||
|
tool_call_id := approval_.tool_call_id
|
||||||
|
]["content"]
|
||||||
message.approval = Approval(
|
message.approval = Approval(
|
||||||
tool_call_id=approval.tool_call_id,
|
tool_call_id=tool_call_id,
|
||||||
interaction_type=approval.interaction_type,
|
|
||||||
)
|
)
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
else:
|
else:
|
||||||
|
|
@ -499,7 +506,7 @@ class ConversationState(rx.State):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.conversations[self.actived_conversation_id] = conversation
|
self.conversations[self.actived_conversation_id] = conversation
|
||||||
yield
|
yield # 通知前端更新渲染
|
||||||
|
|
||||||
# 批量创建消息记录
|
# 批量创建消息记录
|
||||||
await db_state.create_message_records(
|
await db_state.create_message_records(
|
||||||
|
|
@ -507,6 +514,88 @@ class ConversationState(rx.State):
|
||||||
messages=messages,
|
messages=messages,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@rx.var
|
||||||
|
def is_running(self) -> bool:
|
||||||
|
"""
|
||||||
|
当前会话正在运行
|
||||||
|
:return: 当前会话正在运行(True 表示正在运行,False 表示运行完成)
|
||||||
|
"""
|
||||||
|
# 当前会话
|
||||||
|
conversation = self.conversations.get(self.actived_conversation_id)
|
||||||
|
if not conversation:
|
||||||
|
return False
|
||||||
|
return conversation.is_running
|
||||||
|
|
||||||
|
@rx.var
|
||||||
|
def awaiting_stream(self) -> bool:
|
||||||
|
"""
|
||||||
|
当前会话正在等待流式输出
|
||||||
|
:return: 当前会话正在等待流式输出(True 表示等待流式输出,False 表示已开始流式输出或已完成)
|
||||||
|
"""
|
||||||
|
# 当前会话
|
||||||
|
conversation = self.conversations.get(self.actived_conversation_id)
|
||||||
|
if not conversation:
|
||||||
|
return False
|
||||||
|
return conversation.awaiting_stream
|
||||||
|
|
||||||
|
@rx.event
|
||||||
|
def toggle_message_history_item_shown(self, item_id: str) -> None:
|
||||||
|
"""
|
||||||
|
展示 / 隐藏消息历史项
|
||||||
|
"""
|
||||||
|
# 当前会话
|
||||||
|
conversation = self.conversations.get(self.actived_conversation_id)
|
||||||
|
if not conversation:
|
||||||
|
return
|
||||||
|
conversation.messages[item_id].is_shown ^= True
|
||||||
|
|
||||||
|
@rx.event
|
||||||
|
def set_approval_user_prompt(self, item_id: str, user_prompt: str) -> None:
|
||||||
|
"""
|
||||||
|
设置审批用户提示词
|
||||||
|
:param item_id: 消息历史项唯一标识
|
||||||
|
:param user_prompt: 用户提示词
|
||||||
|
"""
|
||||||
|
# 当前会话
|
||||||
|
conversation = self.conversations.get(self.actived_conversation_id)
|
||||||
|
if not conversation:
|
||||||
|
return
|
||||||
|
# 获取消息实例
|
||||||
|
message = conversation.messages.get(item_id)
|
||||||
|
if not message or not isinstance(message.approval, Approval):
|
||||||
|
return
|
||||||
|
message.approval.user_prompt = user_prompt
|
||||||
|
|
||||||
|
@rx.event
|
||||||
|
async def approve(self, item_id: str, result: bool) -> None:
|
||||||
|
"""
|
||||||
|
审批消息历史项
|
||||||
|
:param item_id: 消息历史项唯一标识
|
||||||
|
:param result: 审批结果(True 表示审批通过,False 表示审批拒绝)
|
||||||
|
"""
|
||||||
|
# 当前会话
|
||||||
|
conversation = self.conversations.get(self.actived_conversation_id)
|
||||||
|
if not conversation:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取审批实例
|
||||||
|
if not (message := conversation.messages.get(item_id)) or not isinstance(
|
||||||
|
approval := message.approval, Approval
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 更新审批结果
|
||||||
|
approval.result = result
|
||||||
|
# 运行
|
||||||
|
await self.run(
|
||||||
|
approval,
|
||||||
|
DeferredToolResults(
|
||||||
|
approvals={
|
||||||
|
approval.tool_call_id: ToolApproved() if result else ToolDenied()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
async def set_workflow(self, type: WorkflowType) -> None:
|
async def set_workflow(self, type: WorkflowType) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -529,14 +618,10 @@ class ConversationState(rx.State):
|
||||||
)
|
)
|
||||||
|
|
||||||
conversation.workflow = workflow
|
conversation.workflow = workflow
|
||||||
|
# 获取数据库状态
|
||||||
@rx.event
|
db_state = await self.get_db_state()
|
||||||
def toggle_message_history_item_shown(self, message_id: str) -> None:
|
# 更新会话记录
|
||||||
"""
|
await db_state.update_conversation_record(
|
||||||
展示 / 隐藏消息历史项
|
conversation.id,
|
||||||
"""
|
workflow=conversation.workflow,
|
||||||
# 当前会话
|
)
|
||||||
conversation = self.conversations.get(self.actived_conversation_id)
|
|
||||||
if not conversation:
|
|
||||||
return
|
|
||||||
conversation.messages[message_id].is_shown ^= True
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ from application.states.models import (
|
||||||
usage_validate_json,
|
usage_validate_json,
|
||||||
workflow_dump_json,
|
workflow_dump_json,
|
||||||
workflow_validate_json,
|
workflow_validate_json,
|
||||||
|
approval_dump_json,
|
||||||
|
approval_validate_json,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -101,7 +103,7 @@ class RunRecord(SQLModel, table=True):
|
||||||
description="运行唯一标识",
|
description="运行唯一标识",
|
||||||
)
|
)
|
||||||
conversation_id: str = Field(..., index=True, description="会话唯一标识")
|
conversation_id: str = Field(..., index=True, description="会话唯一标识")
|
||||||
new_messages: str = Field(default="", sa_type=JSON, description="新增消息")
|
new_messages: str = Field(..., description="新增消息")
|
||||||
|
|
||||||
|
|
||||||
class MessageRecord(SQLModel, table=True):
|
class MessageRecord(SQLModel, table=True):
|
||||||
|
|
@ -118,6 +120,9 @@ class MessageRecord(SQLModel, table=True):
|
||||||
type: MessageType = Field(..., description="消息类型")
|
type: MessageType = Field(..., description="消息类型")
|
||||||
title: str = Field(default="", description="消息标题")
|
title: str = Field(default="", description="消息标题")
|
||||||
content: str = Field(default="", description="消息内容")
|
content: str = Field(default="", description="消息内容")
|
||||||
|
approval: str = Field(
|
||||||
|
default="", description="审批"
|
||||||
|
) # 原则上嵌套列表或字典若不涉及查询则以字符串储存
|
||||||
|
|
||||||
|
|
||||||
class DatabaseState(rx.State):
|
class DatabaseState(rx.State):
|
||||||
|
|
@ -197,9 +202,31 @@ class DatabaseState(rx.State):
|
||||||
await session.refresh(record)
|
await session.refresh(record)
|
||||||
return record.id
|
return record.id
|
||||||
|
|
||||||
async def get_conversations(self, user_id: str) -> dict[str, Conversation]:
|
async def create_conversation_record(self, user_id: str) -> dict[str, Conversation]:
|
||||||
"""
|
"""
|
||||||
获取当前用户的会话字典
|
创建会话记录
|
||||||
|
:param user_id: 用户唯一标识
|
||||||
|
:return: 会话实例
|
||||||
|
"""
|
||||||
|
async with rx.asession() as session:
|
||||||
|
record = ConversationRecord(user_id=user_id)
|
||||||
|
session.add(record)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(record)
|
||||||
|
return {
|
||||||
|
record.id: Conversation(
|
||||||
|
id=record.id,
|
||||||
|
description=record.description,
|
||||||
|
usage=usage_validate_json(
|
||||||
|
record.usage
|
||||||
|
), # 将 record.usage 由 JSON 字符串反序列化为 RunUsage
|
||||||
|
created_at=record.created_at,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async def read_conversations(self, user_id: str) -> dict[str, Conversation]:
|
||||||
|
"""
|
||||||
|
查询当前用户的会话字典
|
||||||
:param user_id: 用户唯一标识
|
:param user_id: 用户唯一标识
|
||||||
:return: 当前用户的会话字典
|
:return: 当前用户的会话字典
|
||||||
"""
|
"""
|
||||||
|
|
@ -241,33 +268,11 @@ class DatabaseState(rx.State):
|
||||||
type=message_record.type,
|
type=message_record.type,
|
||||||
title=message_record.title,
|
title=message_record.title,
|
||||||
content=message_record.content,
|
content=message_record.content,
|
||||||
|
approval=approval_validate_json(message_record.approval),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return conversations
|
return conversations
|
||||||
|
|
||||||
async def create_conversation_record(self, user_id: str) -> dict[str, Conversation]:
|
|
||||||
"""
|
|
||||||
创建会话记录
|
|
||||||
:param user_id: 用户唯一标识
|
|
||||||
:return: 会话实例
|
|
||||||
"""
|
|
||||||
async with rx.asession() as session:
|
|
||||||
record = ConversationRecord(user_id=user_id)
|
|
||||||
session.add(record)
|
|
||||||
await session.commit()
|
|
||||||
await session.refresh(record)
|
|
||||||
return {
|
|
||||||
record.id: Conversation(
|
|
||||||
id=record.id,
|
|
||||||
description=record.description,
|
|
||||||
usage=usage_validate_json(
|
|
||||||
record.usage
|
|
||||||
), # 将 record.usage 由 JSON 字符串反序列化为 RunUsage
|
|
||||||
messages={}, # 新会话默认消息字典为空
|
|
||||||
created_at=record.created_at,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async def update_conversation_record(
|
async def update_conversation_record(
|
||||||
self,
|
self,
|
||||||
conversation_id: str,
|
conversation_id: str,
|
||||||
|
|
@ -321,6 +326,7 @@ class DatabaseState(rx.State):
|
||||||
type=message.type,
|
type=message.type,
|
||||||
title=message.title,
|
title=message.title,
|
||||||
content=message.content,
|
content=message.content,
|
||||||
|
approval=approval_dump_json(message.approval),
|
||||||
)
|
)
|
||||||
session.add(record)
|
session.add(record)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
@ -335,8 +341,8 @@ class DatabaseState(rx.State):
|
||||||
"""
|
"""
|
||||||
创建消息记录
|
创建消息记录
|
||||||
:param conversation_id: 会话唯一标识
|
:param conversation_id: 会话唯一标识
|
||||||
:param message: 消息实例
|
:param messages: 消息实例列表
|
||||||
:return: 消息实例
|
:return: None
|
||||||
"""
|
"""
|
||||||
async with rx.asession() as session:
|
async with rx.asession() as session:
|
||||||
for message in messages:
|
for message in messages:
|
||||||
|
|
@ -346,13 +352,14 @@ class DatabaseState(rx.State):
|
||||||
type=message.type,
|
type=message.type,
|
||||||
title=message.title,
|
title=message.title,
|
||||||
content=message.content,
|
content=message.content,
|
||||||
|
approval=approval_dump_json(message.approval),
|
||||||
)
|
)
|
||||||
session.add(record)
|
session.add(record)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
async def get_message_history(self, conversation_id: str) -> list[ModelMessage]:
|
async def read_message_history(self, conversation_id: str) -> list[ModelMessage]:
|
||||||
"""
|
"""
|
||||||
获取消息历史列表
|
查询消息历史列表
|
||||||
:param conversation_id: 会话唯一标识
|
:param conversation_id: 会话唯一标识
|
||||||
:return: 消息历史
|
:return: 消息历史
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -19,23 +19,6 @@ class NoResult(BaseModel):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class InteractionType(StrEnum):
|
|
||||||
"""
|
|
||||||
交互类型枚举
|
|
||||||
"""
|
|
||||||
|
|
||||||
INPUT = "input"
|
|
||||||
|
|
||||||
|
|
||||||
class Approval(BaseModel):
|
|
||||||
"""
|
|
||||||
审批类
|
|
||||||
"""
|
|
||||||
|
|
||||||
tool_call_id: str = Field(..., description="工具调用唯一标识")
|
|
||||||
interaction_type: InteractionType = Field(..., description="交互类型")
|
|
||||||
|
|
||||||
|
|
||||||
class MessageType(StrEnum):
|
class MessageType(StrEnum):
|
||||||
"""
|
"""
|
||||||
消息类型枚举
|
消息类型枚举
|
||||||
|
|
@ -47,6 +30,19 @@ class MessageType(StrEnum):
|
||||||
TEXT = "text"
|
TEXT = "text"
|
||||||
|
|
||||||
|
|
||||||
|
class Approval(BaseModel):
|
||||||
|
"""
|
||||||
|
审批类
|
||||||
|
"""
|
||||||
|
|
||||||
|
tool_call_id: str = Field(..., description="工具调用唯一标识")
|
||||||
|
user_prompt: str = Field(default="", description="用户提示词")
|
||||||
|
result: bool | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="审批结果,True 表示审批通过,False 表示审批拒绝,None 表示待审批",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Message(BaseModel):
|
class Message(BaseModel):
|
||||||
"""
|
"""
|
||||||
消息类
|
消息类
|
||||||
|
|
@ -118,6 +114,9 @@ class MessageHistoryItem(Message):
|
||||||
消息历史项类
|
消息历史项类
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
has_approval: bool = Field(default=False, description="包含审批实例")
|
||||||
|
approval_user_prompt: str = Field(default="", description="审批用户提示词")
|
||||||
|
|
||||||
|
|
||||||
# 工作流适配器
|
# 工作流适配器
|
||||||
WorkflowAdapter = TypeAdapter(Any)
|
WorkflowAdapter = TypeAdapter(Any)
|
||||||
|
|
@ -161,3 +160,27 @@ def usage_dump_json(usage: RunUsage) -> str:
|
||||||
:return: 字符串
|
:return: 字符串
|
||||||
"""
|
"""
|
||||||
return UsageAdapter.dump_json(usage).decode("utf-8")
|
return UsageAdapter.dump_json(usage).decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
# 审批适配器
|
||||||
|
ApprovalAdapter = TypeAdapter(Approval)
|
||||||
|
|
||||||
|
|
||||||
|
def approval_validate_json(approval: str) -> Approval | None:
|
||||||
|
"""
|
||||||
|
将审批反序列化
|
||||||
|
"""
|
||||||
|
if not approval:
|
||||||
|
return None
|
||||||
|
return ApprovalAdapter.validate_json(approval)
|
||||||
|
|
||||||
|
|
||||||
|
def approval_dump_json(approval: Approval | None) -> str:
|
||||||
|
"""
|
||||||
|
将审批序列化
|
||||||
|
:param approval: 审批
|
||||||
|
:return: 字符串
|
||||||
|
"""
|
||||||
|
if approval is None:
|
||||||
|
return ""
|
||||||
|
return ApprovalAdapter.dump_json(approval).decode("utf-8")
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ from pydantic_ai.run import AgentRunResult, AgentRunResultEvent
|
||||||
|
|
||||||
from application.states.models import (
|
from application.states.models import (
|
||||||
NoResult,
|
NoResult,
|
||||||
InteractionType,
|
|
||||||
Approval,
|
Approval,
|
||||||
)
|
)
|
||||||
from application.workshop.models import (
|
from application.workshop.models import (
|
||||||
|
|
@ -207,7 +206,6 @@ async def search_flights(ctx: RunContext[Deps]) -> list[Flight]:
|
||||||
|
|
||||||
@agent.tool(
|
@agent.tool(
|
||||||
name="预定航班",
|
name="预定航班",
|
||||||
requires_approval=True,
|
|
||||||
)
|
)
|
||||||
async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult:
|
async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult:
|
||||||
"""
|
"""
|
||||||
|
|
@ -216,6 +214,9 @@ async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult:
|
||||||
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:
|
||||||
|
raise ApprovalRequired(metadata={"content": "您要预定哪班航班?"})
|
||||||
|
|
||||||
# 提取到的航班号
|
# 提取到的航班号
|
||||||
extracted_flight_number = (
|
extracted_flight_number = (
|
||||||
await extraction_flight_number_agent.run(
|
await extraction_flight_number_agent.run(
|
||||||
|
|
@ -298,6 +299,7 @@ async def run_stream_events(
|
||||||
deferred_tool_results=deferred_tool_results,
|
deferred_tool_results=deferred_tool_results,
|
||||||
) as events:
|
) as events:
|
||||||
async for event in events:
|
async for event in events:
|
||||||
|
print(event)
|
||||||
match event:
|
match event:
|
||||||
case PartStartEvent(
|
case PartStartEvent(
|
||||||
part=part,
|
part=part,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue