Browse Source

Merge branch 'main' into feat/rag-pipeline

tags/2.0.0-beta.1
zxhlyh 5 months ago
parent
commit
c4169f8aa0

+ 9
- 2
api/controllers/inner_api/plugin/wraps.py View File

from functools import wraps from functools import wraps
from typing import Optional from typing import Optional


from flask import request
from flask import current_app, request
from flask_login import user_logged_in
from flask_restful import reqparse from flask_restful import reqparse
from pydantic import BaseModel from pydantic import BaseModel
from sqlalchemy.orm import Session from sqlalchemy.orm import Session


from extensions.ext_database import db from extensions.ext_database import db
from libs.login import _get_user
from models.account import Account, Tenant from models.account import Account, Tenant
from models.model import EndUser from models.model import EndUser
from services.account_service import AccountService from services.account_service import AccountService
raise ValueError("tenant not found") raise ValueError("tenant not found")


kwargs["tenant_model"] = tenant_model kwargs["tenant_model"] = tenant_model
kwargs["user_model"] = get_user(tenant_id, user_id)

user = get_user(tenant_id, user_id)
kwargs["user_model"] = user

current_app.login_manager._update_request_context_with_user(user) # type: ignore
user_logged_in.send(current_app._get_current_object(), user=_get_user()) # type: ignore


return view_func(*args, **kwargs) return view_func(*args, **kwargs)



+ 0
- 2
api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py View File

agent_thought: Optional[MessageAgentThought] = ( agent_thought: Optional[MessageAgentThought] = (
db.session.query(MessageAgentThought).filter(MessageAgentThought.id == event.agent_thought_id).first() db.session.query(MessageAgentThought).filter(MessageAgentThought.id == event.agent_thought_id).first()
) )
db.session.refresh(agent_thought)
db.session.close()


if agent_thought: if agent_thought:
return AgentThoughtStreamResponse( return AgentThoughtStreamResponse(

+ 4
- 2
api/core/workflow/nodes/agent/agent_node.py View File



def _remove_unsupported_model_features_for_old_version(self, model_schema: AIModelEntity) -> AIModelEntity: def _remove_unsupported_model_features_for_old_version(self, model_schema: AIModelEntity) -> AIModelEntity:
if model_schema.features: if model_schema.features:
for feature in model_schema.features:
if feature.value not in AgentOldVersionModelFeatures:
for feature in model_schema.features[:]: # Create a copy to safely modify during iteration
try:
AgentOldVersionModelFeatures(feature.value) # Try to create enum member from value
except ValueError:
model_schema.features.remove(feature) model_schema.features.remove(feature)
return model_schema return model_schema

+ 2
- 2
api/core/workflow/nodes/agent/entities.py View File

from enum import Enum
from enum import Enum, StrEnum
from typing import Any, Literal, Union from typing import Any, Literal, Union


from pydantic import BaseModel from pydantic import BaseModel
OPEN = 1 OPEN = 1




class AgentOldVersionModelFeatures(Enum):
class AgentOldVersionModelFeatures(StrEnum):
""" """
Enum class for old SDK version llm feature. Enum class for old SDK version llm feature.
""" """

+ 8
- 1
api/core/workflow/nodes/http_request/executor.py View File

files[key].append(file_tuple) files[key].append(file_tuple)


# convert files to list for httpx request # convert files to list for httpx request
# If there are no actual files, we still need to force httpx to use `multipart/form-data`.
# This is achieved by inserting a harmless placeholder file that will be ignored by the server.
if not files:
self.files = [("__multipart_placeholder__", ("", b"", "application/octet-stream"))]
if files: if files:
self.files = [] self.files = []
for key, file_tuples in files.items(): for key, file_tuples in files.items():
raw += f"{k}: {v}\r\n" raw += f"{k}: {v}\r\n"


body_string = "" body_string = ""
if self.files:
# Only log actual files if present.
# '__multipart_placeholder__' is inserted to force multipart encoding but is not a real file.
# This prevents logging meaningless placeholder entries.
if self.files and not all(f[0] == "__multipart_placeholder__" for f in self.files):
for key, (filename, content, mime_type) in self.files: for key, (filename, content, mime_type) in self.files:
body_string += f"--{boundary}\r\n" body_string += f"--{boundary}\r\n"
body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n' body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n'

+ 1
- 0
api/pyproject.toml View File

"types-tqdm~=4.67.0", "types-tqdm~=4.67.0",
"types-ujson~=5.10.0", "types-ujson~=5.10.0",
"boto3-stubs>=1.38.20", "boto3-stubs>=1.38.20",
"types-jmespath>=1.0.2.20240106",
] ]


