fix(client): address conversation assembly review feedback

This commit is contained in:
imccyu
2026-08-09 17:28:42 +08:00
parent 8723a0e15f
commit 990f700d3c
20 changed files with 73 additions and 49 deletions
@@ -56,11 +56,10 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
*/
'conversation.chat.commandview': { kind: 'keyed'; scope: 'session'; owner: CommandRowOwnerProps }
/**
* The chat view's turn-tail chain: rendered between a closing assistant
* message's body and its IconActions footer, once per turn (the render
* site elects the closing seq). Entries derive a match from the owner
* currency before mounting, so presentation components never mount only
* to return null; an all-declined chain renders nothing.
* The completed Turn Node's extension chain, rendered before that Node's
* IconActions. Entries derive a match from the engine-owned Turn and
* closing seq before mounting, so presentation components never mount
* only to return null; an all-declined chain renders nothing.
*/
'conversation.chat.turnTail': { kind: 'chain'; scope: 'session'; owner: TurnTailOwnerProps }
/** Selected Tool call output inside the details panel. */
@@ -7,7 +7,7 @@ import {
emptyAssistantBlock, isAppendSurfaceEvent, isTokenDelta, toAssistantBlock, toAssistantBlocks,
} from '@deepseek-ai/dsh-client-runtime/client'
import type { AssistantChatData } from '../contract/chat-nodes.ts'
import { chatNode } from './common.ts'
import { CHAT_SYNTHETIC_SEQ_OFFSETS, chatNode } from './common.ts'
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
interface ChatNodeDataMap {
@@ -169,7 +169,7 @@ function finalNode(
if (boundary === undefined || !hasInterruptionEvidence(blocks)) return undefined
return {
kind: 'assistant',
seq: boundary.seq - 0.9,
seq: boundary.seq + CHAT_SYNTHETIC_SEQ_OFFSETS.interruptedAssistant,
time: boundary.time,
turn: state.turn,
step: state.step,
@@ -154,6 +154,9 @@ const EMPTY_CONTRIBUTION: LegacyContribution = {
function legacyContribution(raw: ChatConversationViewNode): LegacyContribution {
const node = raw as ChatNode
// Content-free settled Assistants remain in the finalized compatibility
// stream so StatsLine preserves its pre-assembly step counts; hidden running
// attempts have no final Node to contribute.
if (raw.visibility !== 'visible' && node.kind !== 'assistant-step') return EMPTY_CONTRIBUTION
switch (node.kind) {
case 'user':
@@ -221,7 +224,7 @@ function sameContribution(left: LegacyContribution | undefined, right: LegacyCon
&& sameReferences(left.nodes, right.nodes)
}
/** Incremental compatibility projection retained solely for unmigrated Trajectory consumers. */
/** Incremental compatibility projection for StatsLine and legacy top-level snapshot fields. */
class LegacySliceBuilder {
private readonly contributions = new Map<string, LegacyContribution>()
private readonly finalizedContributions = new Map<string, LegacyContribution>()
@@ -5,6 +5,16 @@ import type {
ChatNode, ChatNodeDataMap, ChatNodeKind,
} from '../contract/chat-nodes.ts'
/**
* Relative positions in one durable event's seq neighborhood: interrupted
* Assistant, its follow-up Nodes, then follow-ups to an ordinary final.
*/
export const CHAT_SYNTHETIC_SEQ_OFFSETS = {
interruptedAssistant: -0.9,
interruptedFollowup: -0.8,
finalizedFollowup: 0.1,
} as const
/**
* Resolve one Context's best currently loaded event Location.
* @param context - assembled business Context.
@@ -5,7 +5,7 @@ import type {
} from '@deepseek-ai/dsh-client-runtime/client'
import { isAppendSurfaceEvent } from '@deepseek-ai/dsh-client-runtime/client'
import type { ToolChatData } from '../contract/chat-nodes.ts'
import { chatNode } from './common.ts'
import { CHAT_SYNTHETIC_SEQ_OFFSETS, chatNode } from './common.ts'
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
interface ChatNodeDataMap {
@@ -190,7 +190,7 @@ function projectBlock(
? sameReferences(block.subCalls, children) ? block : { ...block, subCalls: children }
: {
kind: 'tool-result',
seq: interruptedAt.seq - 0.8,
seq: interruptedAt.seq + CHAT_SYNTHETIC_SEQ_OFFSETS.interruptedFollowup,
time: interruptedAt.time,
callId: block.callId,
call: { name: block.name, argsRaw: block.argsRaw },
@@ -7,7 +7,7 @@ import type {
AssistantChatData, FinalAssistantChatData, TurnTailChatData,
} from '../contract/chat-nodes.ts'
import { deriveTurnMetrics } from '../chat/turn-metrics.ts'
import { chatNode } from './common.ts'
import { CHAT_SYNTHETIC_SEQ_OFFSETS, chatNode } from './common.ts'
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
interface ChatNodeDataMap {
@@ -85,7 +85,9 @@ function closingAnchor(context: ConversationNodeContext<TurnTailState>): number
}
if (event.type === 'assistant/message') {
steps.set(coordinates.step, { streamedText: false, finalized: true })
if (hasTextAssistant(event)) anchor = event.seq + 0.1
if (hasTextAssistant(event)) {
anchor = event.seq + CHAT_SYNTHETIC_SEQ_OFFSETS.finalizedFollowup
}
continue
}
if ((event.type as string) === 'llm/retry') {
@@ -93,7 +95,7 @@ function closingAnchor(context: ConversationNodeContext<TurnTailState>): number
continue
}
if (event.type === 'step/end' && previous.streamedText && !previous.finalized) {
anchor = event.seq - 0.8
anchor = event.seq + CHAT_SYNTHETIC_SEQ_OFFSETS.interruptedFollowup
}
}
return anchor