refactor(cli-run): stream reasoning text instead of summarized thinking line
Replace the single-line "Thinking: <summary>" rendering with direct streaming of reasoning tokens via writePaddedText. Removes maybePrintThinkingLine and renderThinkingLine in favor of incremental output with dim styling. 🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
@@ -20,7 +20,6 @@ import {
|
||||
closeThinkBlock,
|
||||
openThinkBlock,
|
||||
renderAgentHeader,
|
||||
renderThinkingLine,
|
||||
writePaddedText,
|
||||
} from "./output-renderer"
|
||||
|
||||
@@ -111,10 +110,15 @@ export function handleMessagePartUpdated(ctx: RunContext, payload: EventPayload,
|
||||
|
||||
if (part.type === "reasoning") {
|
||||
ensureThinkBlockOpen(state)
|
||||
const reasoningText = part.text ?? state.lastReasoningText
|
||||
maybePrintThinkingLine(state, reasoningText)
|
||||
const reasoningText = part.text ?? ""
|
||||
const newText = reasoningText.slice(state.lastReasoningText.length)
|
||||
if (newText) {
|
||||
const padded = writePaddedText(newText, state.thinkingAtLineStart)
|
||||
process.stdout.write(pc.dim(padded.output))
|
||||
state.thinkingAtLineStart = padded.atLineStart
|
||||
state.hasReceivedMeaningfulWork = true
|
||||
}
|
||||
state.lastReasoningText = reasoningText
|
||||
state.hasReceivedMeaningfulWork = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -157,9 +161,10 @@ export function handleMessagePartDelta(ctx: RunContext, payload: EventPayload, s
|
||||
|
||||
if (partType === "reasoning") {
|
||||
ensureThinkBlockOpen(state)
|
||||
const nextReasoningText = `${state.lastReasoningText}${delta}`
|
||||
maybePrintThinkingLine(state, nextReasoningText)
|
||||
state.lastReasoningText = nextReasoningText
|
||||
const padded = writePaddedText(delta, state.thinkingAtLineStart)
|
||||
process.stdout.write(pc.dim(padded.output))
|
||||
state.thinkingAtLineStart = padded.atLineStart
|
||||
state.lastReasoningText += delta
|
||||
state.hasReceivedMeaningfulWork = true
|
||||
return
|
||||
}
|
||||
@@ -226,6 +231,7 @@ export function handleMessageUpdated(ctx: RunContext, payload: EventPayload, sta
|
||||
state.hasPrintedThinkingLine = false
|
||||
state.lastThinkingSummary = ""
|
||||
state.textAtLineStart = true
|
||||
state.thinkingAtLineStart = false
|
||||
closeThinkBlockIfNeeded(state)
|
||||
|
||||
const agent = props?.info?.agent ?? null
|
||||
@@ -298,6 +304,7 @@ function ensureThinkBlockOpen(state: EventState): void {
|
||||
openThinkBlock()
|
||||
state.inThinkBlock = true
|
||||
state.hasPrintedThinkingLine = false
|
||||
state.thinkingAtLineStart = false
|
||||
}
|
||||
|
||||
function closeThinkBlockIfNeeded(state: EventState): void {
|
||||
@@ -306,19 +313,5 @@ function closeThinkBlockIfNeeded(state: EventState): void {
|
||||
state.inThinkBlock = false
|
||||
state.lastThinkingLineWidth = 0
|
||||
state.lastThinkingSummary = ""
|
||||
}
|
||||
|
||||
function maybePrintThinkingLine(state: EventState, text: string): void {
|
||||
const normalized = text.replace(/\s+/g, " ").trim()
|
||||
if (!normalized) return
|
||||
|
||||
const summary = normalized
|
||||
if (summary === state.lastThinkingSummary) return
|
||||
|
||||
state.lastThinkingLineWidth = renderThinkingLine(
|
||||
summary,
|
||||
state.lastThinkingLineWidth,
|
||||
)
|
||||
state.lastThinkingSummary = summary
|
||||
state.hasPrintedThinkingLine = true
|
||||
state.thinkingAtLineStart = false
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ export interface EventState {
|
||||
lastThinkingSummary: string
|
||||
/** Whether text stream is currently at line start (for padding) */
|
||||
textAtLineStart: boolean
|
||||
/** Whether reasoning stream is currently at line start (for padding) */
|
||||
thinkingAtLineStart: boolean
|
||||
}
|
||||
|
||||
export function createEventState(): EventState {
|
||||
@@ -60,5 +62,6 @@ export function createEventState(): EventState {
|
||||
messageRoleById: {},
|
||||
lastThinkingSummary: "",
|
||||
textAtLineStart: true,
|
||||
thinkingAtLineStart: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,8 +215,7 @@ describe("message.part.delta handling", () => {
|
||||
|
||||
//#then
|
||||
const rendered = stdoutSpy.mock.calls.map((call) => String(call[0] ?? "")).join("")
|
||||
expect(rendered).toContain("\r")
|
||||
expect(rendered).toContain("Thinking: Composing final summary")
|
||||
expect(rendered).toContain("┃ Thinking: Composing final summary")
|
||||
expect(rendered).toContain("in Korean with specifics.")
|
||||
|
||||
if (previous !== undefined) process.env.GITHUB_ACTIONS = previous
|
||||
@@ -282,7 +281,7 @@ describe("message.part.delta handling", () => {
|
||||
//#then
|
||||
const rendered = stdoutSpy.mock.calls.map((call) => String(call[0] ?? "")).join("")
|
||||
const renderCount = rendered.split("Thinking:").length - 1
|
||||
expect(renderCount).toBe(2)
|
||||
expect(renderCount).toBe(1)
|
||||
|
||||
if (previous !== undefined) process.env.GITHUB_ACTIONS = previous
|
||||
stdoutSpy.mockRestore()
|
||||
|
||||
@@ -25,29 +25,11 @@ export function renderAgentHeader(
|
||||
}
|
||||
|
||||
export function openThinkBlock(): void {
|
||||
return
|
||||
process.stdout.write("\n ┃ Thinking: ")
|
||||
}
|
||||
|
||||
export function closeThinkBlock(): void {
|
||||
process.stdout.write("\n")
|
||||
}
|
||||
|
||||
export function renderThinkingLine(
|
||||
summary: string,
|
||||
previousWidth: number,
|
||||
): number {
|
||||
const line = ` ┃ Thinking: ${summary} `
|
||||
const isGitHubActions = process.env.GITHUB_ACTIONS === "true"
|
||||
|
||||
if (isGitHubActions) {
|
||||
process.stdout.write(`${pc.dim(line)}\n`)
|
||||
return line.length
|
||||
}
|
||||
|
||||
const minPadding = 6
|
||||
const clearPadding = Math.max(previousWidth - line.length + minPadding, minPadding)
|
||||
process.stdout.write(`\r${pc.dim(line)}${" ".repeat(clearPadding)}`)
|
||||
return line.length
|
||||
process.stdout.write(" \n")
|
||||
}
|
||||
|
||||
export function writePaddedText(
|
||||
|
||||
Reference in New Issue
Block a user