| @@ -305,7 +305,9 @@ class AdvancedChatAppGenerateTaskPipeline: | |||
| err = self._base_task_pipeline._handle_error(event=event, session=session, message_id=self._message_id) | |||
| yield self._base_task_pipeline._error_to_stream_response(err) | |||
| def _handle_workflow_started_event(self, **kwargs) -> Generator[StreamResponse, None, None]: | |||
| def _handle_workflow_started_event( | |||
| self, event: QueueWorkflowStartedEvent, **kwargs | |||
| ) -> Generator[StreamResponse, None, None]: | |||
| """Handle workflow started events.""" | |||
| with self._database_session() as session: | |||
| workflow_execution = self._workflow_cycle_manager.handle_workflow_run_start() | |||
| @@ -3,12 +3,12 @@ import type { FC } from 'react' | |||
| import React, { useEffect, useRef, useState } from 'react' | |||
| import { | |||
| RiDeleteBinLine, | |||
| RiNodeTree, | |||
| } from '@remixicon/react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { useContext } from 'use-context-selector' | |||
| import { formatFileSize } from '@/utils/format' | |||
| import cn from '@/utils/classnames' | |||
| import { Yaml as YamlIcon } from '@/app/components/base/icons/src/public/files' | |||
| import { ToastContext } from '@/app/components/base/toast' | |||
| import { UploadCloud01 } from '@/app/components/base/icons/src/vender/line/general' | |||
| import Button from '@/app/components/base/button' | |||
| @@ -122,12 +122,12 @@ const Uploader: FC<Props> = ({ | |||
| {file && ( | |||
| <div className={cn('group flex items-center rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs', 'hover:border-[#B2CCFF] hover:bg-[#F5F8FF]')}> | |||
| <div className='flex items-center justify-center p-3'> | |||
| <YamlIcon className='h-6 w-6 shrink-0' /> | |||
| <RiNodeTree className='h-6 w-6 shrink-0' /> | |||
| </div> | |||
| <div className='flex grow flex-col items-start gap-0.5 py-1 pr-2'> | |||
| <span className='font-inter max-w-[calc(100%_-_30px)] overflow-hidden text-ellipsis whitespace-nowrap text-[12px] font-medium leading-4 text-text-secondary'>{file.name}</span> | |||
| <div className='font-inter flex h-3 items-center gap-1 self-stretch text-[10px] font-medium uppercase leading-3 text-text-tertiary'> | |||
| <span>YAML</span> | |||
| <span>PIPELINE</span> | |||
| <span className='text-text-quaternary'>·</span> | |||
| <span>{formatFileSize(file.size)}</span> | |||
| </div> | |||
| @@ -49,7 +49,10 @@ const ToolSelectorTrigger = ({ | |||
| !!selectedTagsLength && ( | |||
| <RiCloseCircleFill | |||
| className='size-4 text-text-quaternary' | |||
| onClick={() => onTagsChange([])} | |||
| onClick={(e) => { | |||
| e.stopPropagation() | |||
| onTagsChange([]) | |||
| }} | |||
| /> | |||
| ) | |||
| } | |||
| @@ -54,7 +54,7 @@ export const useHiddenFieldNames = (type: PipelineInputVarType) => { | |||
| break | |||
| case PipelineInputVarType.checkbox: | |||
| fieldNames = [ | |||
| t('appDebug.variableConfig.startedChecked'), | |||
| t('appDebug.variableConfig.startChecked'), | |||
| t('appDebug.variableConfig.tooltips'), | |||
| ] | |||
| break | |||
| @@ -70,21 +70,21 @@ const RagPipelineMain = ({ | |||
| ...configsMap, | |||
| }) | |||
| const { | |||
| hasNodeInspectVars, | |||
| hasSetInspectVar, | |||
| fetchInspectVarValue, | |||
| editInspectVarValue, | |||
| renameInspectVarName, | |||
| appendNodeInspectVars, | |||
| deleteInspectVar, | |||
| deleteNodeInspectorVars, | |||
| deleteAllInspectorVars, | |||
| isInspectVarEdited, | |||
| resetToLastRunVar, | |||
| invalidateSysVarValues, | |||
| resetConversationVar, | |||
| invalidateConversationVarValues, | |||
| } = useInspectVarsCrud() | |||
| hasNodeInspectVars, | |||
| hasSetInspectVar, | |||
| fetchInspectVarValue, | |||
| editInspectVarValue, | |||
| renameInspectVarName, | |||
| appendNodeInspectVars, | |||
| deleteInspectVar, | |||
| deleteNodeInspectorVars, | |||
| deleteAllInspectorVars, | |||
| isInspectVarEdited, | |||
| resetToLastRunVar, | |||
| invalidateSysVarValues, | |||
| resetConversationVar, | |||
| invalidateConversationVarValues, | |||
| } = useInspectVarsCrud() | |||
| const hooksStore = useMemo(() => { | |||
| return { | |||
| @@ -22,7 +22,7 @@ export const useDSL = () => { | |||
| const appDetail = useAppStore(s => s.appDetail) | |||
| const handleExportDSL = useCallback(async (include = false) => { | |||
| const handleExportDSL = useCallback(async (include = false, workflowId?: string) => { | |||
| if (!appDetail) | |||
| return | |||
| @@ -35,6 +35,7 @@ export const useDSL = () => { | |||
| const { data } = await exportAppConfig({ | |||
| appID: appDetail.id, | |||
| include, | |||
| workflowID: workflowId, | |||
| }) | |||
| const a = document.createElement('a') | |||
| const file = new Blob([data], { type: 'application/yaml' }) | |||
| @@ -26,7 +26,6 @@ import { PluginType } from '../../plugins/types' | |||
| import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' | |||
| import { useGlobalPublicStore } from '@/context/global-public-context' | |||
| import RAGToolSuggestions from './rag-tool-suggestions' | |||
| import { useRAGRecommendedPlugins } from '@/service/use-tools' | |||
| type AllToolsProps = { | |||
| className?: string | |||
| @@ -117,13 +116,7 @@ const AllTools = ({ | |||
| const wrapElemRef = useRef<HTMLDivElement>(null) | |||
| const isSupportGroupView = [ToolTypeEnum.All, ToolTypeEnum.BuiltIn].includes(activeTab) | |||
| const isShowRAGRecommendations = isInRAGPipeline && activeTab === ToolTypeEnum.All && !searchText && tags.length === 0 | |||
| const { data: ragRecommendedPlugins } = useRAGRecommendedPlugins(isShowRAGRecommendations) | |||
| const recommendedPlugins = useMemo(() => { | |||
| if (ragRecommendedPlugins) | |||
| return [...ragRecommendedPlugins.installed_recommended_plugins] | |||
| return [] | |||
| }, [ragRecommendedPlugins]) | |||
| const isShowRAGRecommendations = isInRAGPipeline && activeTab === ToolTypeEnum.All && !hasFilter | |||
| return ( | |||
| <div className={cn('min-w-[400px] max-w-[500px]', className)}> | |||
| @@ -154,9 +147,8 @@ const AllTools = ({ | |||
| className='max-h-[464px] overflow-y-auto' | |||
| onScroll={pluginRef.current?.handleScroll} | |||
| > | |||
| {recommendedPlugins.length > 0 && ( | |||
| {isShowRAGRecommendations && ( | |||
| <RAGToolSuggestions | |||
| tools={recommendedPlugins} | |||
| viewType={isSupportGroupView ? activeView : ViewType.flat} | |||
| onSelect={onSelect} | |||
| onTagsChange={onTagsChange} | |||
| @@ -30,7 +30,7 @@ const List = forwardRef<ListRef, ListProps>(({ | |||
| disableMaxWidth = false, | |||
| }, ref) => { | |||
| const { t } = useTranslation() | |||
| const hasFilter = !searchText | |||
| const noFilter = !searchText && tags.length === 0 | |||
| const hasRes = list.length > 0 | |||
| const urlWithSearchText = getMarketplaceUrl('', { q: searchText, tags: tags.join(',') }) | |||
| const nextToStickyELemRef = useRef<HTMLDivElement>(null) | |||
| @@ -66,7 +66,7 @@ const List = forwardRef<ListRef, ListProps>(({ | |||
| window.open(urlWithSearchText, '_blank') | |||
| } | |||
| if (hasFilter) { | |||
| if (noFilter) { | |||
| return ( | |||
| <Link | |||
| className='system-sm-medium sticky bottom-0 z-10 flex h-8 cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg' | |||
| @@ -108,7 +108,7 @@ const List = forwardRef<ListRef, ListProps>(({ | |||
| onAction={noop} | |||
| /> | |||
| ))} | |||
| {list.length > 0 && ( | |||
| {hasRes && ( | |||
| <div className='mb-3 mt-2 flex items-center justify-center space-x-2'> | |||
| <div className="h-[2px] w-[90px] bg-gradient-to-l from-[rgba(16,24,40,0.08)] to-[rgba(255,255,255,0.01)]"></div> | |||
| <Link | |||
| @@ -1,27 +1,40 @@ | |||
| import type { Dispatch, SetStateAction } from 'react' | |||
| import React, { useCallback } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import type { OnSelectBlock, ToolWithProvider } from '../types' | |||
| import React, { useCallback, useMemo } from 'react' | |||
| import { Trans, useTranslation } from 'react-i18next' | |||
| import type { OnSelectBlock } from '../types' | |||
| import Tools from './tools' | |||
| import { ToolTypeEnum } from './types' | |||
| import type { ViewType } from './view-type-select' | |||
| import { RiMoreLine } from '@remixicon/react' | |||
| import Loading from '@/app/components/base/loading' | |||
| import Link from 'next/link' | |||
| import { getMarketplaceUrl } from '@/utils/var' | |||
| import { useRAGRecommendedPlugins } from '@/service/use-tools' | |||
| type RAGToolSuggestionsProps = { | |||
| tools: ToolWithProvider[] | |||
| viewType: ViewType | |||
| onSelect: OnSelectBlock | |||
| onTagsChange: Dispatch<SetStateAction<string[]>> | |||
| } | |||
| const RAGToolSuggestions: React.FC<RAGToolSuggestionsProps> = ({ | |||
| tools, | |||
| viewType, | |||
| onSelect, | |||
| onTagsChange, | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| const { | |||
| data: ragRecommendedPlugins, | |||
| isFetching: isFetchingRAGRecommendedPlugins, | |||
| } = useRAGRecommendedPlugins() | |||
| const recommendedPlugins = useMemo(() => { | |||
| if (ragRecommendedPlugins) | |||
| return [...ragRecommendedPlugins.installed_recommended_plugins] | |||
| return [] | |||
| }, [ragRecommendedPlugins]) | |||
| const loadMore = useCallback(() => { | |||
| onTagsChange((prev) => { | |||
| if (prev.includes('rag')) | |||
| @@ -35,26 +48,52 @@ const RAGToolSuggestions: React.FC<RAGToolSuggestionsProps> = ({ | |||
| <div className='system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary'> | |||
| {t('pipeline.ragToolSuggestions.title')} | |||
| </div> | |||
| <Tools | |||
| className='p-0' | |||
| tools={tools} | |||
| onSelect={onSelect} | |||
| canNotSelectMultiple | |||
| toolType={ToolTypeEnum.All} | |||
| viewType={viewType} | |||
| hasSearchText={false} | |||
| /> | |||
| <div | |||
| className='flex cursor-pointer items-center gap-x-2 py-1 pl-3 pr-2' | |||
| onClick={loadMore} | |||
| > | |||
| <div className='px-1'> | |||
| <RiMoreLine className='size-4 text-text-tertiary' /> | |||
| {isFetchingRAGRecommendedPlugins && ( | |||
| <div className='py-2'> | |||
| <Loading type='app' /> | |||
| </div> | |||
| <div className='system-xs-regular text-text-tertiary'> | |||
| {t('common.operation.more')} | |||
| </div> | |||
| </div> | |||
| )} | |||
| {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length === 0 && ( | |||
| <p className='system-xs-regular px-3 py-1 text-text-tertiary'> | |||
| <Trans | |||
| i18nKey='pipeline.ragToolSuggestions.noRecommendationPluginsInstalled' | |||
| components={{ | |||
| CustomLink: ( | |||
| <Link | |||
| className='text-text-accent' | |||
| target='_blank' | |||
| rel='noopener noreferrer' | |||
| href={getMarketplaceUrl('', { tags: 'rag' })} | |||
| /> | |||
| ), | |||
| }} | |||
| /> | |||
| </p> | |||
| )} | |||
| {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length > 0 && ( | |||
| <> | |||
| <Tools | |||
| className='p-0' | |||
| tools={recommendedPlugins} | |||
| onSelect={onSelect} | |||
| canNotSelectMultiple | |||
| toolType={ToolTypeEnum.All} | |||
| viewType={viewType} | |||
| hasSearchText={false} | |||
| /> | |||
| <div | |||
| className='flex cursor-pointer items-center gap-x-2 py-1 pl-3 pr-2' | |||
| onClick={loadMore} | |||
| > | |||
| <div className='px-1'> | |||
| <RiMoreLine className='size-4 text-text-tertiary' /> | |||
| </div> | |||
| <div className='system-xs-regular text-text-tertiary'> | |||
| {t('common.operation.more')} | |||
| </div> | |||
| </div> | |||
| </> | |||
| )} | |||
| </div> | |||
| ) | |||
| } | |||
| @@ -48,7 +48,7 @@ export type CommonHooksFnMap = { | |||
| availableNodesMetaData?: AvailableNodesMetaData | |||
| getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string; traceUrl: string } | |||
| exportCheck?: () => Promise<void> | |||
| handleExportDSL?: (include?: boolean) => Promise<void> | |||
| handleExportDSL?: (include?: boolean, flowId?: string) => Promise<void> | |||
| fetchInspectVars: (params: { passInVars?: boolean, vars?: VarInInspect[], passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>, passedInSchemaTypeDefinitions?: SchemaTypeDefinition[] }) => Promise<void> | |||
| hasNodeInspectVars: (nodeId: string) => boolean | |||
| hasSetInspectVar: (nodeId: string, name: string, sysVars: VarInInspect[], conversationVars: VarInInspect[]) => boolean | |||
| @@ -120,7 +120,7 @@ export const VersionHistoryPanel = ({ | |||
| }) | |||
| break | |||
| case VersionHistoryContextMenuOptions.exportDSL: | |||
| handleExportDSL(false, item.id) | |||
| handleExportDSL?.(false, item.id) | |||
| break | |||
| } | |||
| }, [t, handleExportDSL]) | |||
| @@ -22,10 +22,11 @@ type DisplayContentProps = { | |||
| readonly: boolean | |||
| handleTextChange?: (value: string) => void | |||
| handleEditorChange?: (value: string) => void | |||
| className?: string | |||
| } | |||
| const DisplayContent = (props: DisplayContentProps) => { | |||
| const { previewType, varType, schemaType, mdString, jsonString, readonly, handleTextChange, handleEditorChange } = props | |||
| const { previewType, varType, schemaType, mdString, jsonString, readonly, handleTextChange, handleEditorChange, className } = props | |||
| const [viewMode, setViewMode] = useState<ViewMode>(ViewMode.Code) | |||
| const [isFocused, setIsFocused] = useState(false) | |||
| const { t } = useTranslation() | |||
| @@ -50,7 +51,7 @@ const DisplayContent = (props: DisplayContentProps) => { | |||
| }, [previewType, schemaType, jsonString]) | |||
| return ( | |||
| <div className={cn('flex h-full flex-col rounded-[10px] bg-components-input-bg-normal', isFocused && 'bg-components-input-bg-active outline outline-1 outline-components-input-border-active')}> | |||
| <div className={cn('flex h-full flex-col rounded-[10px] bg-components-input-bg-normal', isFocused && 'bg-components-input-bg-active outline outline-1 outline-components-input-border-active', className)}> | |||
| <div className='flex shrink-0 items-center justify-end p-1'> | |||
| {previewType === PreviewType.Markdown && ( | |||
| <div className='system-xs-semibold-uppercase flex grow items-center px-2 py-0.5 text-text-secondary'> | |||
| @@ -198,6 +198,7 @@ const ValueContent = ({ | |||
| mdString={value as any} | |||
| readonly={textEditorDisabled} | |||
| handleTextChange={handleTextChange} | |||
| className={cn(isTruncated && 'pt-[36px]')} | |||
| /> | |||
| ) : ( | |||
| <Textarea | |||
| @@ -401,7 +401,6 @@ const translation = { | |||
| 'tooltips': 'Tooltips', | |||
| 'tooltipsPlaceholder': 'Enter helpful text shown when hovering over the label', | |||
| 'showAllSettings': 'Show All Settings', | |||
| 'checkbox': 'Checkbox', | |||
| 'startSelectedOption': 'Start selected option', | |||
| 'noDefaultSelected': 'Don\'t select', | |||
| 'hide': 'Hide', | |||
| @@ -33,6 +33,7 @@ const translation = { | |||
| }, | |||
| ragToolSuggestions: { | |||
| title: 'Suggestions for RAG', | |||
| noRecommendationPluginsInstalled: 'No recommended plugins installed, find more in <CustomLink>Marketplace</CustomLink>', | |||
| }, | |||
| } | |||
| @@ -397,7 +397,6 @@ const translation = { | |||
| 'tooltips': '提示', | |||
| 'tooltipsPlaceholder': '输入悬停在标签上时显示的提示文本', | |||
| 'showAllSettings': '显示所有设置', | |||
| 'checkbox': '复选框', | |||
| 'startSelectedOption': '默认选中项', | |||
| 'noDefaultSelected': '不默认选中', | |||
| 'file': { | |||
| @@ -33,6 +33,7 @@ const translation = { | |||
| }, | |||
| ragToolSuggestions: { | |||
| title: 'RAG 工具推荐', | |||
| noRecommendationPluginsInstalled: '暂无已安装的推荐插件,更多插件请在 <CustomLink>Marketplace</CustomLink> 中查找', | |||
| }, | |||
| } | |||
| @@ -39,7 +39,7 @@ import { | |||
| useQuery, | |||
| useQueryClient, | |||
| } from '@tanstack/react-query' | |||
| import { useInvalidateAllBuiltInTools } from './use-tools' | |||
| import { useInvalidateAllBuiltInTools, useInvalidateRAGRecommendedPlugins } from './use-tools' | |||
| import useReferenceSetting from '@/app/components/plugins/plugin-page/use-reference-setting' | |||
| import { uninstallPlugin } from '@/service/plugins' | |||
| import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list' | |||
| @@ -135,12 +135,14 @@ export const useInstalledLatestVersion = (pluginIds: string[]) => { | |||
| export const useInvalidateInstalledPluginList = () => { | |||
| const queryClient = useQueryClient() | |||
| const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools() | |||
| const invalidateRAGRecommendedPlugins = useInvalidateRAGRecommendedPlugins() | |||
| return () => { | |||
| queryClient.invalidateQueries( | |||
| { | |||
| queryKey: useInstalledPluginListKey, | |||
| }) | |||
| invalidateAllBuiltInTools() | |||
| invalidateRAGRecommendedPlugins() | |||
| } | |||
| } | |||
| @@ -312,10 +312,15 @@ export const useRemoveProviderCredentials = ({ | |||
| }) | |||
| } | |||
| export const useRAGRecommendedPlugins = (enabled: boolean) => { | |||
| const useRAGRecommendedPluginListKey = [NAME_SPACE, 'rag-recommended-plugins'] | |||
| export const useRAGRecommendedPlugins = () => { | |||
| return useQuery<RAGRecommendedPlugins>({ | |||
| queryKey: [NAME_SPACE, 'rag-recommended-plugins'], | |||
| queryKey: useRAGRecommendedPluginListKey, | |||
| queryFn: () => get<RAGRecommendedPlugins>('/rag/pipelines/recommended-plugins'), | |||
| enabled, | |||
| }) | |||
| } | |||
| export const useInvalidateRAGRecommendedPlugins = () => { | |||
| return useInvalid(useRAGRecommendedPluginListKey) | |||
| } | |||