fix(client): address remaining conversation review feedback
This commit is contained in:
@@ -9,9 +9,11 @@ type TurnTailNodeViewProps = ChatNodeViewProps<'turn-tail'> & PropsRenderSlots<'
|
||||
|
||||
/** Turn-local actions and feature tail over the Location index, independent of Assistant placement. */
|
||||
export const TurnTailNodeView = memo(function TurnTailNodeView({
|
||||
node, openFile, forkAt, renderSlotChain, t,
|
||||
node, openFile, forkAt, renderSlotChain, t, useSession,
|
||||
}: TurnTailNodeViewProps) {
|
||||
const data = node.data
|
||||
const hasLaterChatNode = useSession(snapshot =>
|
||||
snapshot.chat.locations.getTurn(data.turn).at(-1) !== node.key)
|
||||
const turn = node.location.kind === 'turn' || node.location.kind === 'step'
|
||||
? node.location.turn
|
||||
: undefined
|
||||
@@ -34,7 +36,7 @@ export const TurnTailNodeView = memo(function TurnTailNodeView({
|
||||
tokensPerSecond={data.tokensPerSecond}
|
||||
clock="end"
|
||||
onBranch={() => { forkAt(closing.finalNode.seq) }}
|
||||
branchUnavailable={data.branchUnavailable}
|
||||
branchUnavailable={data.branchUnavailable || hasLaterChatNode}
|
||||
className={css.actions}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
@@ -57,7 +57,7 @@ export interface TurnTailChatData {
|
||||
readonly time: number
|
||||
/** Last finalized content-bearing Assistant in this Turn. */
|
||||
readonly closing: FinalAssistantChatData | null
|
||||
/** Whether later Assistant/Step material makes the closing seq non-tail. */
|
||||
/** Whether non-rendered later evidence makes the closing seq non-tail. */
|
||||
readonly branchUnavailable: boolean
|
||||
readonly ttftMs?: number
|
||||
readonly tokensPerSecond?: number
|
||||
|
||||
@@ -181,14 +181,12 @@ function makeHarness(init?: Partial<ConversationSnapshot>) {
|
||||
if (key !== 'conversation.chat.node') return opts?.fallback ?? null
|
||||
const nodeOwner = owner as RoutedChatNodeOwner
|
||||
const nodeKey = opts?.hookContext as string | undefined
|
||||
const useTurnData = ((dataKey: string) => {
|
||||
return props.useSession((snapshot) => {
|
||||
const location = nodeKey === undefined ? undefined : snapshot.chat.nodes.get(nodeKey)?.location
|
||||
return location?.kind === 'turn' || location?.kind === 'step'
|
||||
? location.turn.data.get(dataKey as never)
|
||||
: undefined
|
||||
})
|
||||
}) as UseChatNodeTurnData
|
||||
const useTurnData: UseChatNodeTurnData = dataKey => props.useSession((snapshot) => {
|
||||
const location = nodeKey === undefined ? undefined : snapshot.chat.nodes.get(nodeKey)?.location
|
||||
return location?.kind === 'turn' || location?.kind === 'step'
|
||||
? location.turn.data.get(dataKey)
|
||||
: undefined
|
||||
})
|
||||
const nodeProps = <Kind extends ChatNode['kind']>(): ChatNodeViewProps<Kind> => (
|
||||
{ ...props, ...nodeOwner, useTurnData } as unknown as ChatNodeViewProps<Kind>
|
||||
)
|
||||
@@ -722,6 +720,28 @@ describe('ChatView', () => {
|
||||
expect(h.forkAt.mock.calls).toEqual([[2]])
|
||||
})
|
||||
|
||||
it('disables fork when the indexed Turn has a later steering Node', () => {
|
||||
const base = chatSnapshotFixture({
|
||||
nodes: [user(1, 'question'), assistant(2, 'answer')],
|
||||
turnEnds: new Map([[1, 4]]),
|
||||
})
|
||||
const chat = {
|
||||
...base,
|
||||
locations: {
|
||||
getTurn: (turn: number) => turn === 1
|
||||
? [...base.locations.getTurn(turn), 'fixture:steering:later']
|
||||
: base.locations.getTurn(turn),
|
||||
getStep: (turn: number, step: number) => base.locations.getStep(turn, step),
|
||||
},
|
||||
}
|
||||
const h = makeHarness({ chat })
|
||||
const view = render(<h.ChatView {...h.props} />)
|
||||
const branch = view.getByRole('button', { name: '在新对话中分支' })
|
||||
expect(branch.getAttribute('aria-disabled')).toBe('true')
|
||||
fireEvent.click(branch)
|
||||
expect(h.forkAt).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps final content actions but disables branch when Tool and interrupted Think follow it', () => {
|
||||
const interruptedThink: AssistantMessageNode = {
|
||||
kind: 'assistant', seq: 4.1, time: 4_100, turn: 1, step: 2,
|
||||
|
||||
@@ -36,7 +36,9 @@ interface DeliverablesState extends DeliverablesTurnData {
|
||||
* Paths a call view reports having created or changed, by render intent rather
|
||||
* than tool name: a diff card, or a generic card whose kind is `edit` (the
|
||||
* shape `str_replace_editor`'s insert presents). Every other card produces
|
||||
* nothing to open — a read looked, a delete removed, a terminal ran.
|
||||
* nothing to open — a read looked, a delete removed, a terminal ran. Only
|
||||
* root call views enter this Turn accumulator; nested Code Mode dispatches
|
||||
* preserve the pre-assembly behavior and do not contribute independently.
|
||||
*/
|
||||
function producedPaths(view: ToolResultNode['callView']): readonly string[] {
|
||||
if (view === null) return []
|
||||
|
||||
Reference in New Issue
Block a user