mirror of https://github.com/langgenius/dify.git
Merge branch 'feat/plugins' into dev/plugin-deploy
This commit is contained in:
commit
5cc9c79284
|
|
@ -1,20 +1,18 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiEqualizer2Line,
|
||||
} from '@remixicon/react'
|
||||
import type { PopupProps } from './config-popup'
|
||||
import ConfigPopup from './config-popup'
|
||||
import cn from '@/utils/classnames'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
|
||||
const I18N_PREFIX = 'app.tracing'
|
||||
|
||||
type Props = {
|
||||
readOnly: boolean
|
||||
className?: string
|
||||
|
|
@ -28,7 +26,6 @@ const ConfigBtn: FC<Props> = ({
|
|||
controlShowPopup,
|
||||
...popupProps
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, doSetOpen] = useState(false)
|
||||
const openRef = useRef(open)
|
||||
const setOpen = useCallback((v: boolean) => {
|
||||
|
|
@ -50,21 +47,6 @@ const ConfigBtn: FC<Props> = ({
|
|||
if (popupProps.readOnly && !hasConfigured)
|
||||
return null
|
||||
|
||||
const triggerContent = hasConfigured
|
||||
? (
|
||||
<div className={cn(className, 'p-1 rounded-md hover:bg-black/5 cursor-pointer')}>
|
||||
<Settings04 className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<Button variant='primary'
|
||||
className={cn(className, '!h-8 !px-3 select-none')}
|
||||
>
|
||||
<Settings04 className='mr-1 w-4 h-4' />
|
||||
<span className='text-[13px]'>{t(`${I18N_PREFIX}.config`)}</span>
|
||||
</Button>
|
||||
)
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
|
|
@ -72,11 +54,13 @@ const ConfigBtn: FC<Props> = ({
|
|||
placement='bottom-end'
|
||||
offset={{
|
||||
mainAxis: 12,
|
||||
crossAxis: hasConfigured ? 8 : 0,
|
||||
crossAxis: hasConfigured ? 8 : 49,
|
||||
}}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={handleTrigger}>
|
||||
{triggerContent}
|
||||
<div className={cn(className, 'p-1 rounded-md')}>
|
||||
<RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[11]'>
|
||||
<ConfigPopup {...popupProps} />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
RiArrowDownDoubleLine,
|
||||
} from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useBoolean } from 'ahooks'
|
||||
|
|
@ -8,7 +11,6 @@ import type { LangFuseConfig, LangSmithConfig } from './type'
|
|||
import { TracingProvider } from './type'
|
||||
import TracingIcon from './tracing-icon'
|
||||
import ConfigButton from './config-button'
|
||||
import cn from '@/utils/classnames'
|
||||
import { LangfuseIcon, LangsmithIcon } from '@/app/components/base/icons/src/public/tracing'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import { fetchTracingConfig as doFetchTracingConfig, fetchTracingStatus, updateTracingStatus } from '@/service/apps'
|
||||
|
|
@ -16,6 +18,8 @@ import type { TracingStatus } from '@/models/app'
|
|||
import Toast from '@/app/components/base/toast'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const I18N_PREFIX = 'app.tracing'
|
||||
|
||||
|
|
@ -27,7 +31,7 @@ const Title = ({
|
|||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'flex items-center text-lg font-semibold text-gray-900')}>
|
||||
<div className={cn('flex items-center system-xl-semibold text-text-primary', className)}>
|
||||
{t('common.appMenus.overview')}
|
||||
</div>
|
||||
)
|
||||
|
|
@ -135,43 +139,68 @@ const Panel: FC = () => {
|
|||
return (
|
||||
<div className={cn('mb-3 flex justify-between items-center')}>
|
||||
<Title className='h-[41px]' />
|
||||
<div className='flex items-center p-2 rounded-xl border-[0.5px] border-gray-200 shadow-xs cursor-pointer hover:bg-gray-100' onClick={showPopup}>
|
||||
{!inUseTracingProvider
|
||||
? <>
|
||||
<TracingIcon size='md' className='mr-2' />
|
||||
<div className='leading-5 text-sm font-semibold text-gray-700'>{t(`${I18N_PREFIX}.title`)}</div>
|
||||
</>
|
||||
: <InUseProviderIcon className='ml-1 h-4' />}
|
||||
|
||||
{hasConfiguredTracing && (
|
||||
<div className='ml-4 mr-1 flex items-center'>
|
||||
<Indicator color={enabled ? 'green' : 'gray'} />
|
||||
<div className='ml-1.5 text-xs font-semibold text-gray-500 uppercase'>
|
||||
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center p-2 rounded-xl bg-background-default-dodge border-t border-l-[0.5px] border-effects-highlight shadow-xs cursor-pointer hover:bg-background-default-lighter hover:border-effects-highlight-lightmode-off',
|
||||
controlShowPopup && 'bg-background-default-lighter border-effects-highlight-lightmode-off',
|
||||
)}
|
||||
onClick={showPopup}
|
||||
>
|
||||
{!inUseTracingProvider && (
|
||||
<>
|
||||
<TracingIcon size='md' />
|
||||
<div className='mx-2 system-sm-semibold text-text-secondary'>{t(`${I18N_PREFIX}.title`)}</div>
|
||||
<div className='flex items-center' onClick={e => e.stopPropagation()}>
|
||||
<ConfigButton
|
||||
appId={appId}
|
||||
readOnly={readOnly}
|
||||
hasConfigured={false}
|
||||
enabled={enabled}
|
||||
onStatusChange={handleTracingEnabledChange}
|
||||
chosenProvider={inUseTracingProvider}
|
||||
onChooseProvider={handleChooseProvider}
|
||||
langSmithConfig={langSmithConfig}
|
||||
langFuseConfig={langFuseConfig}
|
||||
onConfigUpdated={handleTracingConfigUpdated}
|
||||
onConfigRemoved={handleTracingConfigRemoved}
|
||||
controlShowPopup={controlShowPopup}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Divider type='vertical' className='h-3.5' />
|
||||
<div className='p-1 rounded-md'>
|
||||
<RiArrowDownDoubleLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasConfiguredTracing && (
|
||||
<div className='ml-2 w-px h-3.5 bg-gray-200'></div>
|
||||
<>
|
||||
<div className='ml-4 mr-1 flex items-center'>
|
||||
<Indicator color={enabled ? 'green' : 'gray'} />
|
||||
<div className='ml-1.5 system-xs-semibold-uppercase text-text-tertiary'>
|
||||
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
|
||||
</div>
|
||||
</div>
|
||||
<InUseProviderIcon className='ml-1 h-4' />
|
||||
<Divider type='vertical' className='h-3.5' />
|
||||
<div className='flex items-center' onClick={e => e.stopPropagation()}>
|
||||
<ConfigButton
|
||||
appId={appId}
|
||||
readOnly={readOnly}
|
||||
hasConfigured
|
||||
className='ml-2'
|
||||
enabled={enabled}
|
||||
onStatusChange={handleTracingEnabledChange}
|
||||
chosenProvider={inUseTracingProvider}
|
||||
onChooseProvider={handleChooseProvider}
|
||||
langSmithConfig={langSmithConfig}
|
||||
langFuseConfig={langFuseConfig}
|
||||
onConfigUpdated={handleTracingConfigUpdated}
|
||||
onConfigRemoved={handleTracingConfigRemoved}
|
||||
controlShowPopup={controlShowPopup}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className='flex items-center' onClick={e => e.stopPropagation()}>
|
||||
<ConfigButton
|
||||
appId={appId}
|
||||
readOnly={readOnly}
|
||||
hasConfigured
|
||||
className='ml-2'
|
||||
enabled={enabled}
|
||||
onStatusChange={handleTracingEnabledChange}
|
||||
chosenProvider={inUseTracingProvider}
|
||||
onChooseProvider={handleChooseProvider}
|
||||
langSmithConfig={langSmithConfig}
|
||||
langFuseConfig={langFuseConfig}
|
||||
onConfigUpdated={handleTracingConfigUpdated}
|
||||
onConfigRemoved={handleTracingConfigRemoved}
|
||||
controlShowPopup={controlShowPopup}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
'use client'
|
||||
import { ChevronDoubleDownIcon } from '@heroicons/react/20/solid'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import React, { useCallback } from 'react'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
const I18N_PREFIX = 'app.tracing'
|
||||
|
||||
type Props = {
|
||||
isFold: boolean
|
||||
onFoldChange: (isFold: boolean) => void
|
||||
}
|
||||
|
||||
const ToggleFoldBtn: FC<Props> = ({
|
||||
isFold,
|
||||
onFoldChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleFoldChange = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
onFoldChange(!isFold)
|
||||
}, [isFold, onFoldChange])
|
||||
return (
|
||||
// text-[0px] to hide spacing between tooltip elements
|
||||
<div className='shrink-0 cursor-pointer text-[0px]' onClick={handleFoldChange}>
|
||||
<Tooltip
|
||||
popupContent={t(`${I18N_PREFIX}.${isFold ? 'expand' : 'collapse'}`)}
|
||||
>
|
||||
{isFold && (
|
||||
<div className='p-1 rounded-md text-gray-500 hover:text-gray-800 hover:bg-black/5'>
|
||||
<ChevronDoubleDownIcon className='w-4 h-4' />
|
||||
</div>
|
||||
)}
|
||||
{!isFold && (
|
||||
<div className='p-2 rounded-lg text-gray-500 border-[0.5px] border-gray-200 hover:text-gray-800 hover:bg-black/5'>
|
||||
<ChevronDoubleDownIcon className='w-4 h-4 transform rotate-180' />
|
||||
</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(ToggleFoldBtn)
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ const translation = {
|
|||
allTags: 'All Tags',
|
||||
searchTags: 'Search Tags',
|
||||
tags: {
|
||||
agent: 'Agent',
|
||||
search: 'Search',
|
||||
image: 'Image',
|
||||
videos: 'Videos',
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ const translation = {
|
|||
link: 'detail panel',
|
||||
tipRight: ' view it.',
|
||||
},
|
||||
actionLogs: 'Action Logs',
|
||||
circularInvocationTip: 'There is circular invocation of tools/nodes in the current workflow.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -725,6 +725,7 @@ const translation = {
|
|||
modelNotSupport: {
|
||||
title: 'Unsupported Model',
|
||||
desc: 'The installed plugin version does not provide this model.',
|
||||
descForVersionSwitch: 'The installed plugin version does not provide this model. Click to switch version.',
|
||||
},
|
||||
configureModel: 'Configure Model',
|
||||
notAuthorized: 'Not Authorized',
|
||||
|
|
@ -755,9 +756,16 @@ const translation = {
|
|||
},
|
||||
json: 'agent generated json',
|
||||
},
|
||||
},
|
||||
checkList: {
|
||||
strategyNotSelected: 'Strategy not selected',
|
||||
checkList: {
|
||||
strategyNotSelected: 'Strategy not selected',
|
||||
},
|
||||
installPlugin: {
|
||||
title: 'Install Plugin',
|
||||
desc: 'About to install the following plugin',
|
||||
changelog: 'Change log',
|
||||
install: 'Install',
|
||||
cancel: 'Cancel',
|
||||
},
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ const translation = {
|
|||
allTags: '所有标签',
|
||||
searchTags: '搜索标签',
|
||||
tags: {
|
||||
agent: 'Agent',
|
||||
search: '搜索',
|
||||
image: '图片',
|
||||
videos: '视频',
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ const translation = {
|
|||
link: '详细信息面板',
|
||||
tipRight: '查看它。',
|
||||
},
|
||||
actionLogs: 'Action 日志',
|
||||
circularInvocationTip: '当前工作流中存在工具/节点的循环调用。',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -725,6 +725,7 @@ const translation = {
|
|||
modelNotSupport: {
|
||||
title: '不支持的模型',
|
||||
desc: '已安装的插件版本不提供此模型。',
|
||||
descForVersionSwitch: '已安装的插件版本不提供此模型。点击切换版本。',
|
||||
},
|
||||
model: '模型',
|
||||
toolbox: '工具箱',
|
||||
|
|
@ -758,6 +759,13 @@ const translation = {
|
|||
checkList: {
|
||||
strategyNotSelected: '未选择策略',
|
||||
},
|
||||
installPlugin: {
|
||||
title: '安装插件',
|
||||
desc: '即将安装以下插件',
|
||||
changelog: '更新日志',
|
||||
install: '安装',
|
||||
cancel: '取消',
|
||||
},
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
|||
|
|
@ -423,10 +423,10 @@ export const useDownloadPlugin = (info: { organization: string; pluginName: stri
|
|||
})
|
||||
}
|
||||
|
||||
export const useMutationCheckDependecies = () => {
|
||||
export const useMutationCheckDependencies = () => {
|
||||
return useMutation({
|
||||
mutationFn: (appId: string) => {
|
||||
return get<{ leaked_dependencies: Dependency[] }>(`/apps/import/${appId}/check-dependencies`)
|
||||
return get<{ leaked_dependencies: Dependency[] }>(`/apps/imports/${appId}/check-dependencies`)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export type NodeTracing = {
|
|||
index: number
|
||||
predecessor_node_id: string
|
||||
node_id: string
|
||||
iteration_id?: string
|
||||
node_type: BlockEnum
|
||||
title: string
|
||||
inputs: any
|
||||
|
|
|
|||
Loading…
Reference in New Issue