修复Agent消息在响应结束后显示两条
This commit is contained in:
@@ -145,6 +145,7 @@ const ConversationTabView = memo(function ConversationTabView({
|
||||
const [draftAgentType, setDraftAgentType] = useState<AgentType>(agentType)
|
||||
const selectedAgent = conversationId != null ? agentType : draftAgentType
|
||||
const [modeId, setModeId] = useState<string | null>(null)
|
||||
const [sendSignal, setSendSignal] = useState(0)
|
||||
const [agentsLoaded, setAgentsLoaded] = useState(false)
|
||||
const [usableAgentCount, setUsableAgentCount] = useState(0)
|
||||
const [agentConnectError, setAgentConnectError] = useState<string | null>(
|
||||
@@ -433,6 +434,7 @@ const ConversationTabView = memo(function ConversationTabView({
|
||||
optimisticTurn,
|
||||
optimisticTurn.id
|
||||
)
|
||||
setSendSignal((prev) => prev + 1)
|
||||
setSyncState(effectiveConversationId, "awaiting_persist")
|
||||
|
||||
if (connStatus === "connected") {
|
||||
@@ -577,6 +579,7 @@ const ConversationTabView = memo(function ConversationTabView({
|
||||
conversationId={effectiveConversationId}
|
||||
connStatus={connStatus}
|
||||
isActive={isActive}
|
||||
sendSignal={sendSignal}
|
||||
/>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { memo, useCallback, useEffect, useMemo } from "react"
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from "react"
|
||||
import { useDbMessageDetail } from "@/hooks/use-db-message-detail"
|
||||
import { useConversationRuntime } from "@/contexts/conversation-runtime-context"
|
||||
import { ContentPartsRenderer } from "./content-parts-renderer"
|
||||
@@ -29,11 +29,13 @@ import {
|
||||
} from "@/lib/agent-plan"
|
||||
import type { ConnectionStatus } from "@/lib/types"
|
||||
import { VirtualizedMessageThread } from "@/components/message/virtualized-message-thread"
|
||||
import { useStickToBottomContext } from "use-stick-to-bottom"
|
||||
|
||||
interface MessageListViewProps {
|
||||
conversationId: number
|
||||
connStatus?: ConnectionStatus | null
|
||||
isActive?: boolean
|
||||
sendSignal?: number
|
||||
}
|
||||
|
||||
interface ResolvedMessageGroup extends MessageGroup {
|
||||
@@ -169,10 +171,38 @@ const PendingTypingIndicator = memo(function PendingTypingIndicator() {
|
||||
)
|
||||
})
|
||||
|
||||
const AutoScrollOnSend = memo(function AutoScrollOnSend({
|
||||
signal,
|
||||
enabled,
|
||||
}: {
|
||||
signal: number
|
||||
enabled: boolean
|
||||
}) {
|
||||
const { scrollToBottom } = useStickToBottomContext()
|
||||
const lastSignalRef = useRef(signal)
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) return
|
||||
if (signal === lastSignalRef.current) return
|
||||
lastSignalRef.current = signal
|
||||
|
||||
scrollToBottom()
|
||||
const rafId = requestAnimationFrame(() => {
|
||||
scrollToBottom()
|
||||
})
|
||||
return () => {
|
||||
cancelAnimationFrame(rafId)
|
||||
}
|
||||
}, [enabled, scrollToBottom, signal])
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
export function MessageListView({
|
||||
conversationId,
|
||||
connStatus,
|
||||
isActive = true,
|
||||
sendSignal = 0,
|
||||
}: MessageListViewProps) {
|
||||
const t = useTranslations("Folder.chat.messageList")
|
||||
const sharedT = useTranslations("Folder.chat.shared")
|
||||
@@ -339,6 +369,7 @@ export function MessageListView({
|
||||
className="flex-1 min-h-0"
|
||||
resize={shouldUseSmoothResize ? "smooth" : undefined}
|
||||
>
|
||||
<AutoScrollOnSend signal={sendSignal} enabled={isActive} />
|
||||
<VirtualizedMessageThread
|
||||
items={threadItems}
|
||||
getItemKey={(item) => item.key}
|
||||
|
||||
@@ -221,6 +221,9 @@ function reduceHydrateDetail(
|
||||
const current = state.byConversationId.get(conversationId)
|
||||
const nextExternalId = detail.summary.external_id ?? null
|
||||
const acceptSnapshot = shouldAcceptPersistedSnapshot(current, detail)
|
||||
const prevPersistedTurnCount = current?.persistedTurns.length ?? 0
|
||||
const prevPersistedMessageCount = current?.persistedMessageCount ?? 0
|
||||
const prevPersistedUpdatedAt = current?.persistedUpdatedAt ?? null
|
||||
const optimisticTurns = current?.optimisticTurns ?? []
|
||||
const persistedTurns = acceptSnapshot
|
||||
? detail.turns
|
||||
@@ -234,11 +237,20 @@ function reduceHydrateDetail(
|
||||
const shouldDropOptimistic =
|
||||
optimisticTurns.length > 0 &&
|
||||
persistedTurns.length >= (current?.persistedTurns.length ?? 0) + 1
|
||||
const nextUpdatedAt = detail.summary.updated_at ?? null
|
||||
const hasPersistedAdvance =
|
||||
acceptSnapshot &&
|
||||
(detail.turns.length > prevPersistedTurnCount ||
|
||||
detail.summary.message_count > prevPersistedMessageCount ||
|
||||
(nextUpdatedAt !== null &&
|
||||
(prevPersistedUpdatedAt === null ||
|
||||
nextUpdatedAt > prevPersistedUpdatedAt)))
|
||||
|
||||
const nextSession: ConversationRuntimeSession = {
|
||||
...(current ?? createEmptySession(conversationId)),
|
||||
externalId: nextExternalId,
|
||||
persistedTurns,
|
||||
liveMessage: hasPersistedAdvance ? null : (current?.liveMessage ?? null),
|
||||
optimisticTurns: shouldDropOptimistic ? [] : optimisticTurns,
|
||||
syncState: shouldDropOptimistic ? "idle" : (current?.syncState ?? "idle"),
|
||||
activeTurnToken: shouldDropOptimistic
|
||||
@@ -370,6 +382,7 @@ function reducer(
|
||||
|
||||
const preferFromSnapshot =
|
||||
from.persistedTurns.length >= to.persistedTurns.length
|
||||
const mergedLiveMessage = to.liveMessage ?? from.liveMessage
|
||||
|
||||
const merged: ConversationRuntimeSession = {
|
||||
...to,
|
||||
@@ -379,7 +392,7 @@ function reducer(
|
||||
? from.persistedTurns
|
||||
: to.persistedTurns,
|
||||
optimisticTurns: [...from.optimisticTurns, ...to.optimisticTurns],
|
||||
liveMessage: to.liveMessage ?? from.liveMessage,
|
||||
liveMessage: mergedLiveMessage,
|
||||
syncState: to.syncState !== "idle" ? to.syncState : from.syncState,
|
||||
activeTurnToken: to.activeTurnToken ?? from.activeTurnToken,
|
||||
lastHydratedAt:
|
||||
|
||||
Reference in New Issue
Block a user