############################################################ ############################################################

+ 3
- 1
api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py View File

assert "multipart/form-data" in executor.headers["Content-Type"] assert "multipart/form-data" in executor.headers["Content-Type"]
assert executor.params == [] assert executor.params == []
assert executor.json is None assert executor.json is None
assert executor.files is None
# '__multipart_placeholder__' is expected when no file inputs exist,
# to ensure the request is treated as multipart/form-data by the backend.
assert executor.files == [("__multipart_placeholder__", ("", b"", "application/octet-stream"))]
assert executor.content is None assert executor.content is None


# Check that the form data is correctly loaded in executor.data # Check that the form data is correctly loaded in executor.data

+ 11
- 0
api/uv.lock View File

{ name = "types-gevent" }, { name = "types-gevent" },
{ name = "types-greenlet" }, { name = "types-greenlet" },
{ name = "types-html5lib" }, { name = "types-html5lib" },
{ name = "types-jmespath" },
{ name = "types-jsonschema" }, { name = "types-jsonschema" },
{ name = "types-markdown" }, { name = "types-markdown" },
{ name = "types-oauthlib" }, { name = "types-oauthlib" },
{ name = "types-gevent", specifier = "~=24.11.0" }, { name = "types-gevent", specifier = "~=24.11.0" },
{ name = "types-greenlet", specifier = "~=3.1.0" }, { name = "types-greenlet", specifier = "~=3.1.0" },
{ name = "types-html5lib", specifier = "~=1.1.11" }, { name = "types-html5lib", specifier = "~=1.1.11" },
{ name = "types-jmespath", specifier = ">=1.0.2.20240106" },
{ name = "types-jsonschema", specifier = "~=4.23.0" }, { name = "types-jsonschema", specifier = "~=4.23.0" },
{ name = "types-markdown", specifier = "~=3.7.0" }, { name = "types-markdown", specifier = "~=3.7.0" },
{ name = "types-oauthlib", specifier = "~=3.2.0" }, { name = "types-oauthlib", specifier = "~=3.2.0" },
{ url = "https://files.pythonhosted.org/packages/ba/7c/f862b1dc31268ef10fe95b43dcdf216ba21a592fafa2d124445cd6b92e93/types_html5lib-1.1.11.20241018-py3-none-any.whl", hash = "sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403", size = 17292 }, { url = "https://files.pythonhosted.org/packages/ba/7c/f862b1dc31268ef10fe95b43dcdf216ba21a592fafa2d124445cd6b92e93/types_html5lib-1.1.11.20241018-py3-none-any.whl", hash = "sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403", size = 17292 },
] ]


[[package]]
name = "types-jmespath"
version = "1.0.2.20240106"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/e4/1f7414dbca03975f66f1ab1b3f7b3deb7c19b104ef14dd3c99036bbc39b2/types-jmespath-1.0.2.20240106.tar.gz", hash = "sha256:b4a65a116bfc1c700a4fd9d24e2e397f4a431122e0320a77b7f1989a6b5d819e", size = 5071 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f5/30/3d6443f782601dd88820ba31e7668abfec7e19d685ac7f6fbcfd6ebba519/types_jmespath-1.0.2.20240106-py3-none-any.whl", hash = "sha256:c3e715fcaae9e5f8d74e14328fdedc4f2b3f0e18df17f3e457ae0a18e245bde0", size = 6087 },
]

[[package]] [[package]]
name = "types-jsonschema" name = "types-jsonschema"
version = "4.23.0.20241208" version = "4.23.0.20241208"

