Files
agent-framework/python/packages/devui/frontend/src/hooks/useCancellableRequest.ts
T
Victor Dibia 411ee7a60f Python: DevUI fixes : Add multimodal input support for workflows and refactor chat input (#2593)
* show app version in devui .NET: Python: Improved Versioning for DevUI
Fixes #2059

* feat: Add multimodal input support for workflows and refactor chat input

This PR adds support for multimodal content (images, files) in workflow
inputs and refactors the chat input into a reusable component.

## Multimodal Workflow Support
- Add `isChatMessageSchema()` to detect ChatMessage input schemas
- Update `RunWorkflowButton` to use `ChatMessageInput` for ChatMessage workflows
- Wrap multimodal content in OpenAI message format for backend processing
- Add `_is_openai_multimodal_format()` to detect OpenAI ResponseInputParam
- Update `_parse_workflow_input()` to route multimodal input through
  existing `_convert_input_to_chat_message()` converter

## Reusable ChatMessageInput Component
- Extract chat input logic from agent-view into `ChatMessageInput` component
- Support file upload, drag & drop, paste handling, and attachments
- Add `useDragDrop` hook for parent-level drag handling with full-area
  drop zones
- Refactor agent-view to use the new shared component

## Other Improvements
- Add `isStreaming` prop to executor nodes for animation control
- Clean up unused imports and state variables in agent-view
- Add tests for multimodal workflow input handling

Fixes workflow input not receiving images when using AgentExecutor nodes.

* add self loop edge, fix #2470

* fix test
2025-12-03 20:15:51 +00:00

70 lines
1.8 KiB
TypeScript

/**
* Custom hook for managing cancellable requests with AbortController
* Reduces duplication across agent and workflow views
*/
import { useState, useRef, useCallback } from "react";
/**
* Hook for managing cancellable requests with AbortController
* @returns Object with cancellation state and methods
*/
export function useCancellableRequest() {
const [isCancelling, setIsCancelling] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null);
/**
* Creates a new AbortController and returns its signal
* Resets the cancelling state
*/
const createAbortSignal = useCallback((): AbortSignal => {
abortControllerRef.current = new AbortController();
setIsCancelling(false);
return abortControllerRef.current.signal;
}, []);
/**
* Cancels the current request if one exists
*/
const handleCancel = useCallback(() => {
if (abortControllerRef.current) {
setIsCancelling(true);
abortControllerRef.current.abort();
abortControllerRef.current = null;
}
}, []);
/**
* Resets the cancelling state - useful in error handlers
*/
const resetCancelling = useCallback(() => {
setIsCancelling(false);
}, []);
/**
* Cleanup function to be called when component unmounts
*/
const cleanup = useCallback(() => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
abortControllerRef.current = null;
}
}, []);
return {
isCancelling,
createAbortSignal,
handleCancel,
resetCancelling,
cleanup,
};
}
/**
* Utility function to check if an error is an AbortError
* @param error - The error to check
* @returns true if the error is an AbortError
*/
export function isAbortError(error: unknown): boolean {
return error instanceof DOMException && error.name === 'AbortError';
}