This commit is contained in:
liubiren 2026-07-03 18:04:14 +08:00
parent 01eb3befb0
commit c499b16c3b
2 changed files with 75 additions and 152 deletions

View File

@ -69,9 +69,23 @@ class PartKind(StrEnum):
RUN_RETURN = "run-return" RUN_RETURN = "run-return"
class Kind(StrEnum):
"""分段种类"""
THINKING = "thinking"
TOOL_SEARCH = "tool-search"
CAPABILITY_LOAD = "capability-load"
TOOL_CALL = "tool-call"
TEXT = "text"
TOOL_RETURN = "tool-return"
RETRY_PROMPT = "retry-prompt"
RUN_RETURN = "run-return"
class Reasoning(BaseModel): class Reasoning(BaseModel):
"""推理类""" """推理类"""
kind: Kind = Field(..., description="推理类型")
content: str = Field(default="", description="推理内容") content: str = Field(default="", description="推理内容")
@ -84,11 +98,7 @@ class Run(BaseModel):
) )
is_reasoning: bool = Field( is_reasoning: bool = Field(
default=False, default=False,
description="推理状态True 表示正在推理False 表示非正在推理", description="正在推理状态True 表示正在推理False 表示非正在推理",
)
is_collapsed: bool = Field(
default=False,
description="推理面板折叠状态True 表示折叠False 表示展开",
) )
assistant_content: str = Field(default="", description="回复正文") assistant_content: str = Field(default="", description="回复正文")
is_streaming: bool = Field( is_streaming: bool = Field(

View File

@ -25,7 +25,7 @@ from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.openai import OpenAIProvider from pydantic_ai.providers.openai import OpenAIProvider
import reflex as rx import reflex as rx
from application.models import Conversation, EventKind, PartKind, Run, Event, EventKind, PartKind from application.models import Conversation, EventKind, PartKind, Run, Reasoning, Kind
from application.state.create_conversation_modal import CreateConversationModalState from application.state.create_conversation_modal import CreateConversationModalState
from application.state.database import DatabaseState from application.state.database import DatabaseState
@ -52,15 +52,10 @@ class ConversationState(rx.State):
对话状态 对话状态
""" """
# 初始化当前对话唯一标识
conversation_id: str = str(uuid7())
# 初始化对话字典 # 初始化对话字典
conversations: Dict[str, Conversation] = {conversation_id: Conversation()} conversations: Dict[str, Conversation] = {str(uuid7()): Conversation()}
# 当前对话唯一标识
# 初始化当前运行唯一标识 conversation_id: str = next(reversed(conversations.keys()))
run_id: Optional[str] = None
# 初始化当前运行工具调用唯一标识与片段索引映射表
run_mapping: dict[str, int] = {}
# 初始化智能体 # 初始化智能体
agent: Agent = Agent( agent: Agent = Agent(
@ -97,12 +92,10 @@ class ConversationState(rx.State):
if not description: if not description:
description = "新对话" description = "新对话"
# 生成并设置为当前对话唯一标识
self.conversation_id = str(uuid7())
# 创建对话 # 创建对话
self.conversations[self.conversation_id] = Conversation(description=description) self.conversations[str(uuid7())] = Conversation(description=description)
# 初始化当前运行唯一标识 # 将末位对话唯一标识设置为当前对话唯一标识
self.run_id = None self.conversation_id = next(reversed(self.conversations.keys()))
# 获取创建对话模态窗状态 # 获取创建对话模态窗状态
create_conversation_modal_state = await self.get_state( create_conversation_modal_state = await self.get_state(
@ -122,24 +115,18 @@ class ConversationState(rx.State):
return return
del self.conversations[conversation_id] del self.conversations[conversation_id]
# 删除后,若对话字典为空则创建对话
if not self.conversations: if not self.conversations:
# 生成并设置为当前对话唯一标识 self.conversations[str(uuid7())] = Conversation()
self.conversation_id = str(uuid7())
# 创建对话
self.conversations[self.conversation_id] = Conversation()
# 初始化当前运行唯一标识
self.run_id = None
# 删除后,若当前对话唯一标识不存在则将末位对话唯一标识设置为当前对话唯一标识
if self.conversation_id not in self.conversations: if self.conversation_id not in self.conversations:
# 将末位对话唯一标识设置为当前对话唯一标识
self.conversation_id = next(reversed(self.conversations.keys())) self.conversation_id = next(reversed(self.conversations.keys()))
# 初始化当前运行唯一标识
self.run_id = next(reversed(self.conversations[self.conversation_id].runs.keys()), None)
@rx.event @rx.event
def switch_conversation(self, conversation_id: str) -> None: def switch_conversation(self, conversation_id: str) -> None:
""" """
设置为当前对话唯一标识 将指定对话唯一标识设置为当前对话唯一标识
:param conversation_id: 指定对话唯一标识 :param conversation_id: 指定对话唯一标识
:return: None :return: None
""" """
@ -194,9 +181,6 @@ class ConversationState(rx.State):
if not conversation: if not conversation:
return return
# 更新当前运行唯一标识
self.run_id = str(uuid7())
# 获取用户提示词 # 获取用户提示词
user_prompt = form_data.get("user_prompt", "").strip() user_prompt = form_data.get("user_prompt", "").strip()
if not user_prompt: if not user_prompt:
@ -209,8 +193,16 @@ class ConversationState(rx.State):
conversation_id=self.conversation_id conversation_id=self.conversation_id
) )
# 重置当前运行工具调用唯一标识与片段索引映射表 # 初始化工具调用唯一标识与片段索引映射字典
self.run_mapping.clear() tool_call_ids : Dict[int, str] = {}
# 创建运行
conversation.runs[str(uuid7())] = Run(user_prompt=user_prompt)
# 将末位运行唯一标识、运行设置为当前运行唯一标识、运行
run_id, run = next(reversed(conversation.runs.items()))
# 将流式输出状态设置为正在流式输出
run.is_streaming = True
yield # 通知前端渲染流式输出状态
async for event in self.agent.run_stream_events( async for event in self.agent.run_stream_events(
conversation_id=self.conversation_id, conversation_id=self.conversation_id,
@ -220,7 +212,6 @@ class ConversationState(rx.State):
match event: match event:
# ========== 分片开始事件 ========== # ========== 分片开始事件 ==========
case PartStartEvent( case PartStartEvent(
event_kind=event_kind,
index=index, index=index,
part=part, part=part,
previous_part_kind=previous_part_kind, previous_part_kind=previous_part_kind,
@ -230,113 +221,56 @@ class ConversationState(rx.State):
case ThinkingPart( case ThinkingPart(
part_kind=part_kind, content=content part_kind=part_kind, content=content
): ):
yield Event( # 若上一分片种类为空则将正在推理状态设置为正在推理
event_kind=EventKind(event_kind), if not previous_part_kind:
event_content=content, run.is_reasoning = True
part_index=index,
previous_part_kind=PartKind( # 初始化推理
previous_part_kind run.reasonings[index] = Reasoning(kind=Kind.THINKING, content=content)
), yield # 通知前端渲染推理
part_kind=PartKind(part_kind),
)
# 检索工具分片开始事件
case ToolSearchCallPart( case ToolSearchCallPart(
tool_kind=tool_kind, tool_call_id=tool_call_id
tool_call_id=tool_call_id,
tool_name=tool_name,
): ):
# 记录工具调用唯一标识与片段索引映射 # 创建工具调用唯一标识与片段索引映射
self.tool_call_ids[tool_call_id] = index tool_call_ids[index] = tool_call_id
yield Event(
event_kind=EventKind(event_kind), # 初始化推理
part_index=index, run.reasonings[index] = Reasoning(kind=Kind.TOOL_SEARCH, content="正在检索")
previous_part_kind=PartKind( yield # 通知前端渲染推理
previous_part_kind
),
part_kind=PartKind(tool_kind),
tool_name=tool_name,
run_id=current_run_id,
)
# 加载能力分片开始事件
case LoadCapabilityCallPart(
tool_kind=tool_kind,
tool_call_id=tool_call_id,
tool_name=tool_name,
):
# 记录工具调用唯一标识与片段索引映射
self.tool_call_ids[tool_call_id] = index
yield Event(
event_kind=EventKind(event_kind),
part_index=index,
previous_part_kind=PartKind(
previous_part_kind
),
part_kind=PartKind(tool_kind),
tool_name=tool_name,
run_id=current_run_id,
)
# 调用工具分片开始事件
case ToolCallPart(
part_kind=part_kind,
tool_call_id=tool_call_id,
tool_name=tool_name,
):
# 记录工具调用唯一标识与片段索引映射
self.tool_call_ids[tool_call_id] = index
yield Event(
event_kind=EventKind(event_kind),
part_index=index,
previous_part_kind=PartKind(
previous_part_kind
),
part_kind=PartKind(part_kind),
tool_name=tool_name,
run_id=current_run_id,
)
# 文本分片开始事件 # 文本分片开始事件
case TextPart( case TextPart(
part_kind=part_kind, content=content part_kind=part_kind, content=content
): ):
yield Event( # 初始化回复正文
event_kind=EventKind(event_kind), run.assistant_content = content
event_content=content, yield # 通知前端渲染回复正文
part_index=index,
previous_part_kind=PartKind( case _:
previous_part_kind continue
),
part_kind=PartKind(part_kind),
run_id=current_run_id,
)
# ========== 分片增量事件(前端仅就文本片段实现打字机效果) ========== # ========== 分片增量事件(前端仅就文本片段实现打字机效果) ==========
case PartDeltaEvent( case PartDeltaEvent(
event_kind=event_kind, index=index, delta=delta index=index, delta=delta
): ):
match delta: match delta:
# 思考分片增量事件 # 思考分片增量事件
case ThinkingPartDelta( case ThinkingPartDelta(
part_delta_kind=part_delta_kind,
content_delta=content_delta, content_delta=content_delta,
): ):
yield Event( run.reasonings[index].content += content_delta or ""
event_kind=EventKind(event_kind), yield # 通知前端渲染推理
event_content=content_delta or "",
part_index=index,
part_kind=PartKind(part_delta_kind),
run_id=current_run_id,
)
# 文本分片增量事件 # 文本分片增量事件
case TextPartDelta( case TextPartDelta(
part_delta_kind=part_delta_kind,
content_delta=content_delta, content_delta=content_delta,
): ):
yield Event( run.assistant_content += content_delta
event_kind=EventKind(event_kind), yield # 通知前端渲染回复正文
event_content=content_delta,
part_index=index, case _:
part_kind=PartKind(part_delta_kind), continue
run_id=current_run_id,
)
# ========== 分片结束事件 ========== # ========== 分片结束事件 ==========
case PartEndEvent( case PartEndEvent(
@ -350,26 +284,17 @@ class ConversationState(rx.State):
case ThinkingPart( case ThinkingPart(
part_kind=part_kind, content=content part_kind=part_kind, content=content
): ):
yield Event( # 若下一分片种类为文本则将正在推理状态设置为非正在推理
event_kind=EventKind(event_kind), if next_part_kind == Kind.TEXT:
event_content=content, run.is_reasoning = False
part_index=index,
part_kind=PartKind(part_kind),
next_part_kind=PartKind(next_part_kind),
run_id=current_run_id,
)
# 检索工具分片结束事件 # 检索工具分片结束事件
case ToolSearchCallPart( case ToolSearchCallPart(
tool_kind=tool_kind, tool_name=tool_name tool_call_id=tool_call_id
): ):
yield Event( # 创建工具调用唯一标识与片段索引映射
event_kind=EventKind(event_kind), tool_call_ids[index] = tool_call_id
part_index=index,
part_kind=PartKind(tool_kind),
next_part_kind=PartKind(next_part_kind),
tool_name=tool_name,
run_id=current_run_id,
)
# 加载能力分片结束事件 # 加载能力分片结束事件
case LoadCapabilityCallPart( case LoadCapabilityCallPart(
tool_kind=tool_kind, tool_name=tool_name tool_kind=tool_kind, tool_name=tool_name
@ -394,18 +319,6 @@ class ConversationState(rx.State):
tool_name=tool_name, tool_name=tool_name,
run_id=current_run_id, run_id=current_run_id,
) )
# 文本分片结束事件
case TextPart(
part_kind=part_kind, content=content
):
yield Event(
event_kind=EventKind(event_kind),
event_content=content,
part_index=index,
part_kind=PartKind(part_kind),
next_part_kind=PartKind(next_part_kind),
run_id=current_run_id,
)
# ========== 函数工具调用事件 ========== # ========== 函数工具调用事件 ==========
case FunctionToolCallEvent( case FunctionToolCallEvent(