mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 01:48:04 +08:00
Merge branch 'feat/plugins' into dev/plugin-deploy
This commit is contained in:
@ -1,5 +1,4 @@
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
@ -15,7 +14,6 @@ import TracingPanel from '@/app/components/workflow/run/tracing-panel'
|
||||
import cn from '@/utils/classnames'
|
||||
import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
|
||||
type WorkflowProcessProps = {
|
||||
data: WorkflowProcess
|
||||
@ -26,7 +24,6 @@ type WorkflowProcessProps = {
|
||||
}
|
||||
const WorkflowProcessItem = ({
|
||||
data,
|
||||
item,
|
||||
expand = false,
|
||||
hideInfo = false,
|
||||
hideProcessDetail = false,
|
||||
@ -54,22 +51,6 @@ const WorkflowProcessItem = ({
|
||||
setCollapse(!expand)
|
||||
}, [expand])
|
||||
|
||||
const setCurrentLogItem = useAppStore(s => s.setCurrentLogItem)
|
||||
const setShowMessageLogModal = useAppStore(s => s.setShowMessageLogModal)
|
||||
const setCurrentLogModalActiveTab = useAppStore(s => s.setCurrentLogModalActiveTab)
|
||||
|
||||
const showIterationDetail = useCallback(() => {
|
||||
setCurrentLogItem(item)
|
||||
setCurrentLogModalActiveTab('TRACING')
|
||||
setShowMessageLogModal(true)
|
||||
}, [item, setCurrentLogItem, setCurrentLogModalActiveTab, setShowMessageLogModal])
|
||||
|
||||
const showRetryDetail = useCallback(() => {
|
||||
setCurrentLogItem(item)
|
||||
setCurrentLogModalActiveTab('TRACING')
|
||||
setShowMessageLogModal(true)
|
||||
}, [item, setCurrentLogItem, setCurrentLogModalActiveTab, setShowMessageLogModal])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@ -110,8 +91,6 @@ const WorkflowProcessItem = ({
|
||||
{
|
||||
<TracingPanel
|
||||
list={data.tracing}
|
||||
onShowIterationDetail={showIterationDetail}
|
||||
onShowRetryDetail={showRetryDetail}
|
||||
hideNodeInfo={hideInfo}
|
||||
hideNodeProcessDetail={hideProcessDetail}
|
||||
/>
|
||||
|
||||
@ -40,7 +40,7 @@ const ModelIcon: FC<ModelIconProps> = ({
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
'flex items-center justify-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle',
|
||||
'flex items-center justify-center rounded-md border-[0.5px] w-5 h-5 border-components-panel-border-subtle bg-background-default-subtle',
|
||||
className,
|
||||
)}>
|
||||
<div className='flex w-5 h-5 items-center justify-center opacity-35'>
|
||||
|
||||
@ -26,6 +26,7 @@ import cn from '@/utils/classnames'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import { fetchPluginInfoFromMarketPlace } from '@/service/plugins'
|
||||
import { fetchModelProviderModelList } from '@/service/common'
|
||||
|
||||
export type AgentModelTriggerProps = {
|
||||
open?: boolean
|
||||
@ -67,11 +68,22 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||
const [pluginInfo, setPluginInfo] = useState<PluginInfoFromMarketPlace | null>(null)
|
||||
const [isPluginChecked, setIsPluginChecked] = useState(false)
|
||||
const [installed, setInstalled] = useState(false)
|
||||
const [inModelList, setInModelList] = useState(false)
|
||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||
const handleOpenModal = useModelModalHandler()
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (providerName && !modelProvider) {
|
||||
if (modelId && currentProvider) {
|
||||
try {
|
||||
const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${currentProvider?.provider}/models`)
|
||||
if (modelId && modelsData.data.find(item => item.model === modelId))
|
||||
setInModelList(true)
|
||||
}
|
||||
catch (error) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
if (providerName) {
|
||||
const parts = providerName.split('/')
|
||||
const org = parts[0]
|
||||
const name = parts[1]
|
||||
@ -89,7 +101,7 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||
setIsPluginChecked(true)
|
||||
}
|
||||
})()
|
||||
}, [providerName, modelProvider])
|
||||
}, [providerName, modelId, currentProvider])
|
||||
|
||||
if (modelId && !isPluginChecked)
|
||||
return null
|
||||
@ -121,6 +133,7 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||
<StatusIndicators
|
||||
needsConfiguration={needsConfiguration}
|
||||
modelProvider={!!modelProvider}
|
||||
inModelList={inModelList}
|
||||
disabled={!!disabled}
|
||||
pluginInfo={pluginInfo}
|
||||
t={t}
|
||||
|
||||
@ -1,46 +1,73 @@
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Link from 'next/link'
|
||||
import { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version'
|
||||
import { useInstalledPluginList } from '@/service/use-plugins'
|
||||
import { RiErrorWarningFill } from '@remixicon/react'
|
||||
|
||||
type StatusIndicatorsProps = {
|
||||
needsConfiguration: boolean
|
||||
modelProvider: boolean
|
||||
inModelList: boolean
|
||||
disabled: boolean
|
||||
pluginInfo: any
|
||||
t: any
|
||||
}
|
||||
|
||||
const StatusIndicators = ({ needsConfiguration, modelProvider, disabled, pluginInfo, t }: StatusIndicatorsProps) => {
|
||||
const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disabled, pluginInfo, t }: StatusIndicatorsProps) => {
|
||||
const { data: pluginList } = useInstalledPluginList()
|
||||
const renderTooltipContent = (title: string, description?: string, linkText?: string, linkHref?: string) => {
|
||||
return (
|
||||
<div className='flex w-[240px] max-w-[240px] gap-1 flex-col px-1 py-1.5' onClick={e => e.stopPropagation()}>
|
||||
<div className='text-text-primary title-xs-semi-bold'>{title}</div>
|
||||
{description && (
|
||||
<div className='min-w-[200px] text-text-secondary body-xs-regular'>
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
{linkText && linkHref && (
|
||||
<div className='text-text-accent body-xs-regular cursor-pointer z-[100]'>
|
||||
<Link
|
||||
href={linkHref}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
{linkText}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// const installedPluginUniqueIdentifier = pluginList?.plugins.find(plugin => plugin.name === pluginInfo.name)?.plugin_unique_identifier
|
||||
return (
|
||||
<>
|
||||
{/* plugin installed and model is in model list but disabled */}
|
||||
{/* plugin installed from github/local and model is not in model list */}
|
||||
{!needsConfiguration && modelProvider && disabled && (
|
||||
<Tooltip
|
||||
popupContent={t('workflow.nodes.agent.modelSelectorTooltips.deprecated')}
|
||||
popupContent={inModelList ? t('workflow.nodes.agent.modelSelectorTooltips.deprecated')
|
||||
: renderTooltipContent(
|
||||
t('workflow.nodes.agent.modelNotSupport.title'),
|
||||
!pluginInfo ? t('workflow.nodes.agent.modelNotSupport.desc') : t('workflow.nodes.agent.modelNotSupport.descForVersionSwitch'),
|
||||
!pluginInfo ? t('workflow.nodes.agent.linkToPlugin') : '',
|
||||
!pluginInfo ? '/plugins' : '',
|
||||
)
|
||||
}
|
||||
asChild={false}
|
||||
needsDelay={!inModelList}
|
||||
>
|
||||
<RiErrorWarningFill className='w-4 h-4 text-text-destructive' />
|
||||
{!pluginInfo ? <RiErrorWarningFill className='w-4 h-4 text-text-destructive' /> : <SwitchPluginVersion uniqueIdentifier={pluginList?.plugins.find(plugin => plugin.name === pluginInfo.name)?.plugin_unique_identifier ?? ''} />}
|
||||
</Tooltip>
|
||||
)}
|
||||
{!modelProvider && !pluginInfo && (
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='flex w-[240px] max-w-[240px] gap-1 flex-col px-1 py-1.5'>
|
||||
<div className='text-text-primary title-xs-semi-bold'>{t('workflow.nodes.agent.modelNotInMarketplace.title')}</div>
|
||||
<div className='min-w-[200px] text-text-secondary body-xs-regular'>
|
||||
{t('workflow.nodes.agent.modelNotInMarketplace.desc')}
|
||||
</div>
|
||||
<div className='text-text-accent body-xs-regular cursor-pointer z-[100]'>
|
||||
<Link
|
||||
href={'/plugins'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
{t('workflow.nodes.agent.linkToPlugin')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
popupContent={renderTooltipContent(
|
||||
t('workflow.nodes.agent.modelNotInMarketplace.title'),
|
||||
t('workflow.nodes.agent.modelNotInMarketplace.desc'),
|
||||
t('workflow.nodes.agent.linkToPlugin'),
|
||||
'/plugins',
|
||||
)}
|
||||
asChild={false}
|
||||
needsDelay
|
||||
>
|
||||
|
||||
@ -22,11 +22,11 @@ const ModelTrigger: FC<ModelTriggerProps> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('group flex flex-grow items-center p-[3px] pl-1 h-6 gap-1 rounded-lg bg-components-input-bg-disabled cursor-pointer', className)}
|
||||
className={cn('group flex flex-grow items-center p-[3px] pl-1 h-8 gap-1 rounded-lg bg-components-input-bg-disabled cursor-pointer', className)}
|
||||
>
|
||||
<div className='flex items-center py-[1px] gap-1 grow'>
|
||||
<ModelIcon
|
||||
className="m-0.5 w-4 h-4"
|
||||
className="w-4 h-4"
|
||||
provider={currentProvider}
|
||||
modelName={modelName}
|
||||
/>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export const tagKeys = [
|
||||
'agent',
|
||||
'search',
|
||||
'image',
|
||||
'videos',
|
||||
|
||||
@ -12,6 +12,7 @@ import { useInstallPackageFromMarketPlace, useUpdatePackageFromMarketPlace } fro
|
||||
import checkTaskStatus from '../../base/check-task-status'
|
||||
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
|
||||
import Version from '../../base/version'
|
||||
import { usePluginTaskList } from '@/service/use-plugins'
|
||||
|
||||
const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
@ -50,6 +51,7 @@ const Installed: FC<Props> = ({
|
||||
check,
|
||||
stop,
|
||||
} = checkTaskStatus()
|
||||
const { handleRefetch } = usePluginTaskList()
|
||||
|
||||
useEffect(() => {
|
||||
if (hasInstalled && uniqueIdentifier === installedInfoPayload.uniqueIdentifier)
|
||||
@ -93,6 +95,9 @@ const Installed: FC<Props> = ({
|
||||
onInstalled()
|
||||
return
|
||||
}
|
||||
|
||||
handleRefetch()
|
||||
|
||||
const { status, error } = await check({
|
||||
taskId,
|
||||
pluginUniqueIdentifier: uniqueIdentifier,
|
||||
|
||||
@ -8,14 +8,15 @@ import type { UseMutationResult } from '@tanstack/react-query'
|
||||
|
||||
type Props = {
|
||||
plugin: Plugin
|
||||
onSave: () => void
|
||||
onCancel: () => void
|
||||
mutation: UseMutationResult
|
||||
mutation: Pick<UseMutationResult, 'isSuccess' | 'isPending'>
|
||||
mutate: () => void
|
||||
confirmButtonText: ReactNode
|
||||
cancelButtonText: ReactNode
|
||||
modelTitle: ReactNode
|
||||
description: ReactNode
|
||||
cardTitleLeft: ReactNode
|
||||
modalBottomLeft?: ReactNode
|
||||
}
|
||||
|
||||
const PluginMutationModal: FC<Props> = ({
|
||||
@ -27,6 +28,8 @@ const PluginMutationModal: FC<Props> = ({
|
||||
modelTitle,
|
||||
description,
|
||||
cardTitleLeft,
|
||||
mutate,
|
||||
modalBottomLeft,
|
||||
}: Props) => {
|
||||
return (
|
||||
<Modal
|
||||
@ -47,20 +50,25 @@ const PluginMutationModal: FC<Props> = ({
|
||||
titleLeft={cardTitleLeft}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex pt-5 justify-end items-center gap-2 self-stretch'>
|
||||
{mutation.isPending && (
|
||||
<Button onClick={onCancel}>
|
||||
{cancelButtonText}
|
||||
<div className='flex pt-5 items-center gap-2 self-stretch'>
|
||||
<div>
|
||||
{modalBottomLeft}
|
||||
</div>
|
||||
<div className='ml-auto flex gap-2'>
|
||||
{!mutation.isPending && (
|
||||
<Button onClick={onCancel}>
|
||||
{cancelButtonText}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant='primary'
|
||||
loading={mutation.isPending}
|
||||
onClick={mutate}
|
||||
disabled={mutation.isPending}
|
||||
>
|
||||
{confirmButtonText}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant='primary'
|
||||
loading={mutation.isPending}
|
||||
onClick={mutation.mutate}
|
||||
disabled={mutation.isPending}
|
||||
>
|
||||
{confirmButtonText}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@ -3,13 +3,19 @@
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker'
|
||||
import { RiArrowLeftRightLine } from '@remixicon/react'
|
||||
import { RiArrowLeftRightLine, RiExternalLinkLine } from '@remixicon/react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { type FC, useCallback, useState } from 'react'
|
||||
import UpdateFromMarketplace from '@/app/components/plugins/update-plugin/from-market-place'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { useCheckInstalled } from '@/service/use-plugins'
|
||||
import { useCheckInstalled, useUpdatePackageFromMarketPlace } from '@/service/use-plugins'
|
||||
import cn from '@/utils/classnames'
|
||||
import PluginMutationModel from '@/app/components/plugins/plugin-mutation-model'
|
||||
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
|
||||
import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils'
|
||||
import { Badge as Badge2, BadgeState } from '@/app/components/base/badge/index'
|
||||
import Link from 'next/link'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { marketplaceUrlPrefix } from '@/config'
|
||||
|
||||
export type SwitchPluginVersionProps = {
|
||||
uniqueIdentifier: string
|
||||
@ -23,7 +29,10 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => {
|
||||
const [pluginId] = uniqueIdentifier.split(':')
|
||||
const [isShow, setIsShow] = useState(false)
|
||||
const [isShowUpdateModal, { setTrue: showUpdateModal, setFalse: hideUpdateModal }] = useBoolean(false)
|
||||
const [targetVersion, setTargetVersion] = useState<string>()
|
||||
const [target, setTarget] = useState<{
|
||||
version: string,
|
||||
pluginUniqueIden: string;
|
||||
}>()
|
||||
const pluginDetails = useCheckInstalled({
|
||||
pluginIds: [pluginId],
|
||||
enabled: true,
|
||||
@ -33,28 +42,55 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => {
|
||||
const handleUpdatedFromMarketplace = useCallback(() => {
|
||||
hideUpdateModal()
|
||||
pluginDetails.refetch()
|
||||
onChange?.(targetVersion!)
|
||||
}, [hideUpdateModal, onChange, pluginDetails, targetVersion])
|
||||
|
||||
const targetUniqueIdentifier = (() => {
|
||||
if (!targetVersion || !pluginDetail) return uniqueIdentifier
|
||||
return uniqueIdentifier.replaceAll(pluginDetail.version, targetVersion)
|
||||
})()
|
||||
onChange?.(target!.version)
|
||||
}, [hideUpdateModal, onChange, pluginDetails, target])
|
||||
const { getIconUrl } = useGetIcon()
|
||||
const icon = pluginDetail?.declaration.icon ? getIconUrl(pluginDetail.declaration.icon) : undefined
|
||||
const mutation = useUpdatePackageFromMarketPlace()
|
||||
const install = () => {
|
||||
mutation.mutate(
|
||||
{
|
||||
new_plugin_unique_identifier: target!.pluginUniqueIden,
|
||||
original_plugin_unique_identifier: uniqueIdentifier,
|
||||
},
|
||||
{
|
||||
onSuccess() {
|
||||
handleUpdatedFromMarketplace()
|
||||
},
|
||||
})
|
||||
}
|
||||
const { t } = useTranslation()
|
||||
return <Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod='hover'>
|
||||
<div className={cn('w-fit', className)}>
|
||||
{isShowUpdateModal && pluginDetail && <UpdateFromMarketplace
|
||||
payload={{
|
||||
originalPackageInfo: {
|
||||
id: uniqueIdentifier,
|
||||
payload: pluginDetail.declaration,
|
||||
},
|
||||
targetPackageInfo: {
|
||||
id: targetUniqueIdentifier,
|
||||
version: targetVersion!,
|
||||
},
|
||||
}}
|
||||
<div className={cn('w-fit flex items-center justify-center', className)}>
|
||||
{isShowUpdateModal && pluginDetail && <PluginMutationModel
|
||||
onCancel={hideUpdateModal}
|
||||
onSave={handleUpdatedFromMarketplace}
|
||||
plugin={pluginManifestToCardPluginProps({
|
||||
...pluginDetail.declaration,
|
||||
icon: icon!,
|
||||
})}
|
||||
mutation={mutation}
|
||||
mutate={install}
|
||||
confirmButtonText={t('workflow.nodes.agent.installPlugin.install')}
|
||||
cancelButtonText={t('workflow.nodes.agent.installPlugin.cancel')}
|
||||
modelTitle={t('workflow.nodes.agent.installPlugin.title')}
|
||||
description={t('workflow.nodes.agent.installPlugin.desc')}
|
||||
cardTitleLeft={<>
|
||||
<Badge2 className='mx-1' size="s" state={BadgeState.Warning}>
|
||||
{`${pluginDetail.version} -> ${target!.version}`}
|
||||
</Badge2>
|
||||
</>}
|
||||
modalBottomLeft={
|
||||
<Link
|
||||
className='flex justify-center items-center gap-1'
|
||||
href={`${marketplaceUrlPrefix}/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`}
|
||||
target='_blank'
|
||||
>
|
||||
<span className='text-text-accent system-xs-regular text-xs'>
|
||||
{t('workflow.nodes.agent.installPlugin.changelog')}
|
||||
</span>
|
||||
<RiExternalLinkLine className='text-text-accent size-3' />
|
||||
</Link>
|
||||
}
|
||||
/>}
|
||||
{pluginDetail && <PluginVersionPicker
|
||||
isShow={isShow}
|
||||
@ -62,7 +98,10 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => {
|
||||
pluginID={pluginId}
|
||||
currentVersion={pluginDetail.version}
|
||||
onSelect={(state) => {
|
||||
setTargetVersion(state.version)
|
||||
setTarget({
|
||||
pluginUniqueIden: state.unique_identifier,
|
||||
version: state.version,
|
||||
})
|
||||
showUpdateModal()
|
||||
}}
|
||||
trigger={
|
||||
|
||||
@ -26,7 +26,7 @@ const nodeDefault: NodeDefault<AgentNodeType> = {
|
||||
if (!strategy) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.checkList.strategyNotSelected'),
|
||||
errorMessage: t('workflow.nodes.agent.checkList.strategyNotSelected'),
|
||||
}
|
||||
}
|
||||
for (const param of strategy.parameters) {
|
||||
|
||||
@ -27,7 +27,6 @@ import {
|
||||
getProcessedFilesFromResponse,
|
||||
} from '@/app/components/base/file-uploader/utils'
|
||||
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
|
||||
type GetAbortController = (abortController: AbortController) => void
|
||||
type SendCallback = {
|
||||
@ -276,6 +275,7 @@ export const useChat = (
|
||||
)
|
||||
setSuggestQuestions(data)
|
||||
}
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
catch (error) {
|
||||
setSuggestQuestions([])
|
||||
}
|
||||
@ -331,8 +331,7 @@ export const useChat = (
|
||||
responseItem.workflowProcess!.tracing!.push({
|
||||
...data,
|
||||
status: NodeRunningStatus.Running,
|
||||
details: [],
|
||||
} as any)
|
||||
})
|
||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
||||
draft[currentIndex] = {
|
||||
@ -341,30 +340,21 @@ export const useChat = (
|
||||
}
|
||||
}))
|
||||
},
|
||||
onIterationNext: ({ data }) => {
|
||||
const tracing = responseItem.workflowProcess!.tracing!
|
||||
const iterations = tracing.find(item => item.node_id === data.node_id
|
||||
&& (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id))!
|
||||
iterations.details!.push([])
|
||||
|
||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||
const currentIndex = draft.length - 1
|
||||
draft[currentIndex] = responseItem
|
||||
}))
|
||||
},
|
||||
onIterationFinish: ({ data }) => {
|
||||
const tracing = responseItem.workflowProcess!.tracing!
|
||||
const iterationsIndex = tracing.findIndex(item => item.node_id === data.node_id
|
||||
&& (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id))!
|
||||
tracing[iterationsIndex] = {
|
||||
...tracing[iterationsIndex],
|
||||
...data,
|
||||
status: NodeRunningStatus.Succeeded,
|
||||
} as any
|
||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||
const currentIndex = draft.length - 1
|
||||
draft[currentIndex] = responseItem
|
||||
}))
|
||||
const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.id === data.id)
|
||||
if (currentTracingIndex > -1) {
|
||||
responseItem.workflowProcess!.tracing[currentTracingIndex] = {
|
||||
...responseItem.workflowProcess!.tracing[currentTracingIndex],
|
||||
...data,
|
||||
}
|
||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
||||
draft[currentIndex] = {
|
||||
...draft[currentIndex],
|
||||
...responseItem,
|
||||
}
|
||||
}))
|
||||
}
|
||||
},
|
||||
onNodeStarted: ({ data }) => {
|
||||
if (data.iteration_id)
|
||||
@ -386,16 +376,7 @@ export const useChat = (
|
||||
if (data.iteration_id)
|
||||
return
|
||||
|
||||
const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => {
|
||||
if (!item.execution_metadata?.parallel_id)
|
||||
return item.node_id === data.node_id
|
||||
return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id)
|
||||
})
|
||||
if (responseItem.workflowProcess!.tracing[currentIndex].retryDetail)
|
||||
responseItem.workflowProcess!.tracing[currentIndex].retryDetail?.push(data as NodeTracing)
|
||||
else
|
||||
responseItem.workflowProcess!.tracing[currentIndex].retryDetail = [data as NodeTracing]
|
||||
|
||||
responseItem.workflowProcess!.tracing!.push(data)
|
||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
||||
draft[currentIndex] = {
|
||||
@ -408,27 +389,20 @@ export const useChat = (
|
||||
if (data.iteration_id)
|
||||
return
|
||||
|
||||
const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => {
|
||||
if (!item.execution_metadata?.parallel_id)
|
||||
return item.node_id === data.node_id
|
||||
return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id)
|
||||
})
|
||||
responseItem.workflowProcess!.tracing[currentIndex] = {
|
||||
...(responseItem.workflowProcess!.tracing[currentIndex]?.extras
|
||||
? { extras: responseItem.workflowProcess!.tracing[currentIndex].extras }
|
||||
: {}),
|
||||
...(responseItem.workflowProcess!.tracing[currentIndex]?.retryDetail
|
||||
? { retryDetail: responseItem.workflowProcess!.tracing[currentIndex].retryDetail }
|
||||
: {}),
|
||||
...data,
|
||||
} as any
|
||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
||||
draft[currentIndex] = {
|
||||
...draft[currentIndex],
|
||||
...responseItem,
|
||||
const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.id === data.id)
|
||||
if (currentTracingIndex > -1) {
|
||||
responseItem.workflowProcess!.tracing[currentTracingIndex] = {
|
||||
...responseItem.workflowProcess!.tracing[currentTracingIndex],
|
||||
...data,
|
||||
}
|
||||
}))
|
||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
||||
draft[currentIndex] = {
|
||||
...draft[currentIndex],
|
||||
...responseItem,
|
||||
}
|
||||
}))
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@ -24,7 +24,6 @@ import Toast from '../../base/toast'
|
||||
import InputsPanel from './inputs-panel'
|
||||
import cn from '@/utils/classnames'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import formatNodeList from '@/app/components/workflow/run/utils/format-log'
|
||||
|
||||
const WorkflowPreview = () => {
|
||||
const { t } = useTranslation()
|
||||
@ -161,7 +160,7 @@ const WorkflowPreview = () => {
|
||||
{currentTab === 'TRACING' && (
|
||||
<TracingPanel
|
||||
className='bg-background-section-burn'
|
||||
list={formatNodeList(workflowRunningData?.tracing || [], t)}
|
||||
list={workflowRunningData?.tracing || []}
|
||||
/>
|
||||
)}
|
||||
{currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && (
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useStore as usePluginDependenciesStore } from './store'
|
||||
import { useMutationCheckDependecies } from '@/service/use-plugins'
|
||||
import { useMutationCheckDependencies } from '@/service/use-plugins'
|
||||
|
||||
export const usePluginDependencies = () => {
|
||||
const { mutateAsync } = useMutationCheckDependecies()
|
||||
const { mutateAsync } = useMutationCheckDependencies()
|
||||
|
||||
const handleCheckPluginDependencies = useCallback(async (appId: string) => {
|
||||
const { leaked_dependencies } = await mutateAsync(appId)
|
||||
|
||||
@ -27,7 +27,7 @@ const AgentLogItem = ({
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
|
||||
return (
|
||||
<div className='border-[0.5px] border-components-panel-border rounded-[10px]'>
|
||||
<div className='bg-background-default border-[0.5px] border-components-panel-border rounded-[10px]'>
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center pl-1.5 pt-2 pr-3 pb-2 cursor-pointer',
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { RiArrowLeftLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AgentLogNavMore from './agent-log-nav-more'
|
||||
import Button from '@/app/components/base/button'
|
||||
import type { AgentLogItemWithChildren } from '@/types/workflow'
|
||||
@ -11,10 +12,14 @@ const AgentLogNav = ({
|
||||
agentOrToolLogItemStack,
|
||||
onShowAgentOrToolLog,
|
||||
}: AgentLogNavProps) => {
|
||||
const options = agentOrToolLogItemStack.slice(2)
|
||||
const { t } = useTranslation()
|
||||
const agentOrToolLogItemStackLength = agentOrToolLogItemStack.length
|
||||
const first = agentOrToolLogItemStack[0]
|
||||
const mid = agentOrToolLogItemStack.slice(1, -1)
|
||||
const end = agentOrToolLogItemStack.at(-1)
|
||||
|
||||
return (
|
||||
<div className='flex items-center p-1 pr-3 h-8'>
|
||||
<div className='flex items-center p-1 pr-3 h-8 bg-components-panel-bg'>
|
||||
<Button
|
||||
className='shrink-0 px-[5px]'
|
||||
size='small'
|
||||
@ -24,32 +29,48 @@ const AgentLogNav = ({
|
||||
}}
|
||||
>
|
||||
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
|
||||
Agent
|
||||
AGENT
|
||||
</Button>
|
||||
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
|
||||
<Button
|
||||
className='shrink-0 px-[5px]'
|
||||
size='small'
|
||||
variant='ghost-accent'
|
||||
onClick={() => {}}
|
||||
>
|
||||
Agent strategy
|
||||
</Button>
|
||||
{
|
||||
!!options.length && (
|
||||
agentOrToolLogItemStackLength > 1
|
||||
? (
|
||||
<Button
|
||||
className='shrink-0 px-[5px]'
|
||||
size='small'
|
||||
variant='ghost-accent'
|
||||
onClick={() => onShowAgentOrToolLog(first)}
|
||||
>
|
||||
{t('workflow.nodes.agent.strategy.label')}
|
||||
</Button>
|
||||
)
|
||||
: (
|
||||
<div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'>
|
||||
{t('workflow.nodes.agent.strategy.label')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!!mid.length && (
|
||||
<>
|
||||
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
|
||||
<AgentLogNavMore
|
||||
options={options}
|
||||
options={mid}
|
||||
onShowAgentOrToolLog={onShowAgentOrToolLog}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
|
||||
<div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'>
|
||||
Run Actions
|
||||
</div>
|
||||
{
|
||||
!!end && agentOrToolLogItemStackLength > 2 && (
|
||||
<>
|
||||
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
|
||||
<div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'>
|
||||
{end.label}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { RiArrowRightLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type {
|
||||
AgentLogItemWithChildren,
|
||||
NodeTracing,
|
||||
@ -12,12 +13,13 @@ const AgentLogTrigger = ({
|
||||
nodeInfo,
|
||||
onShowAgentOrToolLog,
|
||||
}: AgentLogTriggerProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { agentLog } = nodeInfo
|
||||
|
||||
return (
|
||||
<div className='bg-components-button-tertiary-bg rounded-[10px]'>
|
||||
<div className='flex items-center px-3 pt-2 system-2xs-medium-uppercase text-text-tertiary'>
|
||||
Agent strategy
|
||||
{t('workflow.nodes.agent.strategy.label')}
|
||||
</div>
|
||||
<div className='flex items-center pl-3 pt-1 pr-2 pb-1.5'>
|
||||
<div className='shrink-0 w-5 h-5'></div>
|
||||
@ -28,7 +30,7 @@ const AgentLogTrigger = ({
|
||||
onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
{t('runLog.detail')}
|
||||
<RiArrowRightLine className='ml-0.5 w-3.5 h-3.5' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { RiAlertFill } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AgentLogItem from './agent-log-item'
|
||||
import AgentLogNav from './agent-log-nav'
|
||||
import type { AgentLogItemWithChildren } from '@/types/workflow'
|
||||
@ -13,17 +14,18 @@ const AgentResultPanel = ({
|
||||
agentOrToolLogListMap,
|
||||
onShowAgentOrToolLog,
|
||||
}: AgentResultPanelProps) => {
|
||||
const { t } = useTranslation()
|
||||
const top = agentOrToolLogItemStack[agentOrToolLogItemStack.length - 1]
|
||||
const list = agentOrToolLogListMap[top.id]
|
||||
|
||||
return (
|
||||
<div className='overflow-y-auto'>
|
||||
<div className='bg-background-section overflow-y-auto'>
|
||||
<AgentLogNav
|
||||
agentOrToolLogItemStack={agentOrToolLogItemStack}
|
||||
onShowAgentOrToolLog={onShowAgentOrToolLog}
|
||||
/>
|
||||
{
|
||||
<div className='p-2'>
|
||||
<div className='p-2 space-y-1'>
|
||||
{
|
||||
list.map(item => (
|
||||
<AgentLogItem
|
||||
@ -37,7 +39,7 @@ const AgentResultPanel = ({
|
||||
}
|
||||
{
|
||||
top.hasCircle && (
|
||||
<div className='flex items-center rounded-xl px-3 pr-2 border border-components-panel-border bg-components-panel-bg-blur shadow-md'>
|
||||
<div className='flex items-center mt-1 rounded-xl px-3 pr-2 border border-components-panel-border bg-components-panel-bg-blur shadow-md'>
|
||||
<div
|
||||
className='absolute inset-0 opacity-[0.4] rounded-xl'
|
||||
style={{
|
||||
@ -46,7 +48,7 @@ const AgentResultPanel = ({
|
||||
></div>
|
||||
<RiAlertFill className='mr-1.5 w-4 h-4 text-text-warning-secondary' />
|
||||
<div className='system-xs-medium text-text-primary'>
|
||||
There is circular invocation of tools/nodes in the current workflow.
|
||||
{t('runLog.circularInvocationTip')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -13,7 +13,6 @@ import { fetchRunDetail, fetchTracingList } from '@/service/log'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
import type { WorkflowRunDetailResponse } from '@/models/log'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import formatNodeList from './utils/format-log'
|
||||
export type RunProps = {
|
||||
hideResult?: boolean
|
||||
activeTab?: 'RESULT' | 'DETAIL' | 'TRACING'
|
||||
@ -61,7 +60,7 @@ const RunPanel: FC<RunProps> = ({ hideResult, activeTab = 'RESULT', runID, getRe
|
||||
const { data: nodeList } = await fetchTracingList({
|
||||
url: `/apps/${appID}/workflow-runs/${runID}/node-executions`,
|
||||
})
|
||||
setList(formatNodeList(nodeList, t))
|
||||
setList(nodeList)
|
||||
}
|
||||
catch (err) {
|
||||
notify({
|
||||
|
||||
@ -11,10 +11,12 @@ import {
|
||||
RiArrowDownSLine,
|
||||
RiMenu4Line,
|
||||
} from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useLogs } from './hooks'
|
||||
import NodePanel from './node'
|
||||
import SpecialResultPanel from './special-result-panel'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
import formatNodeList from '@/app/components/workflow/run/utils/format-log'
|
||||
|
||||
type TracingPanelProps = {
|
||||
list: NodeTracing[]
|
||||
@ -29,7 +31,8 @@ const TracingPanel: FC<TracingPanelProps> = ({
|
||||
hideNodeInfo = false,
|
||||
hideNodeProcessDetail = false,
|
||||
}) => {
|
||||
const treeNodes = list
|
||||
const { t } = useTranslation()
|
||||
const treeNodes = formatNodeList(list, t)
|
||||
const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(new Set())
|
||||
const [hoveredParallel, setHoveredParallel] = useState<string | null>(null)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user