mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
* feat: render an AI agent result as its answer, not as raw JSON Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: sanitize agent markdown through the shared plugin chain Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: fold agent stream events incrementally per poll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: separate the agent meta line from the result toggle group Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: add a transcript view of an agent run's conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: retire AIAgentLogViewer in favour of the transcript Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show the partial transcript a max-iterations failure carries Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: move the agent meta line and system prompt below the conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: show an agent run as what it did, not as a conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: name the agent run breakdown a trace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): label the save-as-agent form fields per the guidelines Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): reuse the resource form's path and description fields Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): keep the action tags on a max-iterations failure Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): keep offline replay inert and the streamed answer to one turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): reset the streamed answer on providers that skip tool_call Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: carry the event type narrowing through the stream parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: survive a malformed message rather than take the viewer down Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: coerce agent messages once at the parse boundary Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show an agent run as one scroll ending in its output Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep every streamed turn instead of dropping the narration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: share the chat divider and drop the unsafe run auto-scroll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: give the labelled divider a border colour and the standard pretty icon Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: pad the agent run below its badges as well as above Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: follow a streaming run's pane without moving the page Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: share the chat's stick-to-bottom mechanics with the agent run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the answer's citations and end a turn's reasoning with the turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: read the agent stream through the windmill-chat sdk parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * 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> * fix: stop a stream the fold cannot use from claiming the run pane Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: identify an agent run by more than the job id the replay withholds Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: drop the tests and comment lines that were not earning their place Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a streamed turn's text shifting when a tool call closes it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
128 lines
3.7 KiB
Svelte
128 lines
3.7 KiB
Svelte
<script lang="ts">
|
|
import LogViewer from './LogViewer.svelte'
|
|
import JobProgressBar from '$lib/components/jobs/JobProgressBar.svelte'
|
|
import ScriptFix from './copilot/ScriptFix.svelte'
|
|
import type DiffEditor from './DiffEditor.svelte'
|
|
import type Editor from './Editor.svelte'
|
|
import { type Script, type Job, type FlowModule } from '$lib/gen'
|
|
import OutputPickerInner from '$lib/components/flows/propPicker/OutputPickerInner.svelte'
|
|
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
|
import type { FlowEditorContext, OutputViewerJob } from './flows/types'
|
|
import { getContext } from 'svelte'
|
|
import { getStringError } from './copilot/chat/utils'
|
|
|
|
interface Props {
|
|
lang: Script['language']
|
|
editor: Editor | undefined
|
|
diffEditor: DiffEditor | undefined
|
|
loopStatus?: { type: 'inside' | 'self'; flow: 'forloopflow' | 'whileloopflow' } | undefined
|
|
testJob?: Job & { result_stream?: string }
|
|
scriptProgress?: number | undefined
|
|
mod: FlowModule
|
|
testIsLoading?: boolean
|
|
disableMock?: boolean
|
|
disableHistory?: boolean
|
|
onUpdateMock?: (mock: { enabled: boolean; return_value?: unknown }) => void
|
|
loadingJob?: boolean
|
|
tagLabel?: string
|
|
}
|
|
|
|
let {
|
|
lang,
|
|
editor,
|
|
diffEditor,
|
|
loopStatus = undefined,
|
|
scriptProgress = $bindable(undefined),
|
|
testJob = undefined,
|
|
mod,
|
|
testIsLoading = false,
|
|
disableMock = false,
|
|
disableHistory = false,
|
|
onUpdateMock,
|
|
loadingJob = false,
|
|
tagLabel = undefined
|
|
}: Props = $props()
|
|
|
|
const { stepsInputArgs, flowStateStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
|
|
|
let outputPickerInner: OutputPickerInner | undefined = $state(undefined)
|
|
export function getOutputPickerInner() {
|
|
return outputPickerInner
|
|
}
|
|
|
|
const selectedJob: OutputViewerJob = $derived.by(
|
|
() => outputPickerInner?.getSelectedJob?.() ?? undefined
|
|
)
|
|
const logJob = $derived(testJob ?? selectedJob)
|
|
const preview = $derived.by(() => outputPickerInner?.getPreview?.())
|
|
</script>
|
|
|
|
<Splitpanes horizontal>
|
|
<Pane size={65} minSize={10} class="text-sm text-primary">
|
|
{#if scriptProgress}
|
|
<JobProgressBar job={testJob} {scriptProgress} compact={true} />
|
|
{/if}
|
|
|
|
<OutputPickerInner
|
|
{testJob}
|
|
fullResult
|
|
moduleId={mod.id}
|
|
closeOnOutsideClick={true}
|
|
getLogs
|
|
{onUpdateMock}
|
|
mock={mod.mock}
|
|
isLoading={testIsLoading || loadingJob}
|
|
path={`path` in mod.value ? mod.value.path : ''}
|
|
{loopStatus}
|
|
{disableMock}
|
|
{disableHistory}
|
|
bind:this={outputPickerInner}
|
|
>
|
|
{#snippet copilot_fix()}
|
|
{@const stepError =
|
|
selectedJob?.type === 'CompletedJob' && !selectedJob.success
|
|
? getStringError(selectedJob.result)
|
|
: undefined}
|
|
{#if lang && editor && diffEditor && stepsInputArgs.getStepArgs(mod.id) && stepError}
|
|
<ScriptFix {lang} error={stepError} jobId={selectedJob?.id} moduleId={mod.id} />
|
|
{/if}
|
|
{/snippet}
|
|
</OutputPickerInner>
|
|
</Pane>
|
|
<Pane size={35} minSize={10}>
|
|
{#if (mod.mock?.enabled && preview !== 'job' && testJob?.type !== 'QueuedJob') || preview === 'mock'}
|
|
<LogViewer
|
|
small
|
|
content={undefined}
|
|
isLoading={false}
|
|
tag={undefined}
|
|
customEmptyMessage="Using pinned data"
|
|
{tagLabel}
|
|
/>
|
|
{:else}
|
|
<LogViewer
|
|
small
|
|
jobId={logJob?.id}
|
|
duration={logJob?.['duration_ms']}
|
|
mem={logJob?.['mem_peak']}
|
|
content={logJob?.['logs']}
|
|
isLoading={(testIsLoading && logJob?.['running'] == false) || loadingJob}
|
|
tag={logJob?.['tag']}
|
|
{tagLabel}
|
|
onLogsResolved={(logs) => {
|
|
if (
|
|
mod.id &&
|
|
flowStateStore?.val[mod.id] &&
|
|
flowStateStore.val[mod.id].previewJobId === logJob?.id
|
|
) {
|
|
flowStateStore.val[mod.id] = {
|
|
...flowStateStore.val[mod.id],
|
|
previewLogs: logs
|
|
}
|
|
}
|
|
}}
|
|
/>
|
|
{/if}
|
|
</Pane>
|
|
</Splitpanes>
|