fix: Distinguish streaming vs incomplete tool calls in UI

This commit is contained in:
Aleksander Grygier 2026-01-12 11:14:53 +01:00
parent 3179858e5f
commit da8baaa9b8
2 changed files with 43 additions and 9 deletions

View File

@ -13,8 +13,7 @@
SyntaxHighlightedCode
} from '$lib/components/app';
import { config } from '$lib/stores/settings.svelte';
import { agenticStreamingToolCall } from '$lib/stores/agentic.svelte';
import { Wrench, Loader2 } from '@lucide/svelte';
import { Wrench, Loader2, AlertTriangle } from '@lucide/svelte';
import { AgenticSectionType } from '$lib/enums';
import { AGENTIC_TAGS, AGENTIC_REGEX } from '$lib/constants/agentic';
import { formatJsonPretty } from '$lib/utils/formatters';
@ -24,7 +23,6 @@
interface Props {
content: string;
isStreaming?: boolean;
toolCallTimings?: ChatMessageToolCallTiming[];
}
interface AgenticSection {
@ -35,7 +33,7 @@
toolResult?: string;
}
let { content, isStreaming = false, toolCallTimings = [] }: Props = $props();
let { content, isStreaming = false }: Props = $props();
const sections = $derived(parseAgenticContent(content));
@ -198,6 +196,46 @@
<div class="agentic-text">
<MarkdownContent content={section.content} />
</div>
{:else if section.type === AgenticSectionType.TOOL_CALL_STREAMING}
{@const streamingIcon = isStreaming ? Loader2 : AlertTriangle}
{@const streamingIconClass = isStreaming ? 'h-4 w-4 animate-spin' : 'h-4 w-4 text-yellow-500'}
{@const streamingSubtitle = isStreaming ? 'streaming...' : 'incomplete'}
<CollapsibleContentBlock
open={isExpanded(index, true)}
class="my-2"
icon={streamingIcon}
iconClass={streamingIconClass}
title={section.toolName || 'Tool call'}
subtitle={streamingSubtitle}
onToggle={() => toggleExpanded(index, true)}
>
<div class="pt-3">
<div class="my-3 flex items-center gap-2 text-xs text-muted-foreground">
<span>Arguments:</span>
{#if isStreaming}
<Loader2 class="h-3 w-3 animate-spin" />
{/if}
</div>
{#if section.toolArgs}
<SyntaxHighlightedCode
code={formatJsonPretty(section.toolArgs)}
language="json"
maxHeight="20rem"
class="text-xs"
/>
{:else if isStreaming}
<div class="rounded bg-muted/30 p-2 text-xs text-muted-foreground italic">
Receiving arguments...
</div>
{:else}
<div
class="rounded bg-yellow-500/10 p-2 text-xs text-yellow-600 italic dark:text-yellow-400"
>
Response was truncated
</div>
{/if}
</div>
</CollapsibleContentBlock>
{:else if section.type === AgenticSectionType.TOOL_CALL || section.type === AgenticSectionType.TOOL_CALL_PENDING}
{@const isPending = section.type === AgenticSectionType.TOOL_CALL_PENDING}
{@const toolIcon = isPending ? Loader2 : Wrench}

View File

@ -183,11 +183,7 @@
{#if showRawOutput}
<pre class="raw-output">{messageContent || ''}</pre>
{:else if isAgenticContent}
<AgenticContent
content={messageContent || ''}
isStreaming={isChatStreaming()}
toolCallTimings={message.timings?.agentic?.toolCalls}
/>
<AgenticContent content={messageContent || ''} isStreaming={isChatStreaming()} />
{:else}
<MarkdownContent content={messageContent || ''} />
{/if}