mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-14 09:11:54 +00:00
fix(tui): preserve completed thinking panel
This commit is contained in:
parent
4a21920b5e
commit
bb59d3bac2
3 changed files with 31 additions and 25 deletions
|
|
@ -82,13 +82,12 @@ describe('createGatewayEventHandler', () => {
|
|||
type: 'message.complete'
|
||||
} as any)
|
||||
|
||||
expect(appended).toHaveLength(3)
|
||||
expect(appended).toHaveLength(2)
|
||||
expect(appended[0]).toMatchObject({ kind: 'trail', role: 'system', text: '', thinking: 'mapped the page' })
|
||||
expect(appended[1]).toMatchObject({ kind: 'trail', role: 'system', text: '' })
|
||||
expect(appended[1]?.tools).toHaveLength(1)
|
||||
expect(appended[1]?.tools?.[0]).toContain('hero cards')
|
||||
expect(appended[1]?.toolTokens).toBeGreaterThan(0)
|
||||
expect(appended[2]).toMatchObject({ role: 'assistant', text: 'final answer' })
|
||||
expect(appended[0]?.tools).toHaveLength(1)
|
||||
expect(appended[0]?.tools?.[0]).toContain('hero cards')
|
||||
expect(appended[0]?.toolTokens).toBeGreaterThan(0)
|
||||
expect(appended[1]).toMatchObject({ role: 'assistant', text: 'final answer' })
|
||||
})
|
||||
|
||||
it('keeps tool tokens across handler recreation mid-turn', () => {
|
||||
|
|
@ -116,10 +115,10 @@ describe('createGatewayEventHandler', () => {
|
|||
type: 'message.complete'
|
||||
} as any)
|
||||
|
||||
expect(appended).toHaveLength(3)
|
||||
expect(appended[1]?.tools).toHaveLength(1)
|
||||
expect(appended[1]?.toolTokens).toBeGreaterThan(0)
|
||||
expect(appended[2]).toMatchObject({ role: 'assistant', text: 'final answer' })
|
||||
expect(appended).toHaveLength(2)
|
||||
expect(appended[0]?.tools).toHaveLength(1)
|
||||
expect(appended[0]?.toolTokens).toBeGreaterThan(0)
|
||||
expect(appended[1]).toMatchObject({ role: 'assistant', text: 'final answer' })
|
||||
})
|
||||
|
||||
it('streams legacy thinking.delta into visible reasoning state', () => {
|
||||
|
|
@ -136,6 +135,21 @@ describe('createGatewayEventHandler', () => {
|
|||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('preserves streamed reasoning as one completed thinking panel after segment flushes', () => {
|
||||
const appended: Msg[] = []
|
||||
const streamed = 'first reasoning chunk\nsecond reasoning chunk'
|
||||
|
||||
const onEvent = createGatewayEventHandler(buildCtx(appended))
|
||||
|
||||
onEvent({ payload: { text: streamed }, type: 'reasoning.delta' } as any)
|
||||
onEvent({ payload: { text: 'Before edit.' }, type: 'message.delta' } as any)
|
||||
turnController.flushStreamingSegment()
|
||||
onEvent({ payload: { text: 'final answer' }, type: 'message.complete' } as any)
|
||||
|
||||
expect(appended.map(msg => msg.thinking).filter(Boolean)).toEqual([streamed])
|
||||
expect(appended[appended.length - 1]).toMatchObject({ role: 'assistant', text: 'final answer' })
|
||||
})
|
||||
|
||||
it('ignores fallback reasoning.available when streamed reasoning already exists', () => {
|
||||
const appended: Msg[] = []
|
||||
const streamed = 'short streamed reasoning'
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ class TurnController {
|
|||
persistSpawnTree?: (subagents: SubagentProgress[], sessionId: null | string) => Promise<void>
|
||||
protocolWarned = false
|
||||
reasoningText = ''
|
||||
reasoningSegmentOffset = 0
|
||||
segmentMessages: Msg[] = []
|
||||
pendingSegmentTools: string[] = []
|
||||
statusTimer: Timer = null
|
||||
|
|
@ -107,7 +106,6 @@ class TurnController {
|
|||
clearReasoning() {
|
||||
this.reasoningTimer = clear(this.reasoningTimer)
|
||||
this.reasoningText = ''
|
||||
this.reasoningSegmentOffset = 0
|
||||
this.toolTokenAcc = 0
|
||||
patchTurnState({ reasoning: '', reasoningTokens: 0, toolTokens: 0 })
|
||||
}
|
||||
|
|
@ -202,15 +200,10 @@ class TurnController {
|
|||
patchTurnState({ reasoning: this.reasoningText, reasoningTokens: estimateTokensRough(this.reasoningText) })
|
||||
}
|
||||
|
||||
const thinking = this.reasoningText.slice(this.reasoningSegmentOffset).trim()
|
||||
const msg: Msg = {
|
||||
role: split.text ? 'assistant' : 'system',
|
||||
text: split.text,
|
||||
...(!split.text && { kind: 'trail' as const }),
|
||||
...(thinking && {
|
||||
thinking,
|
||||
thinkingTokens: estimateTokensRough(thinking)
|
||||
}),
|
||||
...(this.pendingSegmentTools.length && { tools: this.pendingSegmentTools })
|
||||
}
|
||||
|
||||
|
|
@ -220,7 +213,6 @@ class TurnController {
|
|||
this.segmentMessages = [...this.segmentMessages, msg]
|
||||
}
|
||||
|
||||
this.reasoningSegmentOffset = this.reasoningText.length
|
||||
this.pendingSegmentTools = []
|
||||
this.bufRef = ''
|
||||
patchTurnState({ streamPendingTools: [], streamSegments: this.segmentMessages, streaming: '' })
|
||||
|
|
@ -329,7 +321,7 @@ class TurnController {
|
|||
return body === null || (!finalHasOwnDiffFence && !finalText.includes(body))
|
||||
})
|
||||
|
||||
const finalThinking = savedReasoning.slice(this.reasoningSegmentOffset).trim()
|
||||
const finalThinking = savedReasoning.trim()
|
||||
const finalDetails: Msg = {
|
||||
kind: 'trail',
|
||||
role: 'system',
|
||||
|
|
|
|||
|
|
@ -646,22 +646,22 @@ export const Thinking = memo(function Thinking({
|
|||
{preview ? (
|
||||
mode === 'full' ? (
|
||||
lines.map((line, index) => (
|
||||
<Text color={t.color.dim} dim key={index} wrap="wrap-trim">
|
||||
<Text color={t.color.cornsilk} key={index} wrap="wrap-trim">
|
||||
{line || ' '}
|
||||
{index === lines.length - 1 ? (
|
||||
<StreamCursor color={t.color.dim} dimColor streaming={streaming} visible={active} />
|
||||
<StreamCursor color={t.color.cornsilk} streaming={streaming} visible={active} />
|
||||
) : null}
|
||||
</Text>
|
||||
))
|
||||
) : (
|
||||
<Text color={t.color.dim} dim wrap="truncate-end">
|
||||
<Text color={t.color.cornsilk} wrap="truncate-end">
|
||||
{preview}
|
||||
<StreamCursor color={t.color.dim} dimColor streaming={streaming} visible={active} />
|
||||
<StreamCursor color={t.color.cornsilk} streaming={streaming} visible={active} />
|
||||
</Text>
|
||||
)
|
||||
) : (
|
||||
<Text color={t.color.dim} dim>
|
||||
<StreamCursor color={t.color.dim} dimColor streaming={streaming} visible={active} />
|
||||
<Text color={t.color.cornsilk}>
|
||||
<StreamCursor color={t.color.cornsilk} streaming={streaming} visible={active} />
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue