fix(client): consume typed business session events
This commit is contained in:
@@ -6,6 +6,7 @@ import type {
|
||||
import {
|
||||
emptyAssistantBlock, isAppendSurfaceEvent, isTokenDelta, toAssistantBlock, toAssistantBlocks,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {} from '@deepseek-ai/dsh-llm-retry/types'
|
||||
import type { AssistantChatData } from '../contract/chat-nodes.ts'
|
||||
import { CHAT_SYNTHETIC_SEQ_OFFSETS, chatNode } from './common.ts'
|
||||
|
||||
@@ -197,7 +198,7 @@ function fallbackState(context: ConversationNodeContext<AssistantState>): Assist
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ((match.event.type as string) === 'llm/retry' && state !== undefined) {
|
||||
if (match.event.type === 'llm/retry' && state !== undefined) {
|
||||
state = resetForRetry(state)
|
||||
}
|
||||
}
|
||||
@@ -247,9 +248,8 @@ export const assistantDefinition: ConversationNodeDefinition<AssistantState> = {
|
||||
|| (event.type === 'assistant/message' && isAppendSurfaceEvent(event))) {
|
||||
return { id: `${event.data.turn}:${event.data.step}`, role: 'update' }
|
||||
}
|
||||
if ((event.type as string) === 'llm/retry') {
|
||||
const data = event.data as unknown as { turn: number; step: number }
|
||||
return { id: `${data.turn}:${data.step}`, role: 'update' }
|
||||
if (event.type === 'llm/retry') {
|
||||
return { id: `${event.data.turn}:${event.data.step}`, role: 'update' }
|
||||
}
|
||||
return null
|
||||
},
|
||||
@@ -268,7 +268,7 @@ export const assistantDefinition: ConversationNodeDefinition<AssistantState> = {
|
||||
usage: match.event.data.usage,
|
||||
}
|
||||
}
|
||||
if ((match.event.type as string) === 'llm/retry') {
|
||||
if (match.event.type === 'llm/retry') {
|
||||
return resetForRetry(context.state)
|
||||
}
|
||||
return context.state
|
||||
|
||||
@@ -5,6 +5,8 @@ import type {
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { isReplacementSurfaceEvent } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { COMPACT_CHECKPOINT_SOURCE } from '@deepseek-ai/dsh-compact/checkpoint'
|
||||
import type {} from '@deepseek-ai/dsh-compact/types'
|
||||
import type {} from '@deepseek-ai/dsh-commands/types'
|
||||
import type { ManualCompactionChatData } from '../contract/chat-nodes.ts'
|
||||
import { chatNode } from './common.ts'
|
||||
|
||||
@@ -32,21 +34,9 @@ interface CompactionEvidence {
|
||||
readonly checkpoint?: ConversationMatch
|
||||
}
|
||||
|
||||
interface CommandRunData {
|
||||
readonly commandId: CommandId
|
||||
readonly name: string
|
||||
readonly args?: string
|
||||
}
|
||||
|
||||
interface CommandDoneData {
|
||||
readonly commandId: CommandId
|
||||
readonly kind: 'success' | 'error'
|
||||
readonly text?: string
|
||||
readonly sourceEventSeq?: number
|
||||
}
|
||||
|
||||
function commandFromRun(match: ConversationMatch): CommandNode {
|
||||
const data = match.event.data as unknown as CommandRunData
|
||||
if (match.event.type !== 'command/run') throw new Error('command start requires command/run')
|
||||
const data = match.event.data
|
||||
return {
|
||||
kind: 'command',
|
||||
seq: match.event.seq,
|
||||
@@ -59,10 +49,12 @@ function commandFromRun(match: ConversationMatch): CommandNode {
|
||||
}
|
||||
|
||||
function commandFromDone(match: ConversationMatch, previous?: CommandNode): CommandNode {
|
||||
const data = match.event.data as unknown as CommandDoneData
|
||||
if (match.event.type !== 'command/done') throw new Error('command update requires command/done')
|
||||
const data = match.event.data
|
||||
const sourceEventSeq = data.kind === 'success'
|
||||
&& Number.isSafeInteger(data.sourceEventSeq) && (data.sourceEventSeq as number) >= 0
|
||||
? data.sourceEventSeq as number
|
||||
&& data.sourceEventSeq !== undefined
|
||||
&& Number.isSafeInteger(data.sourceEventSeq) && data.sourceEventSeq >= 0
|
||||
? data.sourceEventSeq
|
||||
: undefined
|
||||
return {
|
||||
kind: 'command',
|
||||
@@ -112,28 +104,21 @@ function compactSummary(match: ConversationMatch | undefined, checkpoint: Conver
|
||||
let summary: string | null = null
|
||||
let shadowedItemCount: number | null = null
|
||||
let shadowedTokenCount: number | null = null
|
||||
if (match !== undefined) {
|
||||
const data = match.event.data as unknown as {
|
||||
summary?: unknown
|
||||
shadowedSeqs?: unknown
|
||||
shadowedTokenCount?: unknown
|
||||
}
|
||||
if (match?.event.type === 'compact/summary') {
|
||||
const data = match.event.data
|
||||
if (Array.isArray(data.summary)) {
|
||||
const text = data.summary
|
||||
.map((block: unknown) => {
|
||||
const value = block as { type?: unknown; text?: unknown }
|
||||
return value.type === 'text' && typeof value.text === 'string' ? value.text : ''
|
||||
})
|
||||
.map(block => block.type === 'text' ? block.text : '')
|
||||
.join('')
|
||||
summary = text.trim() === '' ? null : text
|
||||
}
|
||||
shadowedItemCount = Array.isArray(data.shadowedSeqs)
|
||||
&& data.shadowedSeqs.every(seq => Number.isSafeInteger(seq) && (seq as number) >= 0)
|
||||
&& data.shadowedSeqs.every(seq => Number.isSafeInteger(seq) && seq >= 0)
|
||||
? data.shadowedSeqs.length
|
||||
: null
|
||||
shadowedTokenCount = Number.isSafeInteger(data.shadowedTokenCount)
|
||||
&& (data.shadowedTokenCount as number) >= 0
|
||||
? data.shadowedTokenCount as number
|
||||
&& data.shadowedTokenCount >= 0
|
||||
? data.shadowedTokenCount
|
||||
: null
|
||||
}
|
||||
return {
|
||||
@@ -148,9 +133,9 @@ function compactSummary(match: ConversationMatch | undefined, checkpoint: Conver
|
||||
}
|
||||
|
||||
function fallbackState(context: ConversationNodeContext<CommandState>): CommandState | undefined {
|
||||
const done = context.matches.find(match => (match.event.type as string) === 'command/done')
|
||||
const done = context.matches.find(match => match.event.type === 'command/done')
|
||||
const checkpoint = context.matches.find(match => compactSource(match.event) !== undefined)
|
||||
const summary = context.matches.find(match => (match.event.type as string) === 'compact/summary')
|
||||
const summary = context.matches.find(match => match.event.type === 'compact/summary')
|
||||
if (checkpoint === undefined) return done === undefined ? undefined : { command: commandFromDone(done) }
|
||||
const source = compactSource(checkpoint.event)
|
||||
if (source?.sourceCommandId === undefined) return done === undefined ? undefined : { command: commandFromDone(done) }
|
||||
@@ -182,7 +167,7 @@ export function updateCompactionState<State extends CompactionEvidence>(
|
||||
state: State,
|
||||
match: ConversationMatch,
|
||||
): State {
|
||||
if ((match.event.type as string) === 'compact/summary') return { ...state, summary: match }
|
||||
if (match.event.type === 'compact/summary') return { ...state, summary: match }
|
||||
if (compactSource(match.event) !== undefined) return { ...state, checkpoint: match }
|
||||
return state
|
||||
}
|
||||
@@ -191,27 +176,28 @@ export function updateCompactionState<State extends CompactionEvidence>(
|
||||
export const commandDefinition: ConversationNodeDefinition<CommandState> = {
|
||||
kind: 'command',
|
||||
match: (event) => {
|
||||
if ((event.type as string) === 'command/run') {
|
||||
return { id: String((event.data as unknown as CommandRunData).commandId), role: 'start' }
|
||||
if (event.type === 'command/run') {
|
||||
return { id: String(event.data.commandId), role: 'start' }
|
||||
}
|
||||
if ((event.type as string) === 'command/done') {
|
||||
return { id: String((event.data as unknown as CommandDoneData).commandId), role: 'update' }
|
||||
if (event.type === 'command/done') {
|
||||
return { id: String(event.data.commandId), role: 'update' }
|
||||
}
|
||||
const checkpoint = compactSource(event)
|
||||
if (checkpoint?.sourceCommandId !== undefined) {
|
||||
return { id: String(checkpoint.sourceCommandId), role: 'update' }
|
||||
}
|
||||
if ((event.type as string) === 'compact/start'
|
||||
|| (event.type as string) === 'compact/summary'
|
||||
|| (event.type as string) === 'compact/end') {
|
||||
const data = event.data as unknown as { sourceCommandId?: CommandId }
|
||||
if (data.sourceCommandId !== undefined) return { id: String(data.sourceCommandId), role: 'update' }
|
||||
if (event.type === 'compact/start'
|
||||
|| event.type === 'compact/summary'
|
||||
|| event.type === 'compact/end') {
|
||||
if (event.data.sourceCommandId !== undefined) {
|
||||
return { id: String(event.data.sourceCommandId), role: 'update' }
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
start: (_context, match) => ({ command: commandFromRun(match) }),
|
||||
update: (context, match) => {
|
||||
if ((match.event.type as string) === 'command/done') {
|
||||
if (match.event.type === 'command/done') {
|
||||
return { ...context.state, command: commandFromDone(match, context.state.command) }
|
||||
}
|
||||
return updateCompactionState(context.state, match)
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Context } from 'cordis'
|
||||
import type {
|
||||
CompactionSummaryNode, ConversationMatch, ConversationNodeContext, ConversationNodeDefinition,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {} from '@deepseek-ai/dsh-compact/types'
|
||||
import { chatNode } from './common.ts'
|
||||
import { compactSource, compactSummary, updateCompactionState } from './command.ts'
|
||||
|
||||
@@ -18,7 +19,7 @@ interface CompactionState {
|
||||
}
|
||||
|
||||
function fallbackState(context: ConversationNodeContext<CompactionState>): CompactionState {
|
||||
const summary = context.matches.find(match => (match.event.type as string) === 'compact/summary')
|
||||
const summary = context.matches.find(match => match.event.type === 'compact/summary')
|
||||
const checkpoint = context.matches.find(match => compactSource(match.event) !== undefined)
|
||||
return {
|
||||
...summary === undefined ? {} : { summary },
|
||||
@@ -34,12 +35,11 @@ export const compactionDefinition: ConversationNodeDefinition<CompactionState> =
|
||||
if (checkpoint !== undefined && checkpoint.sourceCommandId === undefined) {
|
||||
return { id: checkpoint.compactionId, role: 'update' }
|
||||
}
|
||||
if ((event.type as string) === 'compact/start'
|
||||
|| (event.type as string) === 'compact/summary'
|
||||
|| (event.type as string) === 'compact/end') {
|
||||
const data = event.data as unknown as { compactionId?: unknown; sourceCommandId?: unknown }
|
||||
if (typeof data.compactionId !== 'string' || data.sourceCommandId !== undefined) return null
|
||||
return { id: data.compactionId, role: (event.type as string) === 'compact/start' ? 'start' : 'update' }
|
||||
if (event.type === 'compact/start'
|
||||
|| event.type === 'compact/summary'
|
||||
|| event.type === 'compact/end') {
|
||||
if (event.data.sourceCommandId !== undefined) return null
|
||||
return { id: String(event.data.compactionId), role: event.type === 'compact/start' ? 'start' : 'update' }
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Context } from 'cordis'
|
||||
import type {
|
||||
ConversationNodeDefinition, ConversationPreviousContext,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {} from '@deepseek-ai/dsh-agent/types'
|
||||
|
||||
type InboxTarget = 'next-turn' | 'next-step'
|
||||
|
||||
@@ -41,14 +42,14 @@ function inboxDefinition(target: InboxTarget): ConversationNodeDefinition<InboxS
|
||||
const kind = `inbox-${target}`
|
||||
return {
|
||||
kind,
|
||||
match: event => (event.type as string) === 'agent/inbox/spliced'
|
||||
&& (event.data as unknown as { target?: unknown }).target === target
|
||||
match: event => event.type === 'agent/inbox/spliced'
|
||||
&& event.data.target === target
|
||||
? { id: String(event.seq), role: 'start' }
|
||||
: null,
|
||||
start: (_context, match, reader) => applySplice(
|
||||
reader.previous<InboxState>(kind),
|
||||
match.event.data as unknown as InboxSplice,
|
||||
),
|
||||
start: (_context, match, reader) => {
|
||||
if (match.event.type !== 'agent/inbox/spliced') throw new Error(`${kind} start requires agent/inbox/spliced`)
|
||||
return applySplice(reader.previous<InboxState>(kind), match.event.data)
|
||||
},
|
||||
update: context => context.state,
|
||||
publication: () => 'none',
|
||||
buildViewNode: () => null,
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Context } from 'cordis'
|
||||
import type {
|
||||
ConversationLocation, ConversationNodeDefinition, ModelRetryNode,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {} from '@deepseek-ai/dsh-llm-retry/types'
|
||||
import type { RetryChatData } from '../contract/chat-nodes.ts'
|
||||
import { chatNode } from './common.ts'
|
||||
|
||||
@@ -12,11 +13,6 @@ declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
|
||||
}
|
||||
}
|
||||
|
||||
type WithoutRetryProjection<Node> = Node extends unknown
|
||||
? Omit<Node, 'kind' | 'seq' | 'time' | 'retryState'>
|
||||
: never
|
||||
type RetryEventData = WithoutRetryProjection<ModelRetryNode>
|
||||
|
||||
/** Accumulated retry attempts sharing one producer-owned RetryId. */
|
||||
export interface RetryState {
|
||||
readonly turn: number
|
||||
@@ -24,31 +20,14 @@ export interface RetryState {
|
||||
readonly attempts: readonly ModelRetryNode[]
|
||||
}
|
||||
|
||||
function retryData(value: unknown): RetryEventData | undefined {
|
||||
if (value === null || typeof value !== 'object') return undefined
|
||||
const data = value as Record<string, unknown>
|
||||
if (typeof data.retryId !== 'string' || data.retryId === ''
|
||||
|| !Number.isSafeInteger(data.turn) || (data.turn as number) < 0
|
||||
|| !Number.isSafeInteger(data.step) || (data.step as number) < 0
|
||||
|| !Number.isSafeInteger(data.retry) || (data.retry as number) <= 0
|
||||
|| typeof data.delayMs !== 'number' || !Number.isFinite(data.delayMs) || data.delayMs < 0
|
||||
|| typeof data.provider !== 'string' || typeof data.policyKey !== 'string'
|
||||
|| (data.mode !== 'normal' && data.mode !== 'always')
|
||||
|| data.failure === null || typeof data.failure !== 'object') return undefined
|
||||
if (data.mode === 'normal' && (!Number.isSafeInteger(data.maxRetries) || (data.maxRetries as number) <= 0)) {
|
||||
return undefined
|
||||
}
|
||||
return data as unknown as RetryEventData
|
||||
}
|
||||
|
||||
function scheduledNode(event: { seq: number; time: number; data: unknown }): ModelRetryNode | undefined {
|
||||
const data = retryData(event.data)
|
||||
return data === undefined ? undefined : {
|
||||
function scheduledNode(match: Parameters<ConversationNodeDefinition['start']>[1]): ModelRetryNode | undefined {
|
||||
if (match.event.type !== 'llm/retry') return undefined
|
||||
return {
|
||||
kind: 'model-retry',
|
||||
seq: event.seq,
|
||||
time: event.time,
|
||||
seq: match.event.seq,
|
||||
time: match.event.time,
|
||||
retryState: 'scheduled',
|
||||
...data,
|
||||
...match.event.data,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,33 +40,30 @@ function isClosed(location: ConversationLocation): boolean {
|
||||
export const retryDefinition: ConversationNodeDefinition<RetryState> = {
|
||||
kind: 'model-retry',
|
||||
match: (event) => {
|
||||
if ((event.type as string) === 'llm/retry') {
|
||||
const data = retryData(event.data)
|
||||
if (data === undefined) return null
|
||||
return { id: String(data.retryId), role: data.retry === 1 ? 'start' : 'update' }
|
||||
if (event.type === 'llm/retry') {
|
||||
return { id: String(event.data.retryId), role: event.data.retry === 1 ? 'start' : 'update' }
|
||||
}
|
||||
if ((event.type as string) === 'llm/retry-started') {
|
||||
const data = event.data as unknown as { retryId?: unknown }
|
||||
return typeof data.retryId === 'string' ? { id: data.retryId, role: 'update' } : null
|
||||
if (event.type === 'llm/retry-started') {
|
||||
return { id: String(event.data.retryId), role: 'update' }
|
||||
}
|
||||
return null
|
||||
},
|
||||
start: (_context, match) => {
|
||||
const node = scheduledNode(match.event)
|
||||
const node = scheduledNode(match)
|
||||
if (node === undefined) throw new Error('model-retry start requires a valid llm/retry event')
|
||||
return { turn: node.turn, step: node.step, attempts: [node] }
|
||||
},
|
||||
update: (context, match) => {
|
||||
if ((match.event.type as string) === 'llm/retry') {
|
||||
const node = scheduledNode(match.event)
|
||||
if (match.event.type === 'llm/retry') {
|
||||
const node = scheduledNode(match)
|
||||
return node === undefined ? context.state : { ...context.state, attempts: [...context.state.attempts, node] }
|
||||
}
|
||||
if ((match.event.type as string) !== 'llm/retry-started') return context.state
|
||||
const data = match.event.data as unknown as { retry: number }
|
||||
if (match.event.type !== 'llm/retry-started') return context.state
|
||||
const retry = match.event.data.retry
|
||||
return {
|
||||
...context.state,
|
||||
attempts: context.state.attempts.map(attempt =>
|
||||
attempt.retry === data.retry ? { ...attempt, retryState: 'started' } : attempt),
|
||||
attempt.retry === retry ? { ...attempt, retryState: 'started' } : attempt),
|
||||
}
|
||||
},
|
||||
buildViewNode: (context, target) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
RunningToolCall, ToolCallBlock, ToolResultNode,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { isAppendSurfaceEvent } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {} from '@deepseek-ai/dsh-tools/types'
|
||||
import type { ToolChatData } from '../contract/chat-nodes.ts'
|
||||
import { CHAT_SYNTHETIC_SEQ_OFFSETS, chatNode } from './common.ts'
|
||||
|
||||
@@ -141,27 +142,30 @@ function acceptsEdge(state: ToolState, parent: string, child: string): boolean {
|
||||
}
|
||||
|
||||
function updateDispatch(state: ToolState, match: ConversationMatch): ToolState {
|
||||
const data = match.event.data as unknown as DispatchData
|
||||
const siblings = state.children.get(data.parentCallId) ?? []
|
||||
const index = siblings.findIndex(candidate => candidate.callId === data.subCallId)
|
||||
if ((match.event.type as string) === 'tool/code-dispatch-start') {
|
||||
if (index >= 0 || !acceptsEdge(state, data.parentCallId, data.subCallId)) return state
|
||||
const event = match.event
|
||||
if (event.type !== 'tool/code-dispatch-start' && event.type !== 'tool/code-dispatch') return state
|
||||
const data = event.data
|
||||
const parentCallId = String(data.parentCallId)
|
||||
const subCallId = String(data.subCallId)
|
||||
const siblings = state.children.get(parentCallId) ?? []
|
||||
const index = siblings.findIndex(candidate => candidate.callId === subCallId)
|
||||
if (event.type === 'tool/code-dispatch-start') {
|
||||
if (index >= 0 || !acceptsEdge(state, parentCallId, subCallId)) return state
|
||||
const children = new Map(state.children)
|
||||
children.set(data.parentCallId, [...siblings, childCall(match, data)])
|
||||
children.set(parentCallId, [...siblings, childCall(match, data)])
|
||||
const parents = new Map(state.parents)
|
||||
parents.set(data.subCallId, data.parentCallId)
|
||||
parents.set(subCallId, parentCallId)
|
||||
return { ...state, children, parents }
|
||||
}
|
||||
if ((match.event.type as string) !== 'tool/code-dispatch') return state
|
||||
if (index < 0 && !acceptsEdge(state, data.parentCallId, data.subCallId)) return state
|
||||
if (index < 0 && !acceptsEdge(state, parentCallId, subCallId)) return state
|
||||
const previous = index < 0 ? undefined : siblings[index]
|
||||
const settled = childResult(match, data, previous)
|
||||
const children = new Map(state.children)
|
||||
children.set(data.parentCallId, index < 0
|
||||
children.set(parentCallId, index < 0
|
||||
? [...siblings, settled]
|
||||
: siblings.map((child, at) => at === index ? settled : child))
|
||||
const parents = new Map(state.parents)
|
||||
if (index < 0) parents.set(data.subCallId, data.parentCallId)
|
||||
if (index < 0) parents.set(subCallId, parentCallId)
|
||||
return { ...state, children, parents }
|
||||
}
|
||||
|
||||
@@ -236,9 +240,8 @@ export const toolDefinition: ConversationNodeDefinition<ToolState> = {
|
||||
if (event.type === 'tool/result' && isAppendSurfaceEvent(event)) {
|
||||
return { id: String(event.data.message.source.callId), role: 'update' }
|
||||
}
|
||||
if ((event.type as string) === 'tool/code-dispatch-start' || (event.type as string) === 'tool/code-dispatch') {
|
||||
const data = event.data as unknown as { rootCallId: string }
|
||||
return { id: data.rootCallId, role: 'update' }
|
||||
if (event.type === 'tool/code-dispatch-start' || event.type === 'tool/code-dispatch') {
|
||||
return { id: String(event.data.rootCallId), role: 'update' }
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
ConversationMatch, ConversationNodeContext, ConversationNodeDefinition, TurnErrorNode,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { displayFailureMessage } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {} from '@deepseek-ai/dsh-llm-retry/types'
|
||||
import { chatNode } from './common.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
|
||||
@@ -30,9 +31,9 @@ function lastStep(context: ConversationNodeContext<TurnErrorState>): number {
|
||||
}
|
||||
|
||||
function retryTurn(event: Parameters<ConversationNodeDefinition['match']>[0]): number | undefined {
|
||||
if ((event.type as string) !== 'llm/retry' && (event.type as string) !== 'llm/retry-started') return undefined
|
||||
const turn = (event.data as unknown as { turn?: unknown }).turn
|
||||
return Number.isSafeInteger(turn) && (turn as number) >= 0 ? turn as number : undefined
|
||||
return event.type === 'llm/retry' || event.type === 'llm/retry-started'
|
||||
? event.data.turn
|
||||
: undefined
|
||||
}
|
||||
|
||||
function failureFrom(match: ConversationMatch): TurnErrorState['failure'] | undefined {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
ConversationMatch, ConversationNodeContext, ConversationNodeDefinition, TurnLocation,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { isAppendSurfaceEvent, toAssistantBlocks } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {} from '@deepseek-ai/dsh-llm-retry/types'
|
||||
import type {
|
||||
AssistantChatData, FinalAssistantChatData, TurnTailChatData,
|
||||
} from '../contract/chat-nodes.ts'
|
||||
@@ -58,9 +59,7 @@ function turnCoordinates(event: Parameters<ConversationNodeDefinition['match']>[
|
||||
|| event.type === 'step/end') {
|
||||
return { turn: event.data.turn, step: event.data.step }
|
||||
}
|
||||
if ((event.type as string) === 'llm/retry') {
|
||||
return event.data as unknown as { turn: number; step: number }
|
||||
}
|
||||
if (event.type === 'llm/retry') return { turn: event.data.turn, step: event.data.step }
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -90,7 +89,7 @@ function closingAnchor(context: ConversationNodeContext<TurnTailState>): number
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ((event.type as string) === 'llm/retry') {
|
||||
if (event.type === 'llm/retry') {
|
||||
steps.set(coordinates.step, { streamedText: false, finalized: false })
|
||||
continue
|
||||
}
|
||||
@@ -130,7 +129,7 @@ function tailData(context: ConversationNodeContext<TurnTailState>): TurnTailChat
|
||||
const candidate = event.type === 'tool/call'
|
||||
|| (event.type === 'tool/result' && isAppendSurfaceEvent(event))
|
||||
|| (event.type === 'turn/end' && event.data.reason.kind === 'error')
|
||||
|| (event.type as string) === 'llm/retry'
|
||||
|| event.type === 'llm/retry'
|
||||
? event.seq
|
||||
: undefined
|
||||
if (candidate !== undefined && (latestTranscriptSeq === undefined || candidate > latestTranscriptSeq)) {
|
||||
|
||||
Reference in New Issue
Block a user