feat: 添加 topic_id 字段到消息结构,优化消息处理逻辑

This commit is contained in:
oudecheng 2026-06-12 12:22:21 +08:00
parent 87fc8cc3b7
commit cedd8b2a69
5 changed files with 29 additions and 0 deletions

View File

@ -202,6 +202,8 @@ pub enum WsOutbound {
task_id: String, task_id: String,
description: String, description: String,
subagent_type: String, subagent_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
topic_id: Option<String>,
}, },
#[serde(rename = "session_established")] #[serde(rename = "session_established")]
SessionEstablished { session_id: String }, SessionEstablished { session_id: String },

View File

@ -172,6 +172,7 @@ pub(crate) fn ws_outbound_from_outbound_message(message: &OutboundMessage) -> Ve
task_id: message.metadata.get("task_id").cloned().unwrap_or_default(), task_id: message.metadata.get("task_id").cloned().unwrap_or_default(),
description: message.metadata.get("task_description").cloned().unwrap_or_default(), description: message.metadata.get("task_description").cloned().unwrap_or_default(),
subagent_type: message.metadata.get("task_subagent_type").cloned().unwrap_or_default(), subagent_type: message.metadata.get("task_subagent_type").cloned().unwrap_or_default(),
topic_id: message.metadata.get("topic_id").cloned(),
}], }],
} }
} }

View File

@ -250,6 +250,7 @@ impl DefaultSubAgentRuntime {
let mut metadata = HashMap::new(); let mut metadata = HashMap::new();
metadata.insert("subagent_task_id".to_string(), session.id.clone()); metadata.insert("subagent_task_id".to_string(), session.id.clone());
metadata.insert("is_subagent_event".to_string(), "true".to_string()); metadata.insert("is_subagent_event".to_string(), "true".to_string());
metadata.insert("topic_id".to_string(), session.parent_topic_id.clone().unwrap_or_default());
let emitter = Arc::new(PersistingEmittedMessageHandler::new( let emitter = Arc::new(PersistingEmittedMessageHandler::new(
SubAgentEmitter { SubAgentEmitter {
@ -424,6 +425,7 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
metadata.insert("task_id".to_string(), session.id.clone()); metadata.insert("task_id".to_string(), session.id.clone());
metadata.insert("task_description".to_string(), session.description.clone()); metadata.insert("task_description".to_string(), session.description.clone());
metadata.insert("task_subagent_type".to_string(), session.subagent_type.clone()); metadata.insert("task_subagent_type".to_string(), session.subagent_type.clone());
metadata.insert("topic_id".to_string(), session.parent_topic_id.clone().unwrap_or_default());
let event = OutboundMessage { let event = OutboundMessage {
channel: session.parent_channel_name.clone(), channel: session.parent_channel_name.clone(),

View File

@ -181,6 +181,15 @@ export function useChat(): UseChatReturn {
return undefined return undefined
} }
// Extract topic_id from a message if present
const getTopicId = (message: WsOutbound): string | undefined => {
if (message.type === 'tool_call' || message.type === 'tool_result'
|| message.type === 'tool_pending' || message.type === 'assistant_response') {
return (message as ToolCall | ToolResult | ToolPending | AssistantResponse).topic_id
}
return undefined
}
// Convert a server message to ChatMessage (extracted from handleServerMessage logic) // Convert a server message to ChatMessage (extracted from handleServerMessage logic)
const serverMessageToChatMessage = (message: WsOutbound): ChatMessage | null => { const serverMessageToChatMessage = (message: WsOutbound): ChatMessage | null => {
switch (message.type) { switch (message.type) {
@ -311,12 +320,20 @@ export function useChat(): UseChatReturn {
appendToSubAgentViewMessage(message) appendToSubAgentViewMessage(message)
return return
} }
// 丢弃其他子智能体的消息,避免 fall through 到主消息处理
if (msgSubagentTaskId) {
return
}
} }
// In main view, skip sub-agent messages (they belong to sub-agent view). // In main view, skip sub-agent messages (they belong to sub-agent view).
// But use the task_id to associate with the running task tool card. // But use the task_id to associate with the running task tool card.
const msgSubagentTaskId = getSubagentTaskId(message) const msgSubagentTaskId = getSubagentTaskId(message)
if (msgSubagentTaskId) { if (msgSubagentTaskId) {
// 只 backfill 当前话题的 task tool_call避免跨话题串扰
const msgTopicId = getTopicId(message)
if (msgTopicId && msgTopicId !== selectedTopicRef.current) return
setMessages((prev) => { setMessages((prev) => {
for (let i = prev.length - 1; i >= 0; i--) { for (let i = prev.length - 1; i >= 0; i--) {
if (prev[i].type === 'tool_call' && prev[i].toolName === 'task' && !prev[i].subagentTaskId) { if (prev[i].type === 'tool_call' && prev[i].toolName === 'task' && !prev[i].subagentTaskId) {
@ -340,6 +357,9 @@ export function useChat(): UseChatReturn {
case 'task_started': { case 'task_started': {
const msg = message as TaskStarted const msg = message as TaskStarted
// 只 backfill 当前话题的 task tool_call避免跨话题串扰
if (msg.topic_id && msg.topic_id !== selectedTopicRef.current) break
// 立即更新对应的 task tool_call让用户可以点击查看实时进度 // 立即更新对应的 task tool_call让用户可以点击查看实时进度
setMessages((prev) => { setMessages((prev) => {
for (let i = prev.length - 1; i >= 0; i--) { for (let i = prev.length - 1; i >= 0; i--) {
@ -602,6 +622,7 @@ export function useChat(): UseChatReturn {
const selectTopic = useCallback((topicId: string) => { const selectTopic = useCallback((topicId: string) => {
setSelectedTopic(topicId) setSelectedTopic(topicId)
setMessages([]) setMessages([])
setSubAgentView(null)
}, []) }, [])
const createTopic = useCallback((title?: string): Command => { const createTopic = useCallback((title?: string): Command => {
@ -647,6 +668,7 @@ export function useChat(): UseChatReturn {
setTopics([]) setTopics([])
setSelectedTopic(null) setSelectedTopic(null)
setMessages([]) setMessages([])
setSubAgentView(null)
setIsLoading(true) setIsLoading(true)
}, [selectedChannel]) }, [selectedChannel])
@ -656,6 +678,7 @@ export function useChat(): UseChatReturn {
setTopics([]) setTopics([])
setSelectedTopic(null) setSelectedTopic(null)
setMessages([]) setMessages([])
setSubAgentView(null)
setIsLoading(true) setIsLoading(true)
}, [selectedSessionId]) }, [selectedSessionId])

View File

@ -100,6 +100,7 @@ export interface TaskStarted {
task_id: string task_id: string
description: string description: string
subagent_type: string subagent_type: string
topic_id?: string
} }
export interface SessionEstablished { export interface SessionEstablished {