Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins
This commit is contained in:
commit
b656b7a0dd
@ -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,9 +14,8 @@ 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'
|
||||
|
||||
interface WorkflowProcessProps {
|
||||
type WorkflowProcessProps = {
|
||||
data: WorkflowProcess
|
||||
item?: ChatItem
|
||||
expand?: boolean
|
||||
@ -26,7 +24,6 @@ interface 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}
|
||||
/>
|
||||
|
@ -11,7 +11,7 @@ import Placeholder from './base/placeholder'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { getLanguage } from '@/i18n/language'
|
||||
import { useCategories } from '../hooks'
|
||||
import { useSingleCategories } from '../hooks'
|
||||
import { renderI18nObject } from '@/hooks/use-i18n'
|
||||
|
||||
export type Props = {
|
||||
@ -43,7 +43,7 @@ const Card = ({
|
||||
}: Props) => {
|
||||
const defaultLocale = useGetLanguage()
|
||||
const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale
|
||||
const { categoriesMap } = useCategories()
|
||||
const { categoriesMap } = useSingleCategories()
|
||||
const { category, type, name, org, label, brief, icon, verified } = payload
|
||||
const isBundle = !['plugin', 'model', 'tool', 'extension', 'agent_strategy'].includes(type)
|
||||
const cornerMark = isBundle ? categoriesMap.bundle?.label : categoriesMap[category]?.label
|
||||
|
@ -64,3 +64,31 @@ export const useCategories = (translateFromOut?: TFunction) => {
|
||||
categoriesMap,
|
||||
}
|
||||
}
|
||||
|
||||
export const useSingleCategories = (translateFromOut?: TFunction) => {
|
||||
const { t: translation } = useTranslation()
|
||||
const t = translateFromOut || translation
|
||||
|
||||
const categories = categoryKeys.map((category) => {
|
||||
if (category === 'agent') {
|
||||
return {
|
||||
name: 'agent_strategy',
|
||||
label: t(`plugin.categorySingle.${category}`),
|
||||
}
|
||||
}
|
||||
return {
|
||||
name: category,
|
||||
label: t(`plugin.categorySingle.${category}`),
|
||||
}
|
||||
})
|
||||
|
||||
const categoriesMap = categories.reduce((acc, category) => {
|
||||
acc[category.name] = category
|
||||
return acc
|
||||
}, {} as Record<string, Category>)
|
||||
|
||||
return {
|
||||
categories,
|
||||
categoriesMap,
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -22,7 +22,7 @@ import cn from '@/utils/classnames'
|
||||
import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config'
|
||||
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||
import { useInvalidateAllBuiltInTools, useInvalidateAllToolProviders } from '@/service/use-tools'
|
||||
import { useCategories } from '../hooks'
|
||||
import { useSingleCategories } from '../hooks'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
|
||||
@ -36,7 +36,7 @@ const PluginItem: FC<Props> = ({
|
||||
plugin,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { categoriesMap } = useCategories()
|
||||
const { categoriesMap } = useSingleCategories()
|
||||
const currentPluginID = usePluginPageContext(v => v.currentPluginID)
|
||||
const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID)
|
||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -404,6 +404,7 @@ export const SUPPORT_OUTPUT_VARS_NODE = [
|
||||
BlockEnum.HttpRequest, BlockEnum.Tool, BlockEnum.VariableAssigner, BlockEnum.VariableAggregator, BlockEnum.QuestionClassifier,
|
||||
BlockEnum.ParameterExtractor, BlockEnum.Iteration,
|
||||
BlockEnum.DocExtractor, BlockEnum.ListFilter,
|
||||
BlockEnum.Agent,
|
||||
]
|
||||
|
||||
export const LLM_OUTPUT_STRUCT: Var[] = [
|
||||
|
@ -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={
|
||||
|
@ -319,16 +319,13 @@ const formatItem = (
|
||||
case BlockEnum.Agent: {
|
||||
const payload = data as AgentNodeType
|
||||
const outputs: Var[] = []
|
||||
Object.keys(payload.output_schema.properties).forEach((outputKey) => {
|
||||
Object.keys(payload.output_schema?.properties || {}).forEach((outputKey) => {
|
||||
const output = payload.output_schema.properties[outputKey]
|
||||
outputs.push({
|
||||
variable: outputKey,
|
||||
type: output.type === 'array'
|
||||
? `Array[${output.items?.type.slice(0, 1).toLocaleUpperCase()}${output.items?.type.slice(1)}]` as VarType
|
||||
: `${output.type.slice(0, 1).toLocaleUpperCase()}${output.type.slice(1)}` as VarType,
|
||||
// TODO: is this required?
|
||||
// @ts-expect-error todo added
|
||||
description: output.description,
|
||||
})
|
||||
})
|
||||
res.vars = [
|
||||
|
@ -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) {
|
||||
|
@ -28,7 +28,7 @@ import {
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { getLastAnswer } from '@/app/components/base/chat/utils'
|
||||
|
||||
interface ChatWrapperProps {
|
||||
type ChatWrapperProps = {
|
||||
showConversationVariableModal: boolean
|
||||
onConversationModalHide: () => void
|
||||
showInputsFieldsPanel: boolean
|
||||
|
@ -27,10 +27,9 @@ 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
|
||||
interface SendCallback {
|
||||
type SendCallback = {
|
||||
onGetSuggestedQuestions?: (responseItemId: string, getAbortController: GetAbortController) => Promise<any>
|
||||
}
|
||||
export const useChat = (
|
||||
@ -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,
|
||||
}
|
||||
}))
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -22,7 +22,7 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
|
||||
export interface ChatWrapperRefType {
|
||||
export type ChatWrapperRefType = {
|
||||
handleRestart: () => void
|
||||
}
|
||||
const DebugAndPreview = () => {
|
||||
|
@ -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)
|
||||
|
@ -30,26 +30,26 @@ const AgentLogItem = ({
|
||||
<div className='border-[0.5px] border-components-panel-border rounded-[10px]'>
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center pl-1.5 pt-2 pr-3 pb-2',
|
||||
'flex items-center pl-1.5 pt-2 pr-3 pb-2 cursor-pointer',
|
||||
expanded && 'pb-1',
|
||||
)}
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
{
|
||||
expanded
|
||||
? <RiArrowRightSLine className='shrink-0 w-4 h-4 rotate-90' />
|
||||
: <RiArrowRightSLine className='shrink-0 w-4 h-4' />
|
||||
? <RiArrowRightSLine className='shrink-0 w-4 h-4 rotate-90 text-text-quaternary' />
|
||||
: <RiArrowRightSLine className='shrink-0 w-4 h-4 text-text-quaternary' />
|
||||
}
|
||||
<div className='shrink-0 mr-1.5 w-5 h-5'></div>
|
||||
<div className='grow system-sm-semibold-uppercase text-text-secondary truncate'>{label}</div>
|
||||
<div className='shrink-0 mr-2 system-xs-regular text-text-tertiary'>0.02s</div>
|
||||
{/* <div className='shrink-0 mr-2 system-xs-regular text-text-tertiary'>0.02s</div> */}
|
||||
<NodeStatusIcon status={status} />
|
||||
</div>
|
||||
{
|
||||
expanded && (
|
||||
<div className='p-1 pt-0'>
|
||||
{
|
||||
!!children.length && (
|
||||
!!children?.length && (
|
||||
<Button
|
||||
className='flex items-center justify-between mb-1 w-full'
|
||||
variant='tertiary'
|
||||
|
@ -19,7 +19,9 @@ const AgentLogNav = ({
|
||||
className='shrink-0 px-[5px]'
|
||||
size='small'
|
||||
variant='ghost-accent'
|
||||
onClick={() => onShowAgentOrToolLog()}
|
||||
onClick={() => {
|
||||
onShowAgentOrToolLog()
|
||||
}}
|
||||
>
|
||||
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
|
||||
Agent
|
||||
@ -31,7 +33,6 @@ const AgentLogNav = ({
|
||||
variant='ghost-accent'
|
||||
onClick={() => {}}
|
||||
>
|
||||
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
|
||||
Agent strategy
|
||||
</Button>
|
||||
{
|
||||
|
@ -24,7 +24,9 @@ const AgentLogTrigger = ({
|
||||
<div className='grow mx-0.5 px-1 system-xs-medium text-text-secondary'></div>
|
||||
<div
|
||||
className='shrink-0 flex items-center px-[1px] system-xs-regular-uppercase text-text-tertiary cursor-pointer'
|
||||
onClick={() => onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)}
|
||||
onClick={() => {
|
||||
onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
<RiArrowRightLine className='ml-0.5 w-3.5 h-3.5' />
|
||||
|
@ -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({
|
||||
|
@ -36,7 +36,10 @@ const SpecialResultPanel = ({
|
||||
handleShowAgentOrToolLog,
|
||||
}: SpecialResultPanelProps) => {
|
||||
return (
|
||||
<>
|
||||
<div onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
e.nativeEvent.stopImmediatePropagation()
|
||||
}}>
|
||||
{
|
||||
!!showRetryDetail && !!retryResultList?.length && setShowRetryDetailFalse && (
|
||||
<RetryResultPanel
|
||||
@ -63,7 +66,7 @@ const SpecialResultPanel = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
@ -166,7 +169,13 @@ const TracingPanel: FC<TracingPanelProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn(className || 'bg-components-panel-bg', 'py-2')}>
|
||||
<div
|
||||
className={cn(className || 'bg-components-panel-bg', 'py-2')}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
e.nativeEvent.stopImmediatePropagation()
|
||||
}}
|
||||
>
|
||||
{treeNodes.map(renderNode)}
|
||||
</div>
|
||||
)
|
||||
|
@ -3,10 +3,17 @@ const translation = {
|
||||
all: 'All',
|
||||
models: 'Models',
|
||||
tools: 'Tools',
|
||||
agents: 'Agent Strategy',
|
||||
agents: 'Agent Strategies',
|
||||
extensions: 'Extensions',
|
||||
bundles: 'Bundles',
|
||||
},
|
||||
categorySingle: {
|
||||
model: 'Model',
|
||||
tool: 'Tool',
|
||||
agent: 'Agent Strategy',
|
||||
extension: 'Extension',
|
||||
bundle: 'Bundle',
|
||||
},
|
||||
search: 'Search',
|
||||
allCategories: 'All Categories',
|
||||
searchCategories: 'Search Categories',
|
||||
|
@ -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: {
|
||||
|
@ -7,6 +7,13 @@ const translation = {
|
||||
extensions: '扩展',
|
||||
bundles: '插件集',
|
||||
},
|
||||
categorySingle: {
|
||||
model: '模型',
|
||||
tool: '工具',
|
||||
agent: 'Agent 策略',
|
||||
extension: '扩展',
|
||||
bundle: '插件集',
|
||||
},
|
||||
search: '搜索',
|
||||
allCategories: '所有类别',
|
||||
searchCategories: '搜索类别',
|
||||
|
@ -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: {
|
||||
|
@ -365,7 +365,7 @@ export const usePluginTaskList = () => {
|
||||
queryKey: usePluginTaskListKey,
|
||||
queryFn: async () => {
|
||||
const currentData = await get<{ tasks: PluginTask[] }>('/workspaces/current/plugin/tasks?page=1&page_size=100')
|
||||
const taskDone = currentData.tasks.every(task => task.status === TaskStatus.success)
|
||||
const taskDone = currentData.tasks.every(task => task.status === TaskStatus.success || task.status === TaskStatus.failed)
|
||||
|
||||
if (taskDone)
|
||||
setEnabled(false)
|
||||
@ -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
Block a user