mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 16:02:24 +00:00
feat(flow chat): add cancel button (#6869)
* feat(flow chat): add cancel button Add cancel button to flow chat interface that appears when a flow is executing. - Replace send button with red stop button when processing - Wire up cancel functionality to stop flow execution - Support both polling and streaming modes - Fixes #6868 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> * fix --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Button, Alert } from '$lib/components/common'
|
||||
import { MessageCircle, Loader2, ArrowUp } from 'lucide-svelte'
|
||||
import { MessageCircle, Loader2, ArrowUp, Square } from 'lucide-svelte'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import autosize from '$lib/autosize'
|
||||
import FlowChatMessage from './FlowChatMessage.svelte'
|
||||
@@ -123,16 +123,28 @@
|
||||
rows={3}
|
||||
></textarea>
|
||||
<div class="flex-shrink-0 pr-2">
|
||||
<Button
|
||||
color="blue"
|
||||
size="xs2"
|
||||
btnClasses="!rounded-full !p-1.5"
|
||||
startIcon={{ icon: ArrowUp }}
|
||||
disabled={!manager.inputMessage?.trim() || manager.isLoading || deploymentInProgress}
|
||||
on:click={() => manager.sendMessage()}
|
||||
iconOnly
|
||||
title={deploymentInProgress ? 'Deployment in progress' : 'Send message (Enter)'}
|
||||
/>
|
||||
{#if manager.isWaitingForResponse || manager.isLoading}
|
||||
<Button
|
||||
color="red"
|
||||
size="xs2"
|
||||
btnClasses="!rounded-full !p-1.5"
|
||||
startIcon={{ icon: Square }}
|
||||
on:click={() => manager.cancelCurrentJob()}
|
||||
iconOnly
|
||||
title="Cancel execution"
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
color="blue"
|
||||
size="xs2"
|
||||
btnClasses="!rounded-full !p-1.5"
|
||||
startIcon={{ icon: ArrowUp }}
|
||||
disabled={!manager.inputMessage?.trim() || manager.isLoading || deploymentInProgress}
|
||||
on:click={() => manager.sendMessage()}
|
||||
iconOnly
|
||||
title={deploymentInProgress ? 'Deployment in progress' : 'Send message (Enter)'}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FlowConversationMessage } from '$lib/gen/types.gen'
|
||||
import { FlowConversationService } from '$lib/gen'
|
||||
import { FlowConversationService, JobService } from '$lib/gen'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { waitJob } from '$lib/components/waitJob'
|
||||
import { tick } from 'svelte'
|
||||
@@ -32,6 +32,7 @@ class FlowChatManager {
|
||||
loadingMoreMessages = $state(false)
|
||||
currentEventSource = $state<EventSource | undefined>(undefined)
|
||||
pollingInterval = $state<ReturnType<typeof setInterval> | undefined>(undefined)
|
||||
currentJobId = $state<string | undefined>(undefined)
|
||||
|
||||
// Private state
|
||||
#conversationsCache = $state<Record<string, ChatMessage[]>>({})
|
||||
@@ -69,6 +70,7 @@ class FlowChatManager {
|
||||
this.stopPolling()
|
||||
this.isLoading = false
|
||||
this.isWaitingForResponse = false
|
||||
this.currentJobId = undefined
|
||||
}
|
||||
|
||||
// Public methods for component to call
|
||||
@@ -86,6 +88,28 @@ class FlowChatManager {
|
||||
this.page = 1
|
||||
}
|
||||
|
||||
async cancelCurrentJob() {
|
||||
if (!this.#workspace) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.currentJobId) {
|
||||
await JobService.cancelQueuedJob({
|
||||
workspace: this.#workspace,
|
||||
id: this.currentJobId,
|
||||
requestBody: {}
|
||||
})
|
||||
sendUserToast(`Job ${this.currentJobId} cancelled`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error cancelling job:', error)
|
||||
sendUserToast('Could not cancel job', true)
|
||||
} finally {
|
||||
this.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
async loadConversationMessages(conversationId?: string) {
|
||||
this.page = 1
|
||||
await this.loadMessages(true, conversationId)
|
||||
@@ -370,6 +394,9 @@ class FlowChatManager {
|
||||
const data = JSON.parse(event.data)
|
||||
|
||||
if (data.type === 'update') {
|
||||
if (data.flow_stream_job_id) {
|
||||
this.currentJobId = data.flow_stream_job_id
|
||||
}
|
||||
// Process new stream content
|
||||
if (data.new_result_stream) {
|
||||
// Stop polling since we are receiving last step streaming
|
||||
@@ -476,6 +503,9 @@ class FlowChatManager {
|
||||
return
|
||||
}
|
||||
|
||||
// Store the current job ID so it can be cancelled
|
||||
this.currentJobId = jobId
|
||||
|
||||
if (isNewConversation) {
|
||||
await this.#refreshConversations?.()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user