mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
fix: show an agent run's thinking instead of falling back to raw json
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d4067a0e40
commit
e1ab81ba72
@@ -3,6 +3,7 @@
|
||||
import { Badge } from '$lib/components/common'
|
||||
import GfmMarkdown from './GfmMarkdown.svelte'
|
||||
import AgentTrace from './AgentTrace.svelte'
|
||||
import ChatCollapsibleCard from './copilot/chat/ChatCollapsibleCard.svelte'
|
||||
import LabeledDivider from './LabeledDivider.svelte'
|
||||
import WebSearchSourcesDisplay from './copilot/chat/WebSearchSourcesDisplay.svelte'
|
||||
import { buildAgentTrace, splitFinalAnswer } from './agentTrace'
|
||||
@@ -35,6 +36,8 @@
|
||||
// answered itself. Its citations move down with it.
|
||||
let answer = $derived(splitFinalAnswer(buildAgentTrace(result.messages), result.output))
|
||||
let trace = $derived(answer.trace)
|
||||
let reasoning = $derived(result.reasoning?.trim())
|
||||
let reasoningExpanded = $state(false)
|
||||
|
||||
let anchor: HTMLElement | undefined = $state()
|
||||
const sticker = createBottomSticker()
|
||||
@@ -43,6 +46,7 @@
|
||||
// so the end has moved from wherever the stream had the reader parked.
|
||||
runKey
|
||||
trace.length
|
||||
reasoning
|
||||
sticker.scrollToEnd(runPane(anchor))
|
||||
})
|
||||
</script>
|
||||
@@ -50,6 +54,20 @@
|
||||
<div bind:this={anchor} class="flex flex-col w-full py-3">
|
||||
{#if trace.length > 0}
|
||||
<AgentTrace entries={trace} {workspaceId} />
|
||||
{/if}
|
||||
{#if reasoning}
|
||||
<!-- The whole run's thinking, which the worker hands back joined rather than
|
||||
per iteration, so it reads as one block above the answer it led to. -->
|
||||
<ChatCollapsibleCard
|
||||
label="Thinking"
|
||||
expanded={reasoningExpanded}
|
||||
onToggle={() => (reasoningExpanded = !reasoningExpanded)}
|
||||
contentClass="font-main"
|
||||
>
|
||||
<GfmMarkdown md={reasoning} prose="xs" noPadding />
|
||||
</ChatCollapsibleCard>
|
||||
{/if}
|
||||
{#if trace.length > 0 || reasoning}
|
||||
<LabeledDivider class="my-3">
|
||||
<span class="text-2xs text-hint">Output</span>
|
||||
</LabeledDivider>
|
||||
|
||||
@@ -22,6 +22,11 @@ describe('parseAgentResult', () => {
|
||||
it('accepts the envelope with and without its optional keys', () => {
|
||||
expect(parseAgentResult(envelope)?.output).toBe('the answer')
|
||||
expect(parseAgentResult({ output: 1, messages: [{ role: 'user' }] })?.messages).toHaveLength(1)
|
||||
// A key the worker serializes and the signature does not know costs every run
|
||||
// carrying it the pretty display, so each one is pinned here.
|
||||
expect(parseAgentResult({ ...envelope, reasoning: 'let me think' })?.reasoning).toBe(
|
||||
'let me think'
|
||||
)
|
||||
})
|
||||
|
||||
// The signature is the only thing separating an agent result from any other
|
||||
|
||||
@@ -31,10 +31,16 @@ export type AgentResult = {
|
||||
messages: AgentMessage[]
|
||||
usage?: AgentTokenUsage
|
||||
wm_stream?: string
|
||||
/** The model's thinking across every iteration, blank-line separated. */
|
||||
reasoning?: string
|
||||
}
|
||||
|
||||
/** Every key `AIAgentResult` can serialize. `usage` and `wm_stream` are skipped when empty. */
|
||||
const ENVELOPE_KEYS = ['output', 'messages', 'usage', 'wm_stream']
|
||||
/**
|
||||
* Every key `AIAgentResult` can serialize; the optional ones are skipped when
|
||||
* empty. The signature below is closed, so a key the worker gains and this list
|
||||
* does not makes every run carrying it fall back to the raw JSON.
|
||||
*/
|
||||
const ENVELOPE_KEYS = ['output', 'messages', 'usage', 'wm_stream', 'reasoning']
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
@@ -129,7 +135,8 @@ export function parseAgentResult(result: unknown): AgentResult | undefined {
|
||||
output: result.output,
|
||||
messages: toAgentMessages(result.messages),
|
||||
usage: isRecord(result.usage) ? (result.usage as AgentTokenUsage) : undefined,
|
||||
wm_stream: typeof result.wm_stream === 'string' ? result.wm_stream : undefined
|
||||
wm_stream: typeof result.wm_stream === 'string' ? result.wm_stream : undefined,
|
||||
reasoning: typeof result.reasoning === 'string' ? result.reasoning : undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user