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"
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):
"""推理类"""
kind: Kind = Field(..., description="推理类型")
content: str = Field(default="", description="推理内容")
@ -84,11 +98,7 @@ class Run(BaseModel):
)
is_reasoning: bool = Field(
default=False,
description="推理状态True 表示正在推理False 表示非正在推理",
)
is_collapsed: bool = Field(
default=False,
description="推理面板折叠状态True 表示折叠False 表示展开",
description="正在推理状态True 表示正在推理False 表示非正在推理",
)
assistant_content: str = Field(default="", description="回复正文")
is_streaming: bool = Field(

View File

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