fix(ui-trajectory): consume prompt changes once
This commit is contained in:
@@ -46,6 +46,7 @@ function headerFor(
|
|||||||
function applyHeader(
|
function applyHeader(
|
||||||
request: Extract<RequestView, { purpose: 'assistant' }>,
|
request: Extract<RequestView, { purpose: 'assistant' }>,
|
||||||
header: TrajectoryRequestHeaderState | undefined,
|
header: TrajectoryRequestHeaderState | undefined,
|
||||||
|
includeChange: boolean,
|
||||||
): Extract<RequestView, { purpose: 'assistant' }> {
|
): Extract<RequestView, { purpose: 'assistant' }> {
|
||||||
return header === undefined
|
return header === undefined
|
||||||
? request
|
? request
|
||||||
@@ -53,7 +54,7 @@ function applyHeader(
|
|||||||
...request,
|
...request,
|
||||||
prompt: header.prompt,
|
prompt: header.prompt,
|
||||||
requestConfig: header.prompt.config,
|
requestConfig: header.prompt.config,
|
||||||
...(header.change === undefined ? {} : { promptChange: header.change }),
|
...(includeChange && header.change !== undefined ? { promptChange: header.change } : {}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +151,7 @@ export class TrajectorySnapshotBuilder implements ConversationViewBuilder<
|
|||||||
const boundaries: { seq: number; time: number }[] = []
|
const boundaries: { seq: number; time: number }[] = []
|
||||||
const turnEndings: { turn: number; time: number; error?: string }[] = []
|
const turnEndings: { turn: number; time: number; error?: string }[] = []
|
||||||
const callSchemas = new Map<string, ConversationPromptSnapshot['tools'][number]>()
|
const callSchemas = new Map<string, ConversationPromptSnapshot['tools'][number]>()
|
||||||
|
const consumedPromptChanges = new Set<number>()
|
||||||
let partial: TrajectorySnapshot['partial'] = null
|
let partial: TrajectorySnapshot['partial'] = null
|
||||||
const runningCalls: TrajectorySnapshot['runningCalls'][number][] = []
|
const runningCalls: TrajectorySnapshot['runningCalls'][number][] = []
|
||||||
|
|
||||||
@@ -163,7 +165,12 @@ export class TrajectorySnapshotBuilder implements ConversationViewBuilder<
|
|||||||
const header = data.request === undefined ? undefined : headerFor(data.request, headers)
|
const header = data.request === undefined ? undefined : headerFor(data.request, headers)
|
||||||
if (data.node !== undefined) finalized.push(withRequestConfig(data.node, header?.prompt))
|
if (data.node !== undefined) finalized.push(withRequestConfig(data.node, header?.prompt))
|
||||||
if (data.partial !== null) partial = data.partial
|
if (data.partial !== null) partial = data.partial
|
||||||
if (data.request !== undefined) requests.push(applyHeader(data.request, header))
|
if (data.request !== undefined) {
|
||||||
|
const includeChange = header?.change !== undefined
|
||||||
|
&& !consumedPromptChanges.has(header.seq)
|
||||||
|
requests.push(applyHeader(data.request, header, includeChange))
|
||||||
|
if (includeChange) consumedPromptChanges.add(header.seq)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (data.kind === 'tool') {
|
if (data.kind === 'tool') {
|
||||||
|
|||||||
62
packages/client/ui-trajectory/tests/snapshot-builder.spec.ts
Normal file
62
packages/client/ui-trajectory/tests/snapshot-builder.spec.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import type { RequestView } from '@deepseek-ai/dsh-client-runtime/client'
|
||||||
|
import type { TrajectoryConversationViewNode } from '../src/client/trajectory-contract.ts'
|
||||||
|
import { TrajectorySnapshotBuilder } from '../src/client/trajectory-snapshot-builder.ts'
|
||||||
|
|
||||||
|
function assistantRequest(startSeq: number, step: number): Extract<RequestView, { purpose: 'assistant' }> {
|
||||||
|
return {
|
||||||
|
purpose: 'assistant',
|
||||||
|
startSeq,
|
||||||
|
turn: 1,
|
||||||
|
step,
|
||||||
|
startedAt: startSeq,
|
||||||
|
completedAt: startSeq + 1,
|
||||||
|
status: 'complete',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('TrajectorySnapshotBuilder', () => {
|
||||||
|
it('inherits one request header across requests without repeating its prompt change', () => {
|
||||||
|
const prompt = {
|
||||||
|
config: { provider: 'test', model: 'test' },
|
||||||
|
system: 'one initial prompt',
|
||||||
|
tools: [],
|
||||||
|
}
|
||||||
|
const nodes: TrajectoryConversationViewNode[] = [
|
||||||
|
{
|
||||||
|
key: 'header',
|
||||||
|
kind: 'trajectory-request-header',
|
||||||
|
id: '2',
|
||||||
|
target: 'trajectory',
|
||||||
|
anchorSeq: 2,
|
||||||
|
data: {
|
||||||
|
kind: 'request-header',
|
||||||
|
header: {
|
||||||
|
seq: 2,
|
||||||
|
time: 2,
|
||||||
|
prompt,
|
||||||
|
change: { seq: 2, time: 2, kind: 'initial' },
|
||||||
|
location: { kind: 'session' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...[assistantRequest(3, 1), assistantRequest(5, 2)].map(request => ({
|
||||||
|
key: `assistant:${request.step}`,
|
||||||
|
kind: 'trajectory-assistant-step',
|
||||||
|
id: `1:${request.step}`,
|
||||||
|
target: 'trajectory' as const,
|
||||||
|
anchorSeq: request.startSeq,
|
||||||
|
data: { kind: 'assistant' as const, partial: null, request },
|
||||||
|
})),
|
||||||
|
]
|
||||||
|
|
||||||
|
const snapshot = new TrajectorySnapshotBuilder().replace({ nodes })
|
||||||
|
|
||||||
|
expect(snapshot.requests.map(request => request.purpose === 'assistant'
|
||||||
|
? request.prompt?.system
|
||||||
|
: undefined)).toEqual(['one initial prompt', 'one initial prompt'])
|
||||||
|
expect(snapshot.requests.map(request => request.purpose === 'assistant'
|
||||||
|
? request.promptChange?.kind
|
||||||
|
: undefined)).toEqual(['initial', undefined])
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user