+ 9
- 1
web/app/account/header.tsx View File

import Avatar from './avatar' import Avatar from './avatar'
import DifyLogo from '@/app/components/base/logo/dify-logo' import DifyLogo from '@/app/components/base/logo/dify-logo'
import { useCallback } from 'react' import { useCallback } from 'react'
import { useGlobalPublicStore } from '@/context/global-public-context'


const Header = () => { const Header = () => {
const { t } = useTranslation() const { t } = useTranslation()
const router = useRouter() const router = useRouter()
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)


const back = useCallback(() => { const back = useCallback(() => {
router.back() router.back()
<div className='flex flex-1 items-center justify-between px-4'> <div className='flex flex-1 items-center justify-between px-4'>
<div className='flex items-center gap-3'> <div className='flex items-center gap-3'>
<div className='flex cursor-pointer items-center' onClick={back}> <div className='flex cursor-pointer items-center' onClick={back}>
<DifyLogo />
{systemFeatures.branding.enabled && systemFeatures.branding.login_page_logo
? <img
src={systemFeatures.branding.login_page_logo}
className='block h-[22px] w-auto object-contain'
alt='Dify logo'
/>
: <DifyLogo />}
</div> </div>
<div className='h-4 w-[1px] origin-center rotate-[11.31deg] bg-divider-regular' /> <div className='h-4 w-[1px] origin-center rotate-[11.31deg] bg-divider-regular' />
<p className='title-3xl-semi-bold relative mt-[-2px] text-text-primary'>{t('common.account.account')}</p> <p className='title-3xl-semi-bold relative mt-[-2px] text-text-primary'>{t('common.account.account')}</p>

+ 6
- 4
web/app/components/base/chat/chat-with-history/sidebar/index.tsx View File

'flex shrink-0 items-center gap-1.5 px-1', 'flex shrink-0 items-center gap-1.5 px-1',
)}> )}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div> <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{systemFeatures.branding.enabled ? (
<img src={systemFeatures.branding.login_page_logo} alt='logo' className='block h-5 w-auto' />
) : (
<DifyLogo size='small' />)
{
systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' />
: appData?.custom_config?.replace_webapp_logo
? <img src={`${appData?.custom_config?.replace_webapp_logo}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
} }
</div> </div>
)} )}

+ 9
- 6
web/app/components/base/chat/embedded-chatbot/header/index.tsx View File

import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown' import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown'
import DifyLogo from '@/app/components/base/logo/dify-logo' import DifyLogo from '@/app/components/base/logo/dify-logo'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { useGlobalPublicStore } from '@/context/global-public-context'


export type IHeaderProps = { export type IHeaderProps = {
isMobile?: boolean isMobile?: boolean
const [parentOrigin, setParentOrigin] = useState('') const [parentOrigin, setParentOrigin] = useState('')
const [showToggleExpandButton, setShowToggleExpandButton] = useState(false) const [showToggleExpandButton, setShowToggleExpandButton] = useState(false)
const [expanded, setExpanded] = useState(false) const [expanded, setExpanded] = useState(false)
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)


const handleMessageReceived = useCallback((event: MessageEvent) => { const handleMessageReceived = useCallback((event: MessageEvent) => {
let currentParentOrigin = parentOrigin let currentParentOrigin = parentOrigin
'flex shrink-0 items-center gap-1.5 px-2', 'flex shrink-0 items-center gap-1.5 px-2',
)}> )}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div> <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{appData?.custom_config?.replace_webapp_logo && (
<img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!appData?.custom_config?.replace_webapp_logo && (
<DifyLogo size='small' />
)}
{
systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' />
: appData?.custom_config?.replace_webapp_logo
? <img src={`${appData?.custom_config?.replace_webapp_logo}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
}
</div> </div>
)} )}
</div> </div>

+ 9
- 6
web/app/components/base/chat/embedded-chatbot/index.tsx View File

import DifyLogo from '@/app/components/base/logo/dify-logo' import DifyLogo from '@/app/components/base/logo/dify-logo'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import useDocumentTitle from '@/hooks/use-document-title' import useDocumentTitle from '@/hooks/use-document-title'
import { useGlobalPublicStore } from '@/context/global-public-context'


