mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
Merge branch 'main' into feat/attachments
This commit is contained in:
@ -4,6 +4,7 @@ import { memo, useMemo } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiAlertFill,
|
||||
RiQuestionLine,
|
||||
} from '@remixicon/react'
|
||||
import WeightedScore from './weighted-score'
|
||||
@ -26,7 +27,6 @@ import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type {
|
||||
DataSet,
|
||||
WeightedScoreEnum,
|
||||
} from '@/models/datasets'
|
||||
import { RerankingModeEnum } from '@/models/datasets'
|
||||
import cn from '@/utils/classnames'
|
||||
@ -112,12 +112,11 @@ const ConfigContent: FC<Props> = ({
|
||||
})
|
||||
}
|
||||
|
||||
const handleWeightedScoreChange = (value: { type: WeightedScoreEnum; value: number[] }) => {
|
||||
const handleWeightedScoreChange = (value: { value: number[] }) => {
|
||||
const configs = {
|
||||
...datasetConfigs,
|
||||
weights: {
|
||||
...datasetConfigs.weights!,
|
||||
weight_type: value.type,
|
||||
vector_setting: {
|
||||
...datasetConfigs.weights!.vector_setting!,
|
||||
vector_weight: value.value[0],
|
||||
@ -178,14 +177,6 @@ const ConfigContent: FC<Props> = ({
|
||||
popupContent={(
|
||||
<div className='w-[320px]'>
|
||||
{t('dataset.nTo1RetrievalLegacy')}
|
||||
<a
|
||||
className='underline'
|
||||
href={LEGACY_LINK_MAP[language]}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
({t('dataset.nTo1RetrievalLegacyLink')})
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
@ -196,6 +187,22 @@ const ConfigContent: FC<Props> = ({
|
||||
description={t('appDebug.datasetConfig.retrieveOneWay.description')}
|
||||
isChosen={type === RETRIEVE_TYPE.oneWay}
|
||||
onChosen={() => { setType(RETRIEVE_TYPE.oneWay) }}
|
||||
extra={(
|
||||
<div className='flex pl-3 pr-1 py-3 border-t border-divider-subtle bg-state-warning-hover rounded-b-xl'>
|
||||
<RiAlertFill className='shrink-0 mr-1.5 w-4 h-4 text-text-warning-secondary' />
|
||||
<div className='system-xs-medium text-text-primary'>
|
||||
{t('dataset.nTo1RetrievalLegacyLinkText')}
|
||||
<a
|
||||
className='text-text-accent'
|
||||
href={LEGACY_LINK_MAP[language]}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
{t('dataset.nTo1RetrievalLegacyLink')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<RadioCard
|
||||
icon={<MultiPathRetrieval className='shrink-0 mr-3 w-9 h-9 rounded-lg' />}
|
||||
@ -302,7 +309,6 @@ const ConfigContent: FC<Props> = ({
|
||||
<div className='mt-2 space-y-4'>
|
||||
<WeightedScore
|
||||
value={{
|
||||
type: datasetConfigs.weights!.weight_type,
|
||||
value: [
|
||||
datasetConfigs.weights!.vector_setting.vector_weight,
|
||||
datasetConfigs.weights!.keyword_setting.keyword_weight,
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
.weightedScoreSliderTrack {
|
||||
background: var(--color-util-colors-blue-light-blue-light-500) !important;
|
||||
}
|
||||
|
||||
.weightedScoreSliderTrack-1 {
|
||||
background: transparent !important;
|
||||
}
|
||||
@ -1,9 +1,6 @@
|
||||
import { memo, useCallback } from 'react'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
DEFAULT_WEIGHTED_SCORE,
|
||||
WeightedScoreEnum,
|
||||
} from '@/models/datasets'
|
||||
import './weighted-score.css'
|
||||
import Slider from '@/app/components/base/slider'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
@ -17,7 +14,6 @@ const formatNumber = (value: number) => {
|
||||
}
|
||||
|
||||
type Value = {
|
||||
type: WeightedScoreEnum
|
||||
value: number[]
|
||||
}
|
||||
|
||||
@ -30,78 +26,31 @@ const WeightedScore = ({
|
||||
onChange = () => {},
|
||||
}: WeightedScoreProps) => {
|
||||
const { t } = useTranslation()
|
||||
const options = [
|
||||
{
|
||||
value: WeightedScoreEnum.SemanticFirst,
|
||||
label: t('dataset.weightedScore.semanticFirst'),
|
||||
},
|
||||
{
|
||||
value: WeightedScoreEnum.KeywordFirst,
|
||||
label: t('dataset.weightedScore.keywordFirst'),
|
||||
},
|
||||
{
|
||||
value: WeightedScoreEnum.Customized,
|
||||
label: t('dataset.weightedScore.customized'),
|
||||
},
|
||||
]
|
||||
|
||||
const disabled = value.type !== WeightedScoreEnum.Customized
|
||||
|
||||
const handleTypeChange = useCallback((type: WeightedScoreEnum) => {
|
||||
const result = { ...value, type }
|
||||
|
||||
if (type === WeightedScoreEnum.SemanticFirst)
|
||||
result.value = [DEFAULT_WEIGHTED_SCORE.semanticFirst.semantic, DEFAULT_WEIGHTED_SCORE.semanticFirst.keyword]
|
||||
|
||||
if (type === WeightedScoreEnum.KeywordFirst)
|
||||
result.value = [DEFAULT_WEIGHTED_SCORE.keywordFirst.semantic, DEFAULT_WEIGHTED_SCORE.keywordFirst.keyword]
|
||||
|
||||
onChange(result)
|
||||
}, [value, onChange])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='flex items-center mb-1 space-x-4'>
|
||||
{
|
||||
options.map(option => (
|
||||
<div
|
||||
key={option.value}
|
||||
className='flex py-1.5 max-w-[calc((100%-32px)/3)] system-sm-regular text-text-secondary cursor-pointer'
|
||||
onClick={() => handleTypeChange(option.value)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'shrink-0 mr-2 w-4 h-4 bg-components-radio-bg border border-components-radio-border rounded-full shadow-xs',
|
||||
value.type === option.value && 'border-[5px] border-components-radio-border-checked',
|
||||
)}
|
||||
></div>
|
||||
<div className='truncate' title={option.label}>{option.label}</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div className='flex items-center px-3 h-9 space-x-3 rounded-lg border border-components-panel-border'>
|
||||
<div className='shrink-0 flex items-center w-[90px] system-xs-semibold-uppercase text-util-colors-blue-blue-500'>
|
||||
<div className='mr-1 truncate uppercase' title={t('dataset.weightedScore.semantic') || ''}>
|
||||
{t('dataset.weightedScore.semantic')}
|
||||
</div>
|
||||
{formatNumber(value.value[0])}
|
||||
</div>
|
||||
<div className='px-3 pt-5 h-[52px] space-x-3 rounded-lg border border-components-panel-border'>
|
||||
<Slider
|
||||
className={cn('grow h-0.5 bg-gradient-to-r from-[#53B1FD] to-[#2ED3B7]', disabled && 'cursor-not-allowed')}
|
||||
className={cn('grow h-0.5 !bg-util-colors-teal-teal-500 rounded-full')}
|
||||
max={1.0}
|
||||
min={0}
|
||||
step={0.1}
|
||||
value={value.value[0]}
|
||||
onChange={v => onChange({ type: value.type, value: [v, (10 - v * 10) / 10] })}
|
||||
disabled={disabled}
|
||||
thumbClassName={cn(disabled && '!cursor-not-allowed')}
|
||||
trackClassName='!bg-transparent'
|
||||
onChange={v => onChange({ value: [v, (10 - v * 10) / 10] })}
|
||||
trackClassName='weightedScoreSliderTrack'
|
||||
/>
|
||||
<div className='shrink-0 flex items-center justify-end w-[90px] system-xs-semibold-uppercase text-util-colors-cyan-cyan-500'>
|
||||
{formatNumber(value.value[1])}
|
||||
<div className='ml-1 truncate uppercase' title={t('dataset.weightedScore.keyword') || ''}>
|
||||
{t('dataset.weightedScore.keyword')}
|
||||
<div className='flex justify-between mt-1'>
|
||||
<div className='shrink-0 flex items-center w-[90px] system-xs-semibold-uppercase text-util-colors-blue-light-blue-light-500'>
|
||||
<div className='mr-1 truncate uppercase' title={t('dataset.weightedScore.semantic') || ''}>
|
||||
{t('dataset.weightedScore.semantic')}
|
||||
</div>
|
||||
{formatNumber(value.value[0])}
|
||||
</div>
|
||||
<div className='shrink-0 flex items-center justify-end w-[90px] system-xs-semibold-uppercase text-util-colors-teal-teal-500'>
|
||||
{formatNumber(value.value[1])}
|
||||
<div className='ml-1 truncate uppercase' title={t('dataset.weightedScore.keyword') || ''}>
|
||||
{t('dataset.weightedScore.keyword')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -30,6 +30,7 @@ const ChatWrapper = () => {
|
||||
handleFeedback,
|
||||
currentChatInstanceRef,
|
||||
appData,
|
||||
themeBuilder,
|
||||
} = useChatWithHistoryContext()
|
||||
const appConfig = useMemo(() => {
|
||||
const config = appParams || {}
|
||||
@ -143,6 +144,7 @@ const ChatWrapper = () => {
|
||||
onFeedback={handleFeedback}
|
||||
suggestedQuestions={suggestedQuestions}
|
||||
hideProcessDetail
|
||||
themeBuilder={themeBuilder}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import type {
|
||||
ChatItem,
|
||||
Feedback,
|
||||
} from '../types'
|
||||
import type { ThemeBuilder } from '../embedded-chatbot/theme/theme-context'
|
||||
import type {
|
||||
AppConversationData,
|
||||
AppData,
|
||||
@ -46,6 +47,7 @@ export type ChatWithHistoryContextValue = {
|
||||
appId?: string
|
||||
handleFeedback: (messageId: string, feedback: Feedback) => void
|
||||
currentChatInstanceRef: RefObject<{ handleStop: () => void }>
|
||||
themeBuilder?: ThemeBuilder
|
||||
}
|
||||
|
||||
export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>({
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useAsyncEffect } from 'ahooks'
|
||||
import { useThemeContext } from '../embedded-chatbot/theme/theme-context'
|
||||
import {
|
||||
ChatWithHistoryContext,
|
||||
useChatWithHistoryContext,
|
||||
@ -34,6 +35,7 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
|
||||
appChatListDataLoading,
|
||||
chatShouldReloadKey,
|
||||
isMobile,
|
||||
themeBuilder,
|
||||
} = useChatWithHistoryContext()
|
||||
|
||||
const chatReady = (!showConfigPanelBeforeChat || !!appPrevChatList.length)
|
||||
@ -41,13 +43,14 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
|
||||
const site = appData?.site
|
||||
|
||||
useEffect(() => {
|
||||
themeBuilder?.buildTheme(site?.chat_color_theme, site?.chat_color_theme_inverted)
|
||||
if (site) {
|
||||
if (customConfig)
|
||||
document.title = `${site.title}`
|
||||
else
|
||||
document.title = `${site.title} - Powered by Dify`
|
||||
}
|
||||
}, [site, customConfig])
|
||||
}, [site, customConfig, themeBuilder])
|
||||
|
||||
if (appInfoLoading) {
|
||||
return (
|
||||
@ -106,6 +109,7 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
|
||||
}) => {
|
||||
const media = useBreakpoints()
|
||||
const isMobile = media === MediaType.mobile
|
||||
const themeBuilder = useThemeContext()
|
||||
|
||||
const {
|
||||
appInfoError,
|
||||
@ -171,6 +175,7 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
|
||||
appId,
|
||||
handleFeedback,
|
||||
currentChatInstanceRef,
|
||||
themeBuilder,
|
||||
}}>
|
||||
<ChatWithHistory className={className} />
|
||||
</ChatWithHistoryContext.Provider>
|
||||
|
||||
@ -51,7 +51,7 @@ const Chatbot = () => {
|
||||
else
|
||||
document.title = `${site.title} - Powered by Dify`
|
||||
}
|
||||
}, [site, customConfig])
|
||||
}, [site, customConfig, themeBuilder])
|
||||
|
||||
if (appInfoLoading) {
|
||||
return (
|
||||
|
||||
@ -12,6 +12,7 @@ type Props = {
|
||||
onChosen: () => void
|
||||
chosenConfig?: React.ReactNode
|
||||
icon?: JSX.Element
|
||||
extra?: React.ReactNode
|
||||
}
|
||||
|
||||
const RadioCard: FC<Props> = ({
|
||||
@ -20,20 +21,24 @@ const RadioCard: FC<Props> = ({
|
||||
isChosen,
|
||||
onChosen,
|
||||
icon,
|
||||
extra,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(s.item, isChosen && s.active, 'flex')}
|
||||
className={cn(s.item, isChosen && s.active)}
|
||||
onClick={onChosen}
|
||||
>
|
||||
{icon}
|
||||
<div>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div className='leading-5 text-sm font-medium text-gray-900'>{title}</div>
|
||||
<div className={s.radio}></div>
|
||||
<div className='flex px-3 py-2'>
|
||||
{icon}
|
||||
<div>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div className='leading-5 text-sm font-medium text-gray-900'>{title}</div>
|
||||
<div className={s.radio}></div>
|
||||
</div>
|
||||
<div className='leading-[18px] text-xs font-normal text-gray-500'>{description}</div>
|
||||
</div>
|
||||
<div className='leading-[18px] text-xs font-normal text-gray-500'>{description}</div>
|
||||
</div>
|
||||
{extra}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
.item {
|
||||
@apply relative p-4 rounded-xl border border-gray-100 cursor-pointer;
|
||||
@apply relative rounded-xl border border-gray-100 cursor-pointer;
|
||||
background-color: #fcfcfd;
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ import ModelSelector from '@/app/components/header/account-setting/model-provide
|
||||
import { useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import {
|
||||
DEFAULT_WEIGHTED_SCORE,
|
||||
RerankingModeEnum,
|
||||
WeightedScoreEnum,
|
||||
} from '@/models/datasets'
|
||||
@ -69,12 +70,12 @@ const RetrievalParamConfig: FC<Props> = ({
|
||||
result.weights = {
|
||||
weight_type: WeightedScoreEnum.Customized,
|
||||
vector_setting: {
|
||||
vector_weight: 0.5,
|
||||
vector_weight: DEFAULT_WEIGHTED_SCORE.other.semantic,
|
||||
embedding_provider_name: '',
|
||||
embedding_model_name: '',
|
||||
},
|
||||
keyword_setting: {
|
||||
keyword_weight: 0.5,
|
||||
keyword_weight: DEFAULT_WEIGHTED_SCORE.other.keyword,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -202,7 +203,6 @@ const RetrievalParamConfig: FC<Props> = ({
|
||||
value.reranking_mode === RerankingModeEnum.WeightedScore && (
|
||||
<WeightedScore
|
||||
value={{
|
||||
type: value.weights!.weight_type,
|
||||
value: [
|
||||
value.weights!.vector_setting.vector_weight,
|
||||
value.weights!.keyword_setting.keyword_weight,
|
||||
@ -213,7 +213,6 @@ const RetrievalParamConfig: FC<Props> = ({
|
||||
...value,
|
||||
weights: {
|
||||
...value.weights!,
|
||||
weight_type: v.type,
|
||||
vector_setting: {
|
||||
...value.weights!.vector_setting,
|
||||
vector_weight: v.value[0],
|
||||
|
||||
@ -264,20 +264,18 @@ const Form = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{currentDataset?.embedding_available && (
|
||||
<div className={rowClass}>
|
||||
<div className={labelClass} />
|
||||
<div className='w-[480px]'>
|
||||
<Button
|
||||
className='min-w-24'
|
||||
variant='primary'
|
||||
onClick={handleSave}
|
||||
>
|
||||
{t('datasetSettings.form.save')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className={rowClass}>
|
||||
<div className={labelClass} />
|
||||
<div className='w-[480px]'>
|
||||
<Button
|
||||
className='min-w-24'
|
||||
variant='primary'
|
||||
onClick={handleSave}
|
||||
>
|
||||
{t('datasetSettings.form.save')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ const ParameterItem: FC<ParameterItemProps> = ({
|
||||
const handleInputChange = (newValue: ParameterValue) => {
|
||||
setLocalValue(newValue)
|
||||
|
||||
if (onChange && (parameterRule.name === 'stop' || !isNullOrUndefined(value)))
|
||||
if (onChange && (parameterRule.name === 'stop' || !isNullOrUndefined(value) || parameterRule.required))
|
||||
onChange(newValue)
|
||||
}
|
||||
|
||||
|
||||
@ -249,6 +249,7 @@ const SystemModel: FC<SystemModelSelectorProps> = ({
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
className='ml-2'
|
||||
variant='primary'
|
||||
onClick={handleSave}
|
||||
disabled={!isCurrentWorkspaceManager}
|
||||
|
||||
@ -194,6 +194,10 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
e.preventDefault()
|
||||
if ((e.key === 'z' || e.key === 'Z') && (e.ctrlKey || e.metaKey))
|
||||
e.preventDefault()
|
||||
if ((e.key === 'y' || e.key === 'Y') && (e.ctrlKey || e.metaKey))
|
||||
e.preventDefault()
|
||||
if ((e.key === 's' || e.key === 'S') && (e.ctrlKey || e.metaKey))
|
||||
e.preventDefault()
|
||||
})
|
||||
useEventListener('mousemove', (e) => {
|
||||
const containerClientRect = workflowContainerRef.current?.getBoundingClientRect()
|
||||
@ -260,7 +264,7 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
|
||||
const { shortcutsEnabled: workflowHistoryShortcutsEnabled } = useWorkflowHistoryStore()
|
||||
|
||||
useKeyPress('delete', handleNodesDelete)
|
||||
useKeyPress(['delete', 'backspace'], handleNodesDelete)
|
||||
useKeyPress(['delete', 'backspace'], handleEdgeDelete)
|
||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.c`, (e) => {
|
||||
if (isEventTargetInputArea(e.target as HTMLElement))
|
||||
@ -310,7 +314,7 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
>
|
||||
<SyncingDataModal />
|
||||
<CandidateNode />
|
||||
<Header/>
|
||||
<Header />
|
||||
<Panel />
|
||||
<Operator handleRedo={handleHistoryForward} handleUndo={handleHistoryBack} />
|
||||
{
|
||||
|
||||
@ -2,7 +2,6 @@ import type { CommonNodeType, ModelConfig, ValueSelector } from '@/app/component
|
||||
import type { RETRIEVE_TYPE } from '@/types/app'
|
||||
import type {
|
||||
RerankingModeEnum,
|
||||
WeightedScoreEnum,
|
||||
} from '@/models/datasets'
|
||||
|
||||
export type MultipleRetrievalConfig = {
|
||||
@ -14,7 +13,6 @@ export type MultipleRetrievalConfig = {
|
||||
}
|
||||
reranking_mode?: RerankingModeEnum
|
||||
weights?: {
|
||||
weight_type: WeightedScoreEnum
|
||||
vector_setting: {
|
||||
vector_weight: number
|
||||
embedding_provider_name: string
|
||||
|
||||
@ -263,7 +263,7 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => {
|
||||
handleMultipleRetrievalConfigChange,
|
||||
handleModelChanged,
|
||||
handleCompletionParamsChange,
|
||||
selectedDatasets,
|
||||
selectedDatasets: selectedDatasets.filter(d => d.name),
|
||||
handleOnDatasetsChange,
|
||||
isShowSingleRun,
|
||||
hideSingleRun,
|
||||
|
||||
@ -7,7 +7,6 @@ import type {
|
||||
import {
|
||||
DEFAULT_WEIGHTED_SCORE,
|
||||
RerankingModeEnum,
|
||||
WeightedScoreEnum,
|
||||
} from '@/models/datasets'
|
||||
import { RETRIEVE_METHOD } from '@/types/app'
|
||||
import { DATASET_DEFAULT } from '@/config'
|
||||
@ -89,7 +88,7 @@ export const getMultipleRetrievalConfig = (multipleRetrievalConfig: MultipleRetr
|
||||
reranking_mode,
|
||||
reranking_model,
|
||||
weights,
|
||||
reranking_enable,
|
||||
reranking_enable: allEconomic ? reranking_enable : true,
|
||||
}
|
||||
|
||||
if (allEconomic || mixtureHighQualityAndEconomic || inconsistentEmbeddingModel)
|
||||
@ -100,7 +99,6 @@ export const getMultipleRetrievalConfig = (multipleRetrievalConfig: MultipleRetr
|
||||
|
||||
if (allHighQuality && !inconsistentEmbeddingModel && (reranking_mode === RerankingModeEnum.WeightedScore || reranking_mode === undefined) && !weights) {
|
||||
result.weights = {
|
||||
weight_type: WeightedScoreEnum.Customized,
|
||||
vector_setting: {
|
||||
vector_weight: allHighQualityVectorSearch
|
||||
? DEFAULT_WEIGHTED_SCORE.allHighQualityVectorSearch.semantic
|
||||
|
||||
Reference in New Issue
Block a user