mirror of https://github.com/langgenius/dify.git
Merge branch 'main' into feat/rag-pipeline
This commit is contained in:
commit
e04ae927b6
|
|
@ -245,6 +245,13 @@ class Workflow(Base):
|
|||
|
||||
@property
|
||||
def tool_published(self) -> bool:
|
||||
"""
|
||||
DEPRECATED: This property is not accurate for determining if a workflow is published as a tool.
|
||||
It only checks if there's a WorkflowToolProvider for the app, not if this specific workflow version
|
||||
is the one being used by the tool.
|
||||
|
||||
For accurate checking, use a direct query with tenant_id, app_id, and version.
|
||||
"""
|
||||
from models.tools import WorkflowToolProvider
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ from extensions.ext_database import db
|
|||
from models.account import Account
|
||||
from models.enums import CreatedByRole
|
||||
from models.model import App, AppMode
|
||||
from models.tools import WorkflowToolProvider
|
||||
from models.workflow import (
|
||||
Workflow,
|
||||
WorkflowNodeExecution,
|
||||
|
|
@ -523,8 +524,19 @@ class WorkflowService:
|
|||
# Cannot delete a workflow that's currently in use by an app
|
||||
raise WorkflowInUseError(f"Cannot delete workflow that is currently in use by app '{app.name}'")
|
||||
|
||||
# Check if this workflow is published as a tool
|
||||
if workflow.tool_published:
|
||||
# Don't use workflow.tool_published as it's not accurate for specific workflow versions
|
||||
# Check if there's a tool provider using this specific workflow version
|
||||
tool_provider = (
|
||||
session.query(WorkflowToolProvider)
|
||||
.filter(
|
||||
WorkflowToolProvider.tenant_id == workflow.tenant_id,
|
||||
WorkflowToolProvider.app_id == workflow.app_id,
|
||||
WorkflowToolProvider.version == workflow.version,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
if tool_provider:
|
||||
# Cannot delete a workflow that's published as a tool
|
||||
raise WorkflowInUseError("Cannot delete workflow that is published as a tool")
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ def workflow_setup():
|
|||
|
||||
def test_delete_workflow_success(workflow_setup):
|
||||
# Setup mocks
|
||||
|
||||
# Mock the tool provider query to return None (not published as a tool)
|
||||
workflow_setup["session"].query.return_value.filter.return_value.first.return_value = None
|
||||
|
||||
workflow_setup["session"].scalar = MagicMock(
|
||||
side_effect=[workflow_setup["workflow"], None]
|
||||
) # Return workflow first, then None for app
|
||||
|
|
@ -97,7 +101,12 @@ def test_delete_workflow_in_use_by_app_error(workflow_setup):
|
|||
|
||||
def test_delete_workflow_published_as_tool_error(workflow_setup):
|
||||
# Setup mocks
|
||||
workflow_setup["workflow"].tool_published = True
|
||||
from models.tools import WorkflowToolProvider
|
||||
|
||||
# Mock the tool provider query
|
||||
mock_tool_provider = MagicMock(spec=WorkflowToolProvider)
|
||||
workflow_setup["session"].query.return_value.filter.return_value.first.return_value = mock_tool_provider
|
||||
|
||||
workflow_setup["session"].scalar = MagicMock(
|
||||
side_effect=[workflow_setup["workflow"], None]
|
||||
) # Return workflow first, then None for app
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ const SettingBuiltInTool: FC<Props> = ({
|
|||
footer={null}
|
||||
mask={false}
|
||||
positionCenter={false}
|
||||
panelClassname={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
panelClassName={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
>
|
||||
<>
|
||||
{isLoading && <Loading type='app' />}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ const Item: FC<ItemProps> = ({
|
|||
<RiDeleteBinLine className='h-4 w-4' />
|
||||
</div>
|
||||
</div>
|
||||
<Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
||||
<Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
||||
<SettingsModal
|
||||
currentDataset={config}
|
||||
onCancel={() => setShowSettingsModal(false)}
|
||||
|
|
|
|||
|
|
@ -743,7 +743,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
|
|||
onClose={onCloseDrawer}
|
||||
mask={isMobile}
|
||||
footer={null}
|
||||
panelClassname='mt-16 mx-2 sm:mr-2 mb-4 !p-0 !max-w-[640px] rounded-xl bg-components-panel-bg'
|
||||
panelClassName='mt-16 mx-2 sm:mr-2 mb-4 !p-0 !max-w-[640px] rounded-xl bg-components-panel-bg'
|
||||
>
|
||||
<DrawerContext.Provider value={{
|
||||
onClose: onCloseDrawer,
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
|
|||
onClose={onCloseDrawer}
|
||||
mask={isMobile}
|
||||
footer={null}
|
||||
panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[600px] rounded-xl border border-components-panel-border'
|
||||
panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[600px] rounded-xl border border-components-panel-border'
|
||||
>
|
||||
<DetailPanel onClose={onCloseDrawer} runID={currentLog?.workflow_run.id || ''} />
|
||||
</Drawer>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
|||
type Props = {
|
||||
isShow: boolean
|
||||
onHide: () => void
|
||||
dialogClassName?: string
|
||||
dialogBackdropClassName?: string
|
||||
panelClassName?: string
|
||||
maxWidthClassName?: string
|
||||
contentClassName?: string
|
||||
|
|
@ -26,6 +28,8 @@ type Props = {
|
|||
const DrawerPlus: FC<Props> = ({
|
||||
isShow,
|
||||
onHide,
|
||||
dialogClassName = '',
|
||||
dialogBackdropClassName = '',
|
||||
panelClassName = '',
|
||||
maxWidthClassName = '!max-w-[640px]',
|
||||
height = 'calc(100vh - 72px)',
|
||||
|
|
@ -55,7 +59,9 @@ const DrawerPlus: FC<Props> = ({
|
|||
footer={null}
|
||||
mask={isMobile || isShowMask}
|
||||
positionCenter={positionCenter}
|
||||
panelClassname={cn('mx-2 mb-3 mt-16 rounded-xl !p-0 sm:mr-2', panelClassName, maxWidthClassName)}
|
||||
dialogClassName={dialogClassName}
|
||||
dialogBackdropClassName={dialogBackdropClassName}
|
||||
panelClassName={cn('mx-2 mb-3 mt-16 rounded-xl !p-0 sm:mr-2', panelClassName, maxWidthClassName)}
|
||||
>
|
||||
<div
|
||||
className={cn(contentClassName, 'flex w-full flex-col rounded-xl border-[0.5px] border-divider-subtle bg-components-panel-bg shadow-xl')}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ import cn from '@/utils/classnames'
|
|||
export type IDrawerProps = {
|
||||
title?: string
|
||||
description?: string
|
||||
panelClassname?: string
|
||||
dialogClassName?: string
|
||||
dialogBackdropClassName?: string
|
||||
panelClassName?: string
|
||||
children: React.ReactNode
|
||||
footer?: React.ReactNode
|
||||
mask?: boolean
|
||||
|
|
@ -25,7 +27,9 @@ export type IDrawerProps = {
|
|||
export default function Drawer({
|
||||
title = '',
|
||||
description = '',
|
||||
panelClassname = '',
|
||||
dialogClassName = '',
|
||||
dialogBackdropClassName = '',
|
||||
panelClassName = '',
|
||||
children,
|
||||
footer,
|
||||
mask = true,
|
||||
|
|
@ -44,17 +48,17 @@ export default function Drawer({
|
|||
unmount={unmount}
|
||||
open={isOpen}
|
||||
onClose={() => !clickOutsideNotOpen && onClose()}
|
||||
className="fixed inset-0 z-[80] overflow-y-auto"
|
||||
className={cn('fixed inset-0 z-[30] overflow-y-auto', dialogClassName)}
|
||||
>
|
||||
<div className={cn('flex h-screen w-screen justify-end', positionCenter && '!justify-center')}>
|
||||
{/* mask */}
|
||||
<DialogBackdrop
|
||||
className={cn('fixed inset-0 z-[90]', mask && 'bg-black bg-opacity-30')}
|
||||
className={cn('fixed inset-0 z-[40]', mask && 'bg-black/30', dialogBackdropClassName)}
|
||||
onClick={() => {
|
||||
!clickOutsideNotOpen && onClose()
|
||||
}}
|
||||
/>
|
||||
<div className={cn('relative z-[100] flex w-full max-w-sm flex-col justify-between overflow-hidden bg-components-panel-bg p-6 text-left align-middle shadow-xl', panelClassname)}>
|
||||
<div className={cn('relative z-[50] flex w-full max-w-sm flex-col justify-between overflow-hidden bg-components-panel-bg p-6 text-left align-middle shadow-xl', panelClassName)}>
|
||||
<>
|
||||
<div className='flex justify-between'>
|
||||
{title && <DialogTitle
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ const Img = ({ src }: any) => {
|
|||
return <div className="markdown-img-wrapper"><ImageGallery srcs={[src]} /></div>
|
||||
}
|
||||
|
||||
const Link = ({ node, ...props }: any) => {
|
||||
const Link = ({ node, children, ...props }: any) => {
|
||||
if (node.properties?.href && node.properties.href?.toString().startsWith('abbr')) {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const { onSend } = useChatContext()
|
||||
|
|
@ -261,7 +261,7 @@ const Link = ({ node, ...props }: any) => {
|
|||
return <abbr className="cursor-pointer underline !decoration-primary-700 decoration-dashed" onClick={() => onSend?.(hidden_text)} title={node.children[0]?.value}>{node.children[0]?.value}</abbr>
|
||||
}
|
||||
else {
|
||||
return <a {...props} target="_blank" className="cursor-pointer underline !decoration-primary-700 decoration-dashed">{node.children[0] ? node.children[0]?.value : 'Download'}</a>
|
||||
return <a {...props} target="_blank" className="cursor-pointer underline !decoration-primary-700 decoration-dashed">{children || 'Download'}</a>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const FullScreenDrawer: FC<IFullScreenDrawerProps> = ({
|
|||
<Drawer
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
panelClassname={classNames('!p-0 bg-components-panel-bg',
|
||||
panelClassName={classNames('!p-0 bg-components-panel-bg',
|
||||
fullScreen
|
||||
? '!max-w-full !w-full'
|
||||
: 'mt-16 mr-2 mb-2 !max-w-[560px] !w-[560px] border-[0.5px] border-components-panel-border rounded-xl',
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
|
|||
}
|
||||
</div>
|
||||
}
|
||||
<FloatRightContainer showClose isOpen={showMetadata} onClose={() => setShowMetadata(false)} isMobile={isMobile} panelClassname='!justify-start' footer={null}>
|
||||
<FloatRightContainer showClose isOpen={showMetadata} onClose={() => setShowMetadata(false)} isMobile={isMobile} panelClassName='!justify-start' footer={null}>
|
||||
<Metadata
|
||||
className='mr-2 mt-3'
|
||||
datasetId={datasetId}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import { useChildSegmentListKey, useSegmentListKey } from '@/service/knowledge/u
|
|||
import useEditDocumentMetadata from '../metadata/hooks/use-edit-dataset-metadata'
|
||||
import DatasetMetadataDrawer from '../metadata/metadata-dataset/dataset-metadata-drawer'
|
||||
import StatusWithAction from '../common/document-status-with-action/status-with-action'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
|
||||
const FolderPlusIcon = ({ className }: React.SVGProps<SVGElement>) => {
|
||||
return <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
|
|
@ -98,7 +100,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
|||
const isDataSourceWeb = dataset?.data_source_type === DataSourceType.WEB
|
||||
const isDataSourceFile = dataset?.data_source_type === DataSourceType.FILE
|
||||
const embeddingAvailable = !!dataset?.embedding_available
|
||||
|
||||
const locale = getLocaleOnClient()
|
||||
const debouncedSearchValue = useDebounce(searchValue, { wait: 500 })
|
||||
|
||||
const { data: documentsRes, isFetching: isListLoading } = useDocumentList({
|
||||
|
|
@ -260,7 +262,12 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
|||
<a
|
||||
className='flex items-center text-text-accent'
|
||||
target='_blank'
|
||||
href='https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'>
|
||||
href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
: 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
}
|
||||
>
|
||||
<span>{t('datasetDocuments.list.learnMore')}</span>
|
||||
<RiExternalLinkLine className='h-3 w-3' />
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
|
|||
<RecordsEmpty />
|
||||
)}
|
||||
</div>
|
||||
<FloatRightContainer panelClassname='!justify-start !overflow-y-auto' showClose isMobile={isMobile} isOpen={isShowRightPanel} onClose={hideRightPanel} footer={null}>
|
||||
<FloatRightContainer panelClassName='!justify-start !overflow-y-auto' showClose isMobile={isMobile} isOpen={isShowRightPanel} onClose={hideRightPanel} footer={null}>
|
||||
<div className='flex flex-col pt-3'>
|
||||
{/* {renderHitResults(generalResultData)} */}
|
||||
{submitLoading
|
||||
|
|
@ -197,7 +197,7 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
|
|||
}
|
||||
</div>
|
||||
</FloatRightContainer>
|
||||
<Drawer unmount={true} isOpen={isShowModifyRetrievalModal} onClose={() => setIsShowModifyRetrievalModal(false)} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
||||
<Drawer unmount={true} isOpen={isShowModifyRetrievalModal} onClose={() => setIsShowModifyRetrievalModal(false)} footer={null} mask={isMobile} panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
||||
<ModifyRetrievalModal
|
||||
indexMethod={currentDataset?.indexing_technique || ''}
|
||||
value={retrievalConfig}
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ const DatasetMetadataDrawer: FC<Props> = ({
|
|||
showClose
|
||||
title={t('dataset.metadata.metadata')}
|
||||
footer={null}
|
||||
panelClassname='px-4 block !max-w-[420px] my-2 rounded-l-2xl'
|
||||
panelClassName='px-4 block !max-w-[420px] my-2 rounded-l-2xl'
|
||||
>
|
||||
<div className='h-full overflow-y-auto'>
|
||||
<div className='system-sm-regular text-text-tertiary'>{t(`${i18nPrefix}.description`)}</div>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ const ListWithCollection = ({
|
|||
return (
|
||||
<>
|
||||
{
|
||||
marketplaceCollections.map(collection => (
|
||||
marketplaceCollections.filter((collection) => {
|
||||
return marketplaceCollectionPluginsMap[collection.name]?.length
|
||||
}).map(collection => (
|
||||
<div
|
||||
key={collection.name}
|
||||
className='py-3'
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const EndpointModal: FC<Props> = ({
|
|||
footer={null}
|
||||
mask
|
||||
positionCenter={false}
|
||||
panelClassname={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
panelClassName={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
>
|
||||
<>
|
||||
<div className='p-4 pb-2'>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const PluginDetailPanel: FC<Props> = ({
|
|||
footer={null}
|
||||
mask={false}
|
||||
positionCenter={false}
|
||||
panelClassname={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
panelClassName={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
>
|
||||
{detail && (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ const StrategyDetail: FC<Props> = ({
|
|||
footer={null}
|
||||
mask={false}
|
||||
positionCenter={false}
|
||||
panelClassname={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
panelClassName={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
>
|
||||
<>
|
||||
{/* header */}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ const AddToolModal: FC<Props> = ({
|
|||
clickOutsideNotOpen
|
||||
onClose={onHide}
|
||||
footer={null}
|
||||
panelClassname={cn('mx-2 mb-3 mt-16 rounded-xl !p-0 sm:mr-2', 'mt-2 !w-[640px]', '!max-w-[640px]')}
|
||||
panelClassName={cn('mx-2 mb-3 mt-16 rounded-xl !p-0 sm:mr-2', 'mt-2 !w-[640px]', '!max-w-[640px]')}
|
||||
>
|
||||
<div
|
||||
className='flex w-full rounded-xl border-[0.5px] border-gray-200 bg-white shadow-xl'
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ const ConfigCredential: FC<Props> = ({
|
|||
positionCenter={positionCenter}
|
||||
onHide={onHide}
|
||||
title={t('tools.createTool.authMethod.title')!}
|
||||
panelClassName='mt-2 !w-[520px] h-fit'
|
||||
dialogClassName='z-[60]'
|
||||
dialogBackdropClassName='z-[70]'
|
||||
panelClassName='mt-2 !w-[520px] h-fit z-[80]'
|
||||
maxWidthClassName='!max-w-[520px]'
|
||||
height={'fit-content'}
|
||||
headerClassName='!border-b-divider-regular'
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ const ProviderDetail = ({
|
|||
footer={null}
|
||||
mask={false}
|
||||
positionCenter={false}
|
||||
panelClassname={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
panelClassName={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
|
||||
>
|
||||
<div className='p-4'>
|
||||
<div className='mb-3 flex'>
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ const DatasetItem: FC<Props> = ({
|
|||
}
|
||||
|
||||
{isShowSettingsModal && (
|
||||
<Drawer isOpen={isShowSettingsModal} onClose={hideSettingsModal} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
||||
<Drawer isOpen={isShowSettingsModal} onClose={hideSettingsModal} footer={null} mask={isMobile} panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
||||
<SettingsModal
|
||||
currentDataset={payload}
|
||||
onCancel={hideSettingsModal}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ const ChatVariableModal = ({
|
|||
case ChatVarType.Number:
|
||||
return value || 0
|
||||
case ChatVarType.Object:
|
||||
return formatValueFromObject(objectValue)
|
||||
return editInJSON ? value : formatValueFromObject(objectValue)
|
||||
case ChatVarType.ArrayString:
|
||||
case ChatVarType.ArrayNumber:
|
||||
case ChatVarType.ArrayObject:
|
||||
|
|
|
|||
Loading…
Reference in New Issue