const Chatbot = () => { const Chatbot = () => {
const { const {
themeBuilder, themeBuilder,
} = useEmbeddedChatbotContext() } = useEmbeddedChatbotContext()
const { t } = useTranslation() const { t } = useTranslation()
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)


const customConfig = appData?.custom_config const customConfig = appData?.custom_config
const site = appData?.site const site = appData?.site
'flex shrink-0 items-center gap-1.5 px-2', 'flex shrink-0 items-center gap-1.5 px-2',
)}> )}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div> <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{appData?.custom_config?.replace_webapp_logo && (
<img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!appData?.custom_config?.replace_webapp_logo && (
<DifyLogo size='small' />
)}
{
systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' />
: appData?.custom_config?.replace_webapp_logo
? <img src={`${appData?.custom_config?.replace_webapp_logo}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
}
</div> </div>
)} )}
</div> </div>

+ 3
- 10
web/app/components/base/logo/dify-logo.tsx View File

import classNames from '@/utils/classnames' import classNames from '@/utils/classnames'
import useTheme from '@/hooks/use-theme' import useTheme from '@/hooks/use-theme'
import { basePath } from '@/utils/var' import { basePath } from '@/utils/var'
import { useGlobalPublicStore } from '@/context/global-public-context'
export type LogoStyle = 'default' | 'monochromeWhite' export type LogoStyle = 'default' | 'monochromeWhite'


export const logoPathMap: Record<LogoStyle, string> = { export const logoPathMap: Record<LogoStyle, string> = {
}) => { }) => {
const { theme } = useTheme() const { theme } = useTheme()
const themedStyle = (theme === 'dark' && style === 'default') ? 'monochromeWhite' : style const themedStyle = (theme === 'dark' && style === 'default') ? 'monochromeWhite' : style
const { systemFeatures } = useGlobalPublicStore()
const hasBrandingLogo = Boolean(systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo)

let src = `${basePath}${logoPathMap[themedStyle]}`
if (hasBrandingLogo)
src = systemFeatures.branding.workspace_logo


return ( return (
<img <img
src={src}
className={classNames('block object-contain', logoSizeMap[size], hasBrandingLogo && 'w-auto', className)}
alt={hasBrandingLogo ? 'Logo' : 'Dify logo'}
src={`${basePath}${logoPathMap[themedStyle]}`}
className={classNames('block object-contain', logoSizeMap[size], className)}
alt='Dify logo'
/> />
) )
} }

+ 3
- 3
web/app/components/base/tab-slider/index.tsx View File

const newIndex = options.findIndex(option => option.value === value) const newIndex = options.findIndex(option => option.value === value)
setActiveIndex(newIndex) setActiveIndex(newIndex)
updateSliderStyle(newIndex) updateSliderStyle(newIndex)
}, [value, options, pluginList])
}, [value, options, pluginList?.total])


return ( return (
<div className={cn(className, 'relative inline-flex items-center justify-center rounded-[10px] bg-components-segmented-control-bg-normal p-0.5')}> <div className={cn(className, 'relative inline-flex items-center justify-center rounded-[10px] bg-components-segmented-control-bg-normal p-0.5')}>
{option.text} {option.text}
{/* if no plugin installed, the badge won't show */} {/* if no plugin installed, the badge won't show */}
{option.value === 'plugins' {option.value === 'plugins'
&& (pluginList?.plugins.length ?? 0) > 0
&& (pluginList?.total ?? 0) > 0
&& <Badge && <Badge
size='s' size='s'
uppercase={true} uppercase={true}
state={BadgeState.Default} state={BadgeState.Default}
> >
{pluginList?.plugins.length}
{pluginList?.total}
</Badge> </Badge>
} }
</div> </div>

+ 14
- 6
web/app/components/custom/custom-web-app-brand/index.tsx View File

} from '@/service/common' } from '@/service/common'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { useGlobalPublicStore } from '@/context/global-public-context'


const ALLOW_FILE_EXTENSIONS = ['svg', 'png'] const ALLOW_FILE_EXTENSIONS = ['svg', 'png']


const [fileId, setFileId] = useState('') const [fileId, setFileId] = useState('')
const [imgKey, setImgKey] = useState(Date.now()) const [imgKey, setImgKey] = useState(Date.now())
const [uploadProgress, setUploadProgress] = useState(0) const [uploadProgress, setUploadProgress] = useState(0)
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
const isSandbox = enableBilling && plan.type === Plan.sandbox const isSandbox = enableBilling && plan.type === Plan.sandbox
const uploading = uploadProgress > 0 && uploadProgress < 100 const uploading = uploadProgress > 0 && uploadProgress < 100
const webappLogo = currentWorkspace.custom_config?.replace_webapp_logo || '' const webappLogo = currentWorkspace.custom_config?.replace_webapp_logo || ''
{!webappBrandRemoved && ( {!webappBrandRemoved && (
<> <>
<div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div> <div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div>
{webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
{
systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' />
: webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
} }
</> </>
)} )}
{!webappBrandRemoved && ( {!webappBrandRemoved && (
<> <>
<div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div> <div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div>
{webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
{
systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' />
: webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
} }
</> </>
)} )}

+ 10
- 1
web/app/components/header/account-about/index.tsx View File

import { IS_CE_EDITION } from '@/config' import { IS_CE_EDITION } from '@/config'
import DifyLogo from '@/app/components/base/logo/dify-logo' import DifyLogo from '@/app/components/base/logo/dify-logo'
import { noop } from 'lodash-es' import { noop } from 'lodash-es'
import { useGlobalPublicStore } from '@/context/global-public-context'


type IAccountSettingProps = { type IAccountSettingProps = {
langeniusVersionInfo: LangGeniusVersionResponse langeniusVersionInfo: LangGeniusVersionResponse
}: IAccountSettingProps) { }: IAccountSettingProps) {
const { t } = useTranslation() const { t } = useTranslation()
const isLatest = langeniusVersionInfo.current_version === langeniusVersionInfo.latest_version const isLatest = langeniusVersionInfo.current_version === langeniusVersionInfo.latest_version
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)


return ( return (
<Modal <Modal
<RiCloseLine className='h-4 w-4 text-text-tertiary' /> <RiCloseLine className='h-4 w-4 text-text-tertiary' />
</div> </div>
<div className='flex flex-col items-center gap-4 py-8'> <div className='flex flex-col items-center gap-4 py-8'>
<DifyLogo size='large' className='mx-auto' />
{systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img
src={systemFeatures.branding.workspace_logo}
className='block h-7 w-auto object-contain'
alt='logo'
/>
: <DifyLogo size='large' className='mx-auto' />}

<div className='text-center text-xs font-normal text-text-tertiary'>Version {langeniusVersionInfo?.current_version}</div> <div className='text-center text-xs font-normal text-text-tertiary'>Version {langeniusVersionInfo?.current_version}</div>
<div className='flex flex-col items-center gap-2 text-center text-xs font-normal text-text-secondary'> <div className='flex flex-col items-center gap-2 text-center text-xs font-normal text-text-secondary'>
<div>© {dayjs().year()} LangGenius, Inc., Contributors.</div> <div>© {dayjs().year()} LangGenius, Inc., Contributors.</div>

+ 16
- 2
web/app/components/header/index.tsx View File

import PlanBadge from './plan-badge' import PlanBadge from './plan-badge'
import LicenseNav from './license-env' import LicenseNav from './license-env'
import { Plan } from '../billing/type' import { Plan } from '../billing/type'
import { useGlobalPublicStore } from '@/context/global-public-context'


const navClassName = ` const navClassName = `
flex items-center relative mr-0 sm:mr-3 px-3 h-8 rounded-xl flex items-center relative mr-0 sm:mr-3 px-3 h-8 rounded-xl
const [isShowNavMenu, { toggle, setFalse: hideNavMenu }] = useBoolean(false) const [isShowNavMenu, { toggle, setFalse: hideNavMenu }] = useBoolean(false)
const { enableBilling, plan } = useProviderContext() const { enableBilling, plan } = useProviderContext()
const { setShowPricingModal, setShowAccountSettingModal } = useModalContext() const { setShowPricingModal, setShowAccountSettingModal } = useModalContext()
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
const isFreePlan = plan.type === Plan.sandbox const isFreePlan = plan.type === Plan.sandbox
const handlePlanClick = useCallback(() => { const handlePlanClick = useCallback(() => {
if (isFreePlan) if (isFreePlan)
!isMobile !isMobile
&& <div className='flex shrink-0 items-center gap-1.5 self-stretch pl-3'> && <div className='flex shrink-0 items-center gap-1.5 self-stretch pl-3'>
<Link href="/apps" className='flex h-8 shrink-0 items-center justify-center gap-2 px-0.5'> <Link href="/apps" className='flex h-8 shrink-0 items-center justify-center gap-2 px-0.5'>
<DifyLogo />
{systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img
src={systemFeatures.branding.workspace_logo}
className='block h-[22px] w-auto object-contain'
alt='logo'
/>
: <DifyLogo />}
</Link> </Link>
<div className='font-light text-divider-deep'>/</div> <div className='font-light text-divider-deep'>/</div>
<div className='flex items-center gap-0.5'> <div className='flex items-center gap-0.5'>
{isMobile && ( {isMobile && (
<div className='flex'> <div className='flex'>
<Link href="/apps" className='mr-4 flex items-center'> <Link href="/apps" className='mr-4 flex items-center'>
<DifyLogo />
{systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img
src={systemFeatures.branding.workspace_logo}
className='block h-[22px] w-auto object-contain'
alt='logo'
/>
: <DifyLogo />}
</Link> </Link>
<div className='font-light text-divider-deep'>/</div> <div className='font-light text-divider-deep'>/</div>
{enableBilling ? <PlanBadge allowHover sandboxAsUpgrade plan={plan.type} onClick={handlePlanClick} /> : <LicenseNav />} {enableBilling ? <PlanBadge allowHover sandboxAsUpgrade plan={plan.type} onClick={handlePlanClick} /> : <LicenseNav />}

+ 2
- 2
web/app/components/plugins/plugin-page/plugins-panel.tsx View File

import type { FilterState } from './filter-management' import type { FilterState } from './filter-management'
import FilterManagement from './filter-management' import FilterManagement from './filter-management'
import List from './list' import List from './list'
import { useInstalledLatestVersion, useInstalledPluginListWithPagination, useInvalidateInstalledPluginList } from '@/service/use-plugins'
import { useInstalledLatestVersion, useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins'
import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel'
import { usePluginPageContext } from './context' import { usePluginPageContext } from './context'
import { useDebounceFn } from 'ahooks' import { useDebounceFn } from 'ahooks'
const { t } = useTranslation() const { t } = useTranslation()
const filters = usePluginPageContext(v => v.filters) as FilterState const filters = usePluginPageContext(v => v.filters) as FilterState
const setFilters = usePluginPageContext(v => v.setFilters) const setFilters = usePluginPageContext(v => v.setFilters)
const { data: pluginList, isLoading: isPluginListLoading, isFetching, isLastPage, loadNextPage } = useInstalledPluginListWithPagination()
const { data: pluginList, isLoading: isPluginListLoading, isFetching, isLastPage, loadNextPage } = useInstalledPluginList()
const { data: installedLatestVersion } = useInstalledLatestVersion( const { data: installedLatestVersion } = useInstalledLatestVersion(
pluginList?.plugins pluginList?.plugins
.filter(plugin => plugin.source === PluginSource.marketplace) .filter(plugin => plugin.source === PluginSource.marketplace)

+ 7
- 5
web/app/components/share/text-generation/index.tsx View File

!isPC && resultExisted && 'rounded-b-2xl border-b-[0.5px] border-divider-regular', !isPC && resultExisted && 'rounded-b-2xl border-b-[0.5px] border-divider-regular',
)}> )}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div> <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{systemFeatures.branding.enabled ? (
<img src={systemFeatures.branding.login_page_logo} alt='logo' className='block h-5 w-auto' />
) : (
<DifyLogo size='small' />
)}
{
systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? <img src={systemFeatures.branding.workspace_logo} alt='logo' className='block h-5 w-auto' />
: customConfig?.replace_webapp_logo
? <img src={`${customConfig?.replace_webapp_logo}`} alt='logo' className='block h-5 w-auto' />
: <DifyLogo size='small' />
}
</div> </div>
)} )}
</div> </div>

+ 1
- 1
web/app/reset-password/set-password/page.tsx View File

</div> </div>


<div className="mx-auto mt-6 w-full"> <div className="mx-auto mt-6 w-full">
<div className="bg-white">
<div>
{/* Password */} {/* Password */}
<div className='mb-5'> <div className='mb-5'>
<label htmlFor="password" className="system-md-semibold my-2 text-text-secondary"> <label htmlFor="password" className="system-md-semibold my-2 text-text-secondary">

+ 9
- 1
web/app/signin/_header.tsx View File

import type { Locale } from '@/i18n' import type { Locale } from '@/i18n'
import I18n from '@/context/i18n' import I18n from '@/context/i18n'
import dynamic from 'next/dynamic' import dynamic from 'next/dynamic'
import { useGlobalPublicStore } from '@/context/global-public-context'


// Avoid rendering the logo and theme selector on the server // Avoid rendering the logo and theme selector on the server
const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), { const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), {


const Header = () => { const Header = () => {
const { locale, setLocaleOnClient } = useContext(I18n) const { locale, setLocaleOnClient } = useContext(I18n)
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)


return ( return (
<div className='flex w-full items-center justify-between p-6'> <div className='flex w-full items-center justify-between p-6'>
<DifyLogo size='large' />
{systemFeatures.branding.enabled && systemFeatures.branding.login_page_logo
? <img
src={systemFeatures.branding.login_page_logo}
className='block h-7 w-auto object-contain'
alt='logo'
/>
: <DifyLogo size='large' />}
<div className='flex items-center gap-1'> <div className='flex items-center gap-1'>
<Select <Select
value={locale} value={locale}

+ 3
- 3
web/i18n/ko-KR/common.ts View File

perPage: '페이지당 항목 수', perPage: '페이지당 항목 수',
}, },
theme: { theme: {
theme: '주제',
light: '',
dark: '어',
theme: '테마',
light: '밝은',
dark: '어두운',
auto: '시스템', auto: '시스템',
}, },
compliance: { compliance: {

+ 1
- 1
web/i18n/ko-KR/dataset-documents.ts View File

designDocument: '디자인 문서', designDocument: '디자인 문서',
productSpecification: '제품 사양서', productSpecification: '제품 사양서',
financialReport: '재무 보고서', financialReport: '재무 보고서',
marketAnalysis: '시장 분석',
marketAnalysis: '마켓 분석',
projectPlan: '프로젝트 계획서', projectPlan: '프로젝트 계획서',
teamStructure: '팀 구조', teamStructure: '팀 구조',
policiesProcedures: '정책 및 절차', policiesProcedures: '정책 및 절차',

+ 1
- 1
web/i18n/ko-KR/dataset.ts View File

datasetMetadata: { datasetMetadata: {
name: '이름', name: '이름',
deleteTitle: '삭제 확인', deleteTitle: '삭제 확인',
disabled: '장애인',
disabled: '사용안함',
addMetaData: '메타데이터 추가', addMetaData: '메타데이터 추가',
values: '{{num}} 값들', values: '{{num}} 값들',
namePlaceholder: '메타데이터 이름', namePlaceholder: '메타데이터 이름',

+ 2
- 2
web/i18n/ko-KR/plugin.ts View File

}, },
source: { source: {
local: '로컬 패키지 파일', local: '로컬 패키지 파일',
marketplace: '시장',
marketplace: '마켓',
github: '깃허브', github: '깃허브',
}, },
detailPanel: { detailPanel: {
endpointsEnabled: '{{num}}개의 엔드포인트 집합이 활성화되었습니다.', endpointsEnabled: '{{num}}개의 엔드포인트 집합이 활성화되었습니다.',
installFrom: '에서 설치', installFrom: '에서 설치',
allCategories: '모든 카테고리', allCategories: '모든 카테고리',
submitPlugin: '제출 플러그인',
submitPlugin: '플러그인 제출',
findMoreInMarketplace: 'Marketplace에서 더 알아보기', findMoreInMarketplace: 'Marketplace에서 더 알아보기',
searchCategories: '검색 카테고리', searchCategories: '검색 카테고리',
search: '검색', search: '검색',

+ 2
- 2
web/i18n/ko-KR/workflow.ts View File

code: '코드', code: '코드',
model: '모델', model: '모델',
rerankModel: '재정렬 모델', rerankModel: '재정렬 모델',
visionVariable: '시력 변수',
visionVariable: '비전 변수',
}, },
invalidVariable: '잘못된 변수', invalidVariable: '잘못된 변수',
rerankModelRequired: 'Rerank Model을 켜기 전에 설정에서 모델이 성공적으로 구성되었는지 확인하십시오.', rerankModelRequired: 'Rerank Model을 켜기 전에 설정에서 모델이 성공적으로 구성되었는지 확인하십시오.',
metadata: { metadata: {
options: { options: {
disabled: { disabled: {
title: '장애인',
title: '사용안함',
subTitle: '메타데이터 필터링을 활성화하지 않음', subTitle: '메타데이터 필터링을 활성화하지 않음',
}, },
automatic: { automatic: {

+ 8
- 13
web/service/use-plugins.ts View File

GitHubItemAndMarketPlaceDependency, GitHubItemAndMarketPlaceDependency,
InstallPackageResponse, InstallPackageResponse,
InstalledLatestVersionResponse, InstalledLatestVersionResponse,
InstalledPluginListResponse,
InstalledPluginListWithTotalResponse, InstalledPluginListWithTotalResponse,
PackageDependency, PackageDependency,
Permissions, Permissions,
}) })
} }


export const useInstalledPluginList = (disable?: boolean) => {
return useQuery<InstalledPluginListResponse>({
queryKey: useInstalledPluginListKey,
queryFn: () => get<InstalledPluginListResponse>('/workspaces/current/plugin/list'),
enabled: !disable,
initialData: !disable ? undefined : { plugins: [] },
})
}

export const useInstalledPluginListWithPagination = (pageSize = 100) => {
export const useInstalledPluginList = (disable?: boolean, pageSize = 100) => {
const fetchPlugins = async ({ pageParam = 1 }) => { const fetchPlugins = async ({ pageParam = 1 }) => {
const response = await get<InstalledPluginListWithTotalResponse>( const response = await get<InstalledPluginListWithTotalResponse>(
`/workspaces/current/plugin/list?page=${pageParam}&page_size=${pageSize}`, `/workspaces/current/plugin/list?page=${pageParam}&page_size=${pageSize}`,
hasNextPage, hasNextPage,
isFetchingNextPage, isFetchingNextPage,
isLoading, isLoading,
isSuccess,
} = useInfiniteQuery({ } = useInfiniteQuery({
queryKey: ['installed-plugins', pageSize],
enabled: !disable,
queryKey: useInstalledPluginListKey,
queryFn: fetchPlugins, queryFn: fetchPlugins,
getNextPageParam: (lastPage, pages) => { getNextPageParam: (lastPage, pages) => {
const totalItems = lastPage.total const totalItems = lastPage.total
}) })


const plugins = data?.pages.flatMap(page => page.plugins) ?? [] const plugins = data?.pages.flatMap(page => page.plugins) ?? []
const total = data?.pages[0].total ?? 0


return { return {
data: {
data: disable ? undefined : {
plugins, plugins,
total,
}, },
isLastPage: !hasNextPage, isLastPage: !hasNextPage,
loadNextPage: () => { loadNextPage: () => {
isLoading, isLoading,
isFetching: isFetchingNextPage, isFetching: isFetchingNextPage,
error, error,
isSuccess,
} }
} }



Loading…
Cancel
Save