This commit is contained in:
parent
606ad01352
commit
31828a7be3
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"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 = (
|
||||
item.id == ConversationState.is_conversation_history_item_popover_shown
|
||||
item.id == ConversationState.popover_shown_conversation_history_item_id
|
||||
) | (item.id == ConversationState.actived_conversation_id)
|
||||
|
||||
return rx.list.item(
|
||||
|
|
@ -425,7 +425,7 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
|
|||
:return: Component
|
||||
"""
|
||||
return rx.vstack(
|
||||
# 工具调用的标题栏
|
||||
# 标题栏
|
||||
rx.hstack(
|
||||
rx.icon(
|
||||
"square-function",
|
||||
|
|
@ -444,7 +444,7 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
|
|||
margin_bottom="8px",
|
||||
line_height="22px",
|
||||
),
|
||||
# 工具调用的内容
|
||||
# 内容
|
||||
rx.markdown(
|
||||
item.content,
|
||||
width="100%",
|
||||
|
|
@ -453,6 +453,54 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
|
|||
font_size="var(--prismui-font-size-1)",
|
||||
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,
|
||||
# 点击事件:运行
|
||||
on_click=ConversationState.run,
|
||||
on_click=lambda: ConversationState.run(),
|
||||
display="inline-flex",
|
||||
position="relative",
|
||||
justify_content="center",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,12 @@
|
|||
"""
|
||||
from datetime import datetime
|
||||
from typing import AsyncGenerator, cast
|
||||
from pydantic_ai import DeferredToolRequests
|
||||
from pydantic_ai import (
|
||||
DeferredToolRequests,
|
||||
DeferredToolResults,
|
||||
ToolApproved,
|
||||
ToolDenied,
|
||||
)
|
||||
|
||||
from pydantic_ai.messages import (
|
||||
FunctionToolCallEvent,
|
||||
|
|
@ -67,13 +72,13 @@ class ConversationState(rx.State):
|
|||
|
||||
# 当前用户的会话字典
|
||||
conversations: dict[str, Conversation] = {} # 键为会话唯一标识,值为会话实例
|
||||
# 激活会话唯一标识
|
||||
# 激活的会话唯一标识
|
||||
actived_conversation_id: str = ""
|
||||
|
||||
# 显示会话历史,True 表示显示,False 表示隐藏
|
||||
is_conversation_history_shown: bool = False
|
||||
# 会话历史项显示悬停气泡
|
||||
is_conversation_history_item_popover_shown: str = ""
|
||||
# 悬停气泡显示的会话历史项唯一标识(一个会话只允许存在一个悬停气泡会话历史项)
|
||||
popover_shown_conversation_history_item_id: str = ""
|
||||
|
||||
# 当前数据库状态(私有变量)
|
||||
# 私有变量:reflex 约定以 _ 开头的变量为私有变量,后端不序列化,前端不可使用
|
||||
|
|
@ -101,8 +106,8 @@ class ConversationState(rx.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:
|
||||
self.conversations.update(
|
||||
|
|
@ -136,15 +141,13 @@ class ConversationState(rx.State):
|
|||
|
||||
@rx.event
|
||||
def set_conversation_history_item_popover_shown(
|
||||
self, conversation_id: str, is_shown: bool
|
||||
self, item_id: str, is_shown: bool
|
||||
) -> None:
|
||||
"""
|
||||
设置会话历史项悬停气泡显示 / 隐藏
|
||||
:return: None
|
||||
"""
|
||||
self.is_conversation_history_item_popover_shown = (
|
||||
conversation_id if is_shown else ""
|
||||
)
|
||||
self.popover_shown_conversation_history_item_id = item_id if is_shown else ""
|
||||
|
||||
@rx.event
|
||||
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)
|
||||
if not conversation:
|
||||
return []
|
||||
return [
|
||||
MessageHistoryItem(**message.model_dump())
|
||||
for message in list(conversation.messages.values())
|
||||
]
|
||||
message_history: list[MessageHistoryItem] = []
|
||||
has_approval = False
|
||||
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
|
||||
async def create_conversation(self) -> None:
|
||||
|
|
@ -245,34 +260,16 @@ class ConversationState(rx.State):
|
|||
return True
|
||||
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
|
||||
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
|
||||
"""
|
||||
# 当前会话
|
||||
|
|
@ -280,6 +277,11 @@ class ConversationState(rx.State):
|
|||
if not conversation:
|
||||
return
|
||||
|
||||
# 获取数据库状态
|
||||
db_state = await self.get_db_state()
|
||||
|
||||
# 无审批实例
|
||||
if not approval:
|
||||
# 若用户提示词为空则直接返回
|
||||
if not conversation.user_prompt:
|
||||
return
|
||||
|
|
@ -290,13 +292,6 @@ class ConversationState(rx.State):
|
|||
conversation.awaiting_stream = True
|
||||
yield # 通知前端更新渲染
|
||||
|
||||
# 获取数据库状态
|
||||
db_state = await self.get_db_state()
|
||||
# 更新会话记录
|
||||
await db_state.update_conversation_record(
|
||||
conversation.id,
|
||||
workflow=conversation.workflow,
|
||||
)
|
||||
# 先创建消息记录再添加消息实例
|
||||
conversation.messages.update(
|
||||
await db_state.create_message_record(
|
||||
|
|
@ -311,6 +306,15 @@ class ConversationState(rx.State):
|
|||
conversation.user_prompt = ""
|
||||
yield # 通知前端更新渲染
|
||||
|
||||
# 有审批实例
|
||||
else:
|
||||
if not (user_prompt := approval.user_prompt):
|
||||
return
|
||||
|
||||
# 将等待流式输出设置为是
|
||||
conversation.awaiting_stream = True
|
||||
yield # 通知前端更新渲染
|
||||
|
||||
# 初始化片段索引映射为消息实例唯一标识字典
|
||||
index_map_to_message_id: dict[int, str] = {}
|
||||
# 初始化工具调用片段工具名称映射为消息实例唯一标识字典
|
||||
|
|
@ -328,10 +332,11 @@ class ConversationState(rx.State):
|
|||
stream_events = run_stream_events(
|
||||
deps=conversation.workflow.deps,
|
||||
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
|
||||
),
|
||||
usage=conversation.usage,
|
||||
deferred_tool_results=deferred_tool_results,
|
||||
)
|
||||
else:
|
||||
from application.workshop.talk import (
|
||||
|
|
@ -341,7 +346,7 @@ class ConversationState(rx.State):
|
|||
# 运行并流式输出事件
|
||||
stream_events = run_stream_events(
|
||||
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
|
||||
),
|
||||
usage=conversation.usage,
|
||||
|
|
@ -469,15 +474,17 @@ class ConversationState(rx.State):
|
|||
|
||||
# ========== 智能体运行结果事件 ==========
|
||||
case AgentRunResultEvent(result=result):
|
||||
if isinstance(result.output, DeferredToolRequests):
|
||||
for approval in result.output.approvals:
|
||||
if isinstance(output := result.output, DeferredToolRequests):
|
||||
for approval_ in output.approvals:
|
||||
# 获取消息实例
|
||||
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(
|
||||
tool_call_id=approval.tool_call_id,
|
||||
interaction_type=approval.interaction_type,
|
||||
tool_call_id=tool_call_id,
|
||||
)
|
||||
messages.append(message)
|
||||
else:
|
||||
|
|
@ -499,7 +506,7 @@ class ConversationState(rx.State):
|
|||
)
|
||||
|
||||
self.conversations[self.actived_conversation_id] = conversation
|
||||
yield
|
||||
yield # 通知前端更新渲染
|
||||
|
||||
# 批量创建消息记录
|
||||
await db_state.create_message_records(
|
||||
|
|
@ -507,6 +514,88 @@ class ConversationState(rx.State):
|
|||
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
|
||||
async def set_workflow(self, type: WorkflowType) -> None:
|
||||
"""
|
||||
|
|
@ -529,14 +618,10 @@ class ConversationState(rx.State):
|
|||
)
|
||||
|
||||
conversation.workflow = workflow
|
||||
|
||||
@rx.event
|
||||
def toggle_message_history_item_shown(self, message_id: str) -> None:
|
||||
"""
|
||||
展示 / 隐藏消息历史项
|
||||
"""
|
||||
# 当前会话
|
||||
conversation = self.conversations.get(self.actived_conversation_id)
|
||||
if not conversation:
|
||||
return
|
||||
conversation.messages[message_id].is_shown ^= True
|
||||
# 获取数据库状态
|
||||
db_state = await self.get_db_state()
|
||||
# 更新会话记录
|
||||
await db_state.update_conversation_record(
|
||||
conversation.id,
|
||||
workflow=conversation.workflow,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ from application.states.models import (
|
|||
usage_validate_json,
|
||||
workflow_dump_json,
|
||||
workflow_validate_json,
|
||||
approval_dump_json,
|
||||
approval_validate_json,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -101,7 +103,7 @@ class RunRecord(SQLModel, table=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):
|
||||
|
|
@ -118,6 +120,9 @@ class MessageRecord(SQLModel, table=True):
|
|||
type: MessageType = Field(..., description="消息类型")
|
||||
title: str = Field(default="", description="消息标题")
|
||||
content: str = Field(default="", description="消息内容")
|
||||
approval: str = Field(
|
||||
default="", description="审批"
|
||||
) # 原则上嵌套列表或字典若不涉及查询则以字符串储存
|
||||
|
||||
|
||||
class DatabaseState(rx.State):
|
||||
|
|
@ -197,9 +202,31 @@ class DatabaseState(rx.State):
|
|||
await session.refresh(record)
|
||||
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: 用户唯一标识
|
||||
:return: 当前用户的会话字典
|
||||
"""
|
||||
|
|
@ -241,33 +268,11 @@ class DatabaseState(rx.State):
|
|||
type=message_record.type,
|
||||
title=message_record.title,
|
||||
content=message_record.content,
|
||||
approval=approval_validate_json(message_record.approval),
|
||||
),
|
||||
)
|
||||
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(
|
||||
self,
|
||||
conversation_id: str,
|
||||
|
|
@ -321,6 +326,7 @@ class DatabaseState(rx.State):
|
|||
type=message.type,
|
||||
title=message.title,
|
||||
content=message.content,
|
||||
approval=approval_dump_json(message.approval),
|
||||
)
|
||||
session.add(record)
|
||||
await session.commit()
|
||||
|
|
@ -335,8 +341,8 @@ class DatabaseState(rx.State):
|
|||
"""
|
||||
创建消息记录
|
||||
:param conversation_id: 会话唯一标识
|
||||
:param message: 消息实例
|
||||
:return: 消息实例
|
||||
:param messages: 消息实例列表
|
||||
:return: None
|
||||
"""
|
||||
async with rx.asession() as session:
|
||||
for message in messages:
|
||||
|
|
@ -346,13 +352,14 @@ class DatabaseState(rx.State):
|
|||
type=message.type,
|
||||
title=message.title,
|
||||
content=message.content,
|
||||
approval=approval_dump_json(message.approval),
|
||||
)
|
||||
session.add(record)
|
||||
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: 会话唯一标识
|
||||
: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):
|
||||
"""
|
||||
消息类型枚举
|
||||
|
|
@ -47,6 +30,19 @@ class MessageType(StrEnum):
|
|||
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):
|
||||
"""
|
||||
消息类
|
||||
|
|
@ -118,6 +114,9 @@ class MessageHistoryItem(Message):
|
|||
消息历史项类
|
||||
"""
|
||||
|
||||
has_approval: bool = Field(default=False, description="包含审批实例")
|
||||
approval_user_prompt: str = Field(default="", description="审批用户提示词")
|
||||
|
||||
|
||||
# 工作流适配器
|
||||
WorkflowAdapter = TypeAdapter(Any)
|
||||
|
|
@ -161,3 +160,27 @@ def usage_dump_json(usage: RunUsage) -> str:
|
|||
:return: 字符串
|
||||
"""
|
||||
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 (
|
||||
NoResult,
|
||||
InteractionType,
|
||||
Approval,
|
||||
)
|
||||
from application.workshop.models import (
|
||||
|
|
@ -207,7 +206,6 @@ async def search_flights(ctx: RunContext[Deps]) -> list[Flight]:
|
|||
|
||||
@agent.tool(
|
||||
name="预定航班",
|
||||
requires_approval=True,
|
||||
)
|
||||
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:
|
||||
raise ModelRetry("必须先使用 search_flights 查询航班")
|
||||
|
||||
if not ctx.tool_call_approved:
|
||||
raise ApprovalRequired(metadata={"content": "您要预定哪班航班?"})
|
||||
|
||||
# 提取到的航班号
|
||||
extracted_flight_number = (
|
||||
await extraction_flight_number_agent.run(
|
||||
|
|
@ -298,6 +299,7 @@ async def run_stream_events(
|
|||
deferred_tool_results=deferred_tool_results,
|
||||
) as events:
|
||||
async for event in events:
|
||||
print(event)
|
||||
match event:
|
||||
case PartStartEvent(
|
||||
part=part,
|
||||
|
|
|
|||
Loading…
Reference in New